Project import
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..a896cb6
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,65 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+version: 0.1.{build}
+image: Visual Studio 2015
+configuration:
+- Release
+platform:
+- x64
+- x86
+- arm
+clone_depth: 10
+install:
+    - cmd: Bcdedit.exe -set TESTSIGNING ON
+    # Configure logging
+    - cmd: reg import tools\windows\ThreadEtw.reg
+    - ps: Restart-Computer -Force
+    - ps: Start-Sleep -s 10
+before_build:
+    - cmd: ren "C:\Program Files (x86)\Windows Kits\10\include\00wdf" "wdf"
+build:
+  project: etc/visual-studio/openthread.sln
+  verbosity: minimal
+after_build:
+    - ps: $env:Platform2 = $env:Platform
+    - ps: If ($env:Platform2 -eq "x86") { $env:Platform2 = "Win32" }
+    # Set up the release directories
+    - cmd: .appveyor\make_release.cmd
+    # Install driver (only runs on x64)
+    - cmd: .appveyor\install_driver.cmd
+test_script:
+    # Run the unit tests
+    - cmd: .appveyor\run_unit_tests.cmd
+    # Run the tests for the driver (only runs on x64)
+    #- cmd: .appveyor\test_driver.cmd
+artifacts:
+- path: release
+  name: release
+- path: build\bin\AppPackages
+  name: app
diff --git a/.appveyor/install_driver.cmd b/.appveyor/install_driver.cmd
new file mode 100644
index 0000000..73e0742
--- /dev/null
+++ b/.appveyor/install_driver.cmd
@@ -0,0 +1,43 @@
+REM
+REM  Copyright (c) 2016, The OpenThread Authors.
+REM  All rights reserved.
+REM
+REM  Redistribution and use in source and binary forms, with or without
+REM  modification, are permitted provided that the following conditions are met:
+REM  1. Redistributions of source code must retain the above copyright
+REM     notice, this list of conditions and the following disclaimer.
+REM  2. Redistributions in binary form must reproduce the above copyright
+REM     notice, this list of conditions and the following disclaimer in the
+REM     documentation and/or other materials provided with the distribution.
+REM  3. Neither the name of the copyright holder nor the
+REM     names of its contributors may be used to endorse or promote products
+REM     derived from this software without specific prior written permission.
+REM
+REM  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+REM  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+REM  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+REM  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+REM  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+REM  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+REM  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+REM  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+REM  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+REM  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+REM  POSSIBILITY OF SUCH DAMAGE.
+REM
+
+IF NOT "%Platform%"=="x64" GOTO :EOF
+
+pushd %APPVEYOR_BUILD_FOLDER%\build\bin\%Platform2%\%Configuration%\sys
+
+REM Install the certifications to the cert stores
+certutil -addstore root otLwf.cer
+certutil -addstore TrustedPublisher otLwf.cer
+
+cd otLwf
+
+REM Install the NDIS LWF driver, otLwf.sys
+
+netcfg.exe -v -l otlwf.inf -c s -i otLwf
+
+popd
\ No newline at end of file
diff --git a/.appveyor/make_release.cmd b/.appveyor/make_release.cmd
new file mode 100644
index 0000000..bc69f2c
--- /dev/null
+++ b/.appveyor/make_release.cmd
@@ -0,0 +1,87 @@
+REM
+REM  Copyright (c) 2016, The OpenThread Authors.
+REM  All rights reserved.
+REM
+REM  Redistribution and use in source and binary forms, with or without
+REM  modification, are permitted provided that the following conditions are met:
+REM  1. Redistributions of source code must retain the above copyright
+REM     notice, this list of conditions and the following disclaimer.
+REM  2. Redistributions in binary form must reproduce the above copyright
+REM     notice, this list of conditions and the following disclaimer in the
+REM     documentation and/or other materials provided with the distribution.
+REM  3. Neither the name of the copyright holder nor the
+REM     names of its contributors may be used to endorse or promote products
+REM     derived from this software without specific prior written permission.
+REM
+REM  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+REM  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+REM  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+REM  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+REM  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+REM  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+REM  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+REM  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+REM  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+REM  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+REM  POSSIBILITY OF SUCH DAMAGE.
+REM
+
+pushd %APPVEYOR_BUILD_FOLDER%
+
+REM Make the release directories
+mkdir release
+mkdir release\include
+mkdir release\include\openthread\
+mkdir release\include\openthread\missing
+mkdir release\include\openthread\platform
+mkdir release\libs
+mkdir release\symbols
+mkdir release\symbols\TraceFormat
+
+REM Copy the relavant include headers
+
+copy include\openthread\commissioner.h release\include\openthread
+copy include\openthread\dataset.h release\include\openthread
+copy include\openthread\instance.h release\include\openthread
+copy include\openthread\ip6.h release\include\openthread
+copy include\openthread\joiner.h release\include\openthread
+copy include\openthread\link.h release\include\openthread
+copy include\openthread\message.h release\include\openthread
+copy include\openthread\netdata.h release\include\openthread
+copy include\openthread\openthread.h release\include\openthread
+copy include\openthread\thread.h release\include\openthread
+copy include\openthread\types.h release\include\openthread
+copy include\openthread\platform\toolchain.h release\include\openthread\platform
+copy src\missing\stdbool\stdbool.h release\include\openthread\missing
+copy src\missing\stdint\stdint.h release\include\openthread\missing
+
+REM Copy the relavant binaries
+
+copy build\bin\%Platform2%\%Configuration%\sys\otlwf\* release
+copy build\bin\%Platform2%\%Configuration%\sys\otlwf.cer release
+copy build\bin\%Platform2%\%Configuration%\sys\otlwf.pdb release\symbols
+copy build\bin\%Platform2%\%Configuration%\sys\ottmp\* release
+copy build\bin\%Platform2%\%Configuration%\sys\ottmp.cer release
+copy build\bin\%Platform2%\%Configuration%\sys\ottmp.pdb release\symbols
+copy build\bin\%Platform2%\%Configuration%\dll\otApi.dll release
+copy build\bin\%Platform2%\%Configuration%\dll\otApi.lib release\libs
+copy build\bin\%Platform2%\%Configuration%\dll\otApi.pdb release\symbols
+copy build\bin\%Platform2%\%Configuration%\dll\otNodeApi.dll release
+copy build\bin\%Platform2%\%Configuration%\dll\otNodeApi.lib release\libs
+copy build\bin\%Platform2%\%Configuration%\dll\otNodeApi.pdb release\symbols
+copy build\bin\%Platform2%\%Configuration%\exe\otCli.exe release
+copy build\bin\%Platform2%\%Configuration%\exe\otCli.pdb release\symbols
+copy build\bin\%Platform2%\%Configuration%\exe\otTestRunner.exe release
+copy build\bin\%Platform2%\%Configuration%\exe\otTestRunner.pdb release\symbols
+
+REM Copy the tools
+
+copy tools\windows\* release
+copy tools\windows\%Platform%\otInstall.exe release
+copy "C:\Program Files (x86)\Windows Kits\10\Tools\%Platform%\devcon.exe" release
+
+REM Generate the trace format files to decode the WPP logs
+
+"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\x64\TracePdb.exe" -f release\symbols\*.pdb -p release\symbols\TraceFormat
+
+popd
\ No newline at end of file
diff --git a/.appveyor/run_unit_tests.cmd b/.appveyor/run_unit_tests.cmd
new file mode 100644
index 0000000..c4f496c
--- /dev/null
+++ b/.appveyor/run_unit_tests.cmd
@@ -0,0 +1,31 @@
+REM
+REM  Copyright (c) 2016, The OpenThread Authors.
+REM  All rights reserved.
+REM
+REM  Redistribution and use in source and binary forms, with or without
+REM  modification, are permitted provided that the following conditions are met:
+REM  1. Redistributions of source code must retain the above copyright
+REM     notice, this list of conditions and the following disclaimer.
+REM  2. Redistributions in binary form must reproduce the above copyright
+REM     notice, this list of conditions and the following disclaimer in the
+REM     documentation and/or other materials provided with the distribution.
+REM  3. Neither the name of the copyright holder nor the
+REM     names of its contributors may be used to endorse or promote products
+REM     derived from this software without specific prior written permission.
+REM
+REM  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+REM  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+REM  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+REM  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+REM  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+REM  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+REM  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+REM  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+REM  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+REM  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+REM  POSSIBILITY OF SUCH DAMAGE.
+REM
+
+IF "%Platform%"=="arm" GOTO :EOF
+
+vstest.console /logger:Appveyor /inIsolation /platform:%Platform% build\bin\%Platform2%\%Configuration%\dll\UnitTests.dll
\ No newline at end of file
diff --git a/.appveyor/test_driver.cmd b/.appveyor/test_driver.cmd
new file mode 100644
index 0000000..7425419
--- /dev/null
+++ b/.appveyor/test_driver.cmd
@@ -0,0 +1,47 @@
+REM
+REM  Copyright (c) 2016, The OpenThread Authors.
+REM  All rights reserved.
+REM
+REM  Redistribution and use in source and binary forms, with or without
+REM  modification, are permitted provided that the following conditions are met:
+REM  1. Redistributions of source code must retain the above copyright
+REM     notice, this list of conditions and the following disclaimer.
+REM  2. Redistributions in binary form must reproduce the above copyright
+REM     notice, this list of conditions and the following disclaimer in the
+REM     documentation and/or other materials provided with the distribution.
+REM  3. Neither the name of the copyright holder nor the
+REM     names of its contributors may be used to endorse or promote products
+REM     derived from this software without specific prior written permission.
+REM
+REM  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+REM  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+REM  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+REM  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+REM  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+REM  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+REM  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+REM  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+REM  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+REM  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+REM  POSSIBILITY OF SUCH DAMAGE.
+REM
+
+IF NOT "%Platform%"=="x64" GOTO :EOF
+
+pushd %APPVEYOR_BUILD_FOLDER%\release
+
+REM Query the driver state
+
+sc query otlwf
+
+REM Run the basic driver test
+
+otTestRunner.exe ..\tests\scripts\thread-cert Test_otLwf* appveyor
+
+REM Grab the logs
+
+mkdir logs
+logman stop Thread -ets
+copy %SystemRoot%\System32\LogFiles\WMI\Thread.* logs
+
+popd
\ No newline at end of file
diff --git a/.astyle/astyle-opts b/.astyle/astyle-opts
new file mode 100644
index 0000000..3bebecd
--- /dev/null
+++ b/.astyle/astyle-opts
@@ -0,0 +1,16 @@
+--style=allman
+--max-code-length=120
+--max-instatement-indent=100
+--attach-namespaces --attach-inlines
+--attach-extern-c
+--min-conditional-indent=0
+--break-blocks
+--pad-oper
+--pad-header
+--unpad-paren
+--align-pointer=name
+--add-brackets
+--keep-one-line-blocks
+--convert-tabs
+--break-after-logical
+--formatted
diff --git a/.astyle/astyle-wrap.sh b/.astyle/astyle-wrap.sh
new file mode 100755
index 0000000..d530450
--- /dev/null
+++ b/.astyle/astyle-wrap.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# astye does not return a non-zero exit code.  This wrapper exists with a
+# non-zero exit code if there is any output from astyle.
+#
+
+die() {
+	echo " *** ERROR: " $*
+	exit 1
+}
+
+set -x
+
+[ -z "`$@`" ] || {
+    [ "$3" != "--dry-run" ] || die
+}
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..6e21a7f
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,8 @@
+coverage:
+  ignore:
+    - "tests/*"
+    - "third_party/*"
+
+  status:
+    project: false
+    patch: false
diff --git a/.default-version b/.default-version
new file mode 100644
index 0000000..7626585
--- /dev/null
+++ b/.default-version
@@ -0,0 +1 @@
+0.01.00
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..eaad68c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,45 @@
+# Auto normalize all files which appear to be text files.
+*        text=auto
+
+# Auto normalize files which are explicitly text
+*.[ch]   text
+*.[ch]pp text
+*.txt    text
+*.md     text
+*.yml    text
+*.html   text
+*.py     text
+
+# Files which are explicitly binary
+*.gz     binary !eol
+*.xz     binary !eol
+*.bz2    binary !eol
+*.tar    binary !eol
+*.png    binary !eol
+*.jpg    binary !eol
+*.gif    binary !eol
+
+# Files normalized to always keep Unix line endings
+.default-version  eol=lf
+bootstrap*        eol=lf
+config.guess      eol=lf
+config.status     eol=lf
+configure         eol=lf
+libtool           eol=lf
+libtoolize        eol=lf
+autoreconf        eol=lf
+mkversion         eol=lf
+*.sh              eol=lf
+*-sh              eol=lf
+*.m4              eol=lf
+configure.ac      eol=lf
+Makefile.am       eol=lf
+Makefile.in       eol=lf
+Makefile          eol=lf
+
+# Files normalized to always keep Windows line endings
+*.vcxproj         eol=crlf
+*.vcxproj.filters eol=crlf
+*.sln             eol=crlf
+*.rc     text     eol=crlf
+*.inf    text     eol=crlf
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..30c0e64
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,7 @@
+GitHub Issues are for bugs and feature requests.  To make bugs and feature requests more easy to find and organize, we close issues that are deemed out of scope for GitHub Issues.
+
+Usage questions? Post questions to [Stack Overflow](http://stackoverflow.com/) using the [[openthread] tag](http://stackoverflow.com/questions/tagged/openthread). We also use Google Groups for discussion and announcements:
+
+* [openthread-announce](https://groups.google.com/forum/#!forum/openthread-announce) - subscribe for release notes and new updates on OpenThread
+* [openthread-users](https://groups.google.com/forum/#!forum/openthread-users) - the best place for users to discuss OpenThread and interact with the OpenThread team
+* [openthread-devel](https://groups.google.com/forum/#!forum/openthread-devel) - team members discuss the on-going development of OpenThread
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c23efd7
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,96 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+language: generic
+
+sudo: required
+dist: trusty
+
+before_install:
+  - .travis/before_install.sh
+
+script:
+  - .travis/script.sh
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
+
+matrix:
+  include:
+    - env: BUILD_TARGET="pretty-check"
+      os: linux
+    - env: BUILD_TARGET="scan-build" CC="clang" CXX="clang++"
+      os: linux
+      compiler: clang
+    - env: BUILD_TARGET="arm-gcc49"
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="arm-gcc54"
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="arm-gcc63"
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="posix" CC="clang" CXX="clang++"
+      os: linux
+      compiler: clang
+    - env: BUILD_TARGET="posix" CC="gcc" CXX="g++"
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="posix" CC="gcc-5" CXX="g++-5"
+      os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-5
+            - g++-5
+    - env: BUILD_TARGET="posix" CC="gcc-6" CXX="g++-6"
+      os: linux
+      compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-6
+            - g++-6
+    - env: BUILD_TARGET="posix-distcheck" VERBOSE=1
+      os: linux
+      compiler: clang
+    - env: BUILD_TARGET="posix-32-bit" VERBOSE=1
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="posix-ncp-spi" VERBOSE=1
+      os: linux
+      compiler: gcc
+    - env: BUILD_TARGET="posix-ncp" VERBOSE=1
+      os: linux
+      compiler: gcc
diff --git a/.travis/before_install.sh b/.travis/before_install.sh
new file mode 100755
index 0000000..087e508
--- /dev/null
+++ b/.travis/before_install.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+die() {
+	echo " *** ERROR: " $*
+	exit 1
+}
+
+set -x
+
+cd /tmp || die
+
+[ $TRAVIS_OS_NAME != linux ] || {
+    sudo apt-get update || die
+
+    [ $BUILD_TARGET != posix-distcheck -a $BUILD_TARGET != posix-32-bit -a $BUILD_TARGET != posix-ncp ] || {
+        pip install --upgrade pip || die
+        pip install --user -r $TRAVIS_BUILD_DIR/tests/scripts/thread-cert/requirements.txt || die
+        [ $BUILD_TARGET != posix-ncp ] || {
+            # Packages used by ncp tools.
+            pip install --user git+https://github.com/openthread/pyspinel || die
+        }
+    }
+
+    [ $BUILD_TARGET != pretty-check ] || {
+        wget http://jaist.dl.sourceforge.net/project/astyle/astyle/astyle%202.05.1/astyle_2.05.1_linux.tar.gz || die
+        tar xzvf astyle_2.05.1_linux.tar.gz || die
+        cd astyle/build/gcc || die
+        LDFLAGS=" " make || die
+        cd ../../..
+        export PATH=/tmp/astyle/build/gcc/bin:$PATH || die
+        astyle --version || die
+    }
+
+    [ $BUILD_TARGET != scan-build ] || {
+        sudo apt-get install clang || die
+    }
+
+    [ $BUILD_TARGET != arm-gcc49 ] || {
+        sudo apt-get install lib32z1 || die
+        wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 || die
+        tar xjf gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 || die
+        export PATH=/tmp/gcc-arm-none-eabi-4_9-2015q3/bin:$PATH || die
+        arm-none-eabi-gcc --version || die
+    }
+
+    [ $BUILD_TARGET != arm-gcc54 ] || {
+        sudo apt-get install lib32z1 || die
+        wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2 || die
+        tar xjf gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2 || die
+        export PATH=/tmp/gcc-arm-none-eabi-5_4-2016q3/bin:$PATH || die
+        arm-none-eabi-gcc --version || die
+    }
+
+    [ $BUILD_TARGET != arm-gcc63 ] || {
+        wget https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases/download/arc-2017.03-rc2/arc_gnu_2017.03-rc2_prebuilt_elf32_le_linux_install.tar.gz || die
+        tar xzf arc_gnu_2017.03-rc2_prebuilt_elf32_le_linux_install.tar.gz
+        export PATH=/tmp/arc_gnu_2017.03-rc2_prebuilt_elf32_le_linux_install/bin:$PATH || die
+        arc-elf32-gcc --version || die
+    }
+
+    [ $BUILD_TARGET != posix-32-bit ] || {
+        sudo apt-get install g++-multilib || die
+    }
+
+    [ $BUILD_TARGET != posix-distcheck ] || {
+        sudo apt-get install clang || die
+        sudo apt-get install llvm-3.4-runtime || die
+    }
+
+    [ $BUILD_TARGET != posix -o $CC != clang ] || {
+        sudo apt-get install clang || die
+    }
+}
+
+[ $TRAVIS_OS_NAME != osx ] || {
+    sudo easy_install pexpect || die
+
+    [ $BUILD_TARGET != cc2538 ] || {
+        wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q3-update/+download/gcc-arm-none-eabi-4_9-2015q3-20150921-mac.tar.bz2 || die
+        tar xjf gcc-arm-none-eabi-4_9-2015q3-20150921-mac.tar.bz2 || die
+        export PATH=/tmp/gcc-arm-none-eabi-4_9-2015q3/bin:$PATH || die
+        arm-none-eabi-gcc --version || die
+    }
+}
diff --git a/.travis/script.sh b/.travis/script.sh
new file mode 100755
index 0000000..00cd393
--- /dev/null
+++ b/.travis/script.sh
@@ -0,0 +1,204 @@
+#!/bin/sh
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+die() {
+	echo " *** ERROR: " $*
+	exit 1
+}
+
+set -x
+
+[ $BUILD_TARGET != pretty-check ] || {
+    export PATH=/tmp/astyle/build/gcc/bin:$PATH || die
+    ./bootstrap || die
+    ./configure || die
+    make pretty-check || die
+}
+
+[ $BUILD_TARGET != scan-build ] || {
+    ./bootstrap || die
+    scan-build ./configure                \
+        --enable-cli-app=all              \
+        --enable-ncp-app=all              \
+        --with-ncp-bus=uart               \
+        --enable-diag                     \
+        --enable-default-logging          \
+        --enable-raw-link-api=yes         \
+        --with-examples=posix             \
+        --with-platform-info=POSIX        \
+        --enable-application-coap         \
+        --enable-border-agent-proxy       \
+        --enable-cert-log                 \
+        --enable-commissioner             \
+        --enable-dhcp6-client             \
+        --enable-dhcp6-server             \
+        --enable-dns-client               \
+        --enable-jam-detection            \
+        --enable-joiner                   \
+        --enable-legacy                   \
+        --enable-mac-whitelist            \
+        --enable-mtd-network-diagnostic || die
+    scan-build --status-bugs -analyze-headers -v make || die
+}
+
+[ $BUILD_TARGET != arm-gcc49 ] || {
+    export PATH=/tmp/gcc-arm-none-eabi-4_9-2015q3/bin:$PATH || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-cc2538 || die
+    arm-none-eabi-size  output/cc2538/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-da15000 || die
+    arm-none-eabi-size  output/da15000/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/da15000/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/da15000/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/da15000/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-kw41z || die
+    arm-none-eabi-size  output/kw41z/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-nrf52840 || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    make -f examples/Makefile-cc2650 || die
+    arm-none-eabi-size  output/cc2650/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/cc2650/bin/ot-ncp-mtd || die
+}
+
+[ $BUILD_TARGET != arm-gcc54 ] || {
+    export PATH=/tmp/gcc-arm-none-eabi-5_4-2016q3/bin:$PATH || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-cc2538 || die
+    arm-none-eabi-size  output/cc2538/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/cc2538/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-da15000 || die
+    arm-none-eabi-size  output/da15000/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/da15000/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/da15000/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/da15000/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-kw41z || die
+    arm-none-eabi-size  output/kw41z/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/kw41z/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-nrf52840 || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-cli-ftd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-ncp-ftd || die
+    arm-none-eabi-size  output/nrf52840/bin/ot-ncp-mtd || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    make -f examples/Makefile-cc2650 || die
+    arm-none-eabi-size  output/cc2650/bin/ot-cli-mtd || die
+    arm-none-eabi-size  output/cc2650/bin/ot-ncp-mtd || die
+}
+
+[ $BUILD_TARGET != arm-gcc63 ] || {
+    export PATH=/tmp/arc_gnu_2017.03-rc2_prebuilt_elf32_le_linux_install/bin:$PATH || die
+
+    git checkout -- . || die
+    git clean -xfd || die
+    ./bootstrap || die
+    COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 make -f examples/Makefile-emsk || die
+    arc-elf32-size  output/emsk/bin/ot-cli-ftd || die
+    arc-elf32-size  output/emsk/bin/ot-cli-mtd || die
+    arc-elf32-size  output/emsk/bin/ot-ncp-ftd || die
+    arc-elf32-size  output/emsk/bin/ot-ncp-mtd || die
+}
+
+[ $BUILD_TARGET != posix ] || {
+    sh -c '$CC --version' || die
+    sh -c '$CXX --version' || die
+    ./bootstrap || die
+    make -f examples/Makefile-posix || die
+}
+
+[ $BUILD_TARGET != posix-distcheck ] || {
+    export ASAN_SYMBOLIZER_PATH=`which llvm-symbolizer-3.4` || die
+    export ASAN_OPTIONS=symbolize=1 || die
+    ./bootstrap || die
+    make -f examples/Makefile-posix distcheck || die
+}
+
+[ $BUILD_TARGET != posix-32-bit ] || {
+    ./bootstrap || die
+    COVERAGE=1 CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 make -f examples/Makefile-posix check || die
+}
+
+[ $BUILD_TARGET != posix-ncp-spi ] || {
+    ./bootstrap || die
+    make -f examples/Makefile-posix check configure_OPTIONS="--enable-ncp-app=ftd --with-ncp-bus=spi --with-examples=posix --with-platform-info=POSIX" || die
+}
+
+[ $BUILD_TARGET != posix-ncp ] || {
+    ./bootstrap || die
+    COVERAGE=1 NODE_TYPE=ncp-sim make -f examples/Makefile-posix check || die
+}
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..70ab4bc
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,16 @@
+# This is the official list of OpenThread authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder. To see the full list
+# of contributors, see the revision history in source control or
+# https://github.com/openthread/openthread/graphs/contributors
+#
+# Authors who wish to be recognized in this file should add themselves (or
+# their employer, as appropriate).
+
+Nest Labs, Inc.
+Microsoft Corporation
+Nordic Semiconductor
+Texas Instruments Incorporated
+NXP Semiconductors
+Synopsys, Inc.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..c721749
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,74 @@
+# OpenThread Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+  address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at openthread-conduct@google.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ce62736
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,142 @@
+# Contributing to OpenThread
+
+We would love for you to contribute to OpenThread and help make it even better than it is today! As a contributor, here are the guidelines we would like you to follow.
+
+* [1 Code of Conduct](#code-of-conduct)
+* [2 Bugs](#bugs)
+* [3 New Features](#new-features)
+* [4 Contributing Code](#contributing-code)
+  * [4.1 Initial Setup](#initial-setup)
+  * [4.2 Contributor License Agreement (CLA)](#contributor-license-agreement--cla-)
+  * [4.3 Submitting a Pull Request](#submitting-a-pull-request)
+
+## Code of Conduct
+
+Help us keep OpenThread open and inclusive.  Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).
+
+## Bugs
+
+If you find a bug in the source code, you can help us by [submitting a GitHub Issue](https://github.com/openthread/openthread/issues/new).  The best bug reports provide a detailed description of the issue and step-by-step instructions for predictably reproducing the issue.  Even better, you can [submit a Pull Request](#submitting-a-pull-request) with a fix.
+
+## New Features
+
+You can request a new feature by [submitting a GitHub Issue](https://github.com/openthread/openthread/issues/new).
+
+If you would like to implement a new feature, please consider the scope of the new feature:
+
+* *Large feature*: first [submit a GitHub Issue](https://github.com/openthread/openthread/issues/new) and communicate your proposal so that the community can review and provide feedback.  Getting early feedback will help ensure your implementation work is accepted by the community.  This will also allow us to better coordinate our efforts and minimize duplicated effort.
+
+* *Small feature*: can be implemented and directly [submitted as a Pull Request](#submitting-a-pull-request).
+
+## Contributing Code
+
+The OpenThread Project follows the "Fork-and-Pull" model for accepting contributions.
+
+### Initial Setup
+
+Setup your GitHub fork and continuous-integration services:
+
+1. Fork the [OpenThread repository](https://github.com/openthread/openthread) by clicking "Fork" on the web UI.
+2. Enable [Travis CI](https://travis-ci.org/) and [AppVeyor](https://ci.appveyor.com/) by logging in the respective services with your GitHub account and enabling your newly created fork.  We use Travis CI for Linux-based continuous integration checks and AppVeyor for Windows-based continuous integration checks.  All contributions must pass these checks to be accepted.
+
+Setup your local development environment:
+
+```bash
+# Clone your fork
+git clone git@github.com:<username>/openthread.git
+
+# Configure upstream alias
+git remote add upstream git@github.com:openthread/openthread.git
+```
+
+### Contributor License Agreement (CLA)
+
+The OpenThread Project requires all contributors to sign a Contributor License Agreement ([individual](https://developers.google.com/open-source/cla/individual) or [corporate](https://developers.google.com/open-source/cla/corporate)) in order to protect contributors, users, and Google in issues of intellectual property.
+
+With each Pull Request, an automated check occurs to verify that you have signed the CLA.  Make sure that you sign the CLA with the same email address associated with your commits (i.e. via the `user.email` Git config as described on GitHub's [Set up Git](https://help.github.com/articles/set-up-git/) page.
+
+NOTE: Only original source code from you and other people that have signed the CLA can be accepted into the repository. This policy does not apply to [third_party](https://github.com/openthread/openthread/tree/master/third_party).
+
+
+### Submitting a Pull Request
+
+#### Branch
+
+For each new feature, create a working branch:
+
+```bash
+# Create a working branch for your new feature
+git branch --track <branch-name> origin/master
+
+# Checkout the branch
+git checkout <branch-name>
+```
+
+#### Create Commits
+
+```bash
+# Add each modified file you'd like to include in the commit
+git add <file1> <file2>
+
+# Create a commit
+git commit
+```
+
+This will open up a text editor where you can craft your commit message.
+
+#### Upstream Sync and Clean Up
+
+Prior to submitting your pull request, you might want to do a few things to clean up your branch and make it as simple as possible for the original repo's maintainer to test, accept, and merge your work.
+
+If any commits have been made to the upstream master branch, you should rebase your development branch so that merging it will be a simple fast-forward that won't require any conflict resolution work.
+
+```bash
+# Fetch upstream master and merge with your repo's master branch
+git checkout master
+git pull upstream master
+
+# If there were any new commits, rebase your development branch
+git checkout <branch-name>
+git rebase master
+```
+
+Now, it may be desirable to squash some of your smaller commits down into a small number of larger more cohesive commits. You can do this with an interactive rebase:
+
+```bash
+# Rebase all commits on your development branch
+git checkout 
+git rebase -i master
+```
+
+This will open up a text editor where you can specify which commits to squash.
+
+#### Coding Conventions and Style
+
+OpenThread uses and enforces the [OpenThread Coding Conventions and Style](STYLE_GUIDE.md) on all code, except for code located in [third_party](third_party).
+
+As part of the cleanup process, you should also run `make pretty-check` to ensure that your code passes the baseline code style checks.
+
+```bash
+./bootstrap
+./configure --enable-ftd --enable-cli --enable-diag --enable-dhcp6-client --enable-dhcp6-server --enable-commissioner --enable-joiner --with-examples=posix
+make pretty-check
+
+```
+
+Make sure to include any code format changes in your commits.
+
+#### Push and Test
+
+```bash
+# Checkout your branch
+git checkout <branch-name>
+
+# Push to your GitHub fork:
+git push origin <branch-name>
+```
+
+This will trigger the Travis CI and AppVeyor continuous-integration checks.  You can view the results in the respective services.  Note that the integration checks will report failures on occasion.  If a failure occurs, you may try rerunning the test via the Travis and/or AppVeyor web UI.
+
+#### Submit Pull Request
+
+Once you've validated the Travis CI and AppVeyor results, go to the page for your fork on GitHub, select your development branch, and click the pull request button. If you need to make any adjustments to your pull request, just push the updates to GitHub. Your pull request will automatically track the changes on your development branch and update.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8417008
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2016, The OpenThread Authors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..81861e6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,204 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+AM_MAKEFLAGS                        = --no-print-directory
+
+AM_DISTCHECK_CONFIGURE_FLAGS        = \
+    --enable-address-sanitizer        \
+    --enable-cli-app=all              \
+    --enable-ncp-app=all              \
+    --with-ncp-bus=uart               \
+    --enable-diag                     \
+    --with-examples=posix             \
+    --enable-commissioner             \
+    --enable-joiner                   \
+    --enable-dhcp6-client             \
+    --enable-dhcp6-server             \
+    --enable-dns-client               \
+    --enable-application-coap         \
+    --enable-border-router            \
+    $(NULL)
+
+SUBDIRS                             = \
+    include                           \
+    src                               \
+    third_party                       \
+    examples                          \
+    tests                             \
+    tools                             \
+    doc                               \
+    $(NULL)
+
+EXTRA_DIST                          = \
+    .astyle/astyle-opts               \
+    .astyle/astyle-wrap.sh            \
+    .default-version                  \
+    bootstrap                         \
+    etc                               \
+    README.md                         \
+    NOTICE                            \
+    CONTRIBUTING.md                   \
+    LICENSE                           \
+    $(NULL)
+
+BUILT_SOURCES                       = \
+    .local-version                    \
+    $(NULL)
+
+dist_doc_DATA                       = \
+    $(NULL)
+
+DISTCLEANFILES                      = \
+    .local-version                    \
+    $(NULL)
+
+PRETTY_SUBDIRS                      = \
+    examples                          \
+    include                           \
+    src                               \
+    tests                             \
+    tools                             \
+    $(NULL)
+
+# Ignore the pseudo flash files on Posix platform during diskcheck
+distcleancheck_listfiles            = \
+    $(AM_V_at)find . -type f -name "*flash"
+
+#
+# Package version files:
+#
+# .default-version - The default package version. This file is ALWAYS checked
+#                    in and should always represent the current baseline
+#                    version of the package.
+#
+# .dist-version    - The distributed package version. This file is NEVER
+#                    checked in within the upstream repository, is auto-
+#                    generated, and is only found in the package distribution.
+#
+# .local-version   - The current source code controlled package version. This
+#                    file is NEVER checked in within the upstream repository,
+#                    is auto-generated, and can always be found in both the
+#                    build tree and distribution.
+#
+# When present, the .local-version file is preferred first, the
+# .dist-version second, and the .default-version last.
+#
+
+VERSION_FILE                      := $(if $(wildcard $(builddir)/.local-version),$(builddir)/.local-version,$(if $(wildcard $(srcdir)/.dist-version),$(srcdir)/.dist-version,$(srcdir)/.default-version))
+
+#
+# Override autotool's default notion of the package version variables.
+# This ensures that when we create a source distribution that the
+# version is always the current version, not the version when the
+# package was bootstrapped.
+#
+
+OPENTHREAD_VERSION                ?= $(shell cat $(VERSION_FILE) 2> /dev/null)
+
+PACKAGE_VERSION                    = $(OPENTHREAD_VERSION)
+VERSION                            = $(PACKAGE_VERSION)
+
+distdir = $(PACKAGE)-$(shell                                     \
+if [ "$(origin OPENTHREAD_VERSION)" != "file" ]; then            \
+    echo "$(OPENTHREAD_VERSION)" ;                               \
+else                                                             \
+    $(abs_top_nlbuild_autotools_dir)/scripts/mkversion           \
+        -b "$(OPENTHREAD_VERSION)" "$(top_srcdir)";              \
+fi )
+
+#
+# check-file-.local-version
+#
+# Speculatively regenerate .local-version and check to see if it needs
+# to be updated.
+#
+# If OPENTHREAD_VERSION has been supplied anywhere other than in this file
+# (which is implicitly the contents of .local-version), then use that;
+# otherwise, attempt to generate it from the SCM system.
+#
+# This is called from $(call check-file,.local-version).
+#
+define check-file-.local-version
+if [ "$(origin OPENTHREAD_VERSION)" != "file" ]; then \
+    echo "$(OPENTHREAD_VERSION)" > "$(2)";            \
+else                                                             \
+    $(abs_top_nlbuild_autotools_dir)/scripts/mkversion           \
+        -b "$(OPENTHREAD_VERSION)" "$(top_srcdir)"    \
+        > "$(2)";                                                \
+fi
+endef
+
+#
+# check-file-.dist-version
+#
+# Speculatively regenerate .dist-version and check to see if it needs
+# to be updated.
+#
+# This is called from $(call check-file,.dist-version).
+#
+define check-file-.dist-version
+cat "$(1)" > "$(2)"
+endef
+
+#
+# A convenience target to allow package users to easily rerun the
+# package configuration according to the current configuration.
+#
+.PHONY: reconfigure
+reconfigure: $(builddir)/config.status
+	$(AM_V_at)$(<) --recheck
+
+#
+# Version file regeneration rules.
+#
+.PHONY: force
+
+$(builddir)/.local-version: $(srcdir)/.default-version force
+
+$(distdir)/.dist-version: $(builddir)/.local-version force
+
+$(distdir)/.dist-version $(builddir)/.local-version:
+	$(call check-file,$(@F))
+
+dist distcheck: $(BUILT_SOURCES)
+
+dist-hook: $(distdir)/.dist-version
+
+#
+# Top-level convenience target for making a documentation-only
+# distribution whose results appear at the top level of the build tree
+# in the same fashion that the distribution would be for 'make dist'.
+#
+
+.PHONY: docdist
+docdist: $(BUILT_SOURCES)
+	$(MAKE) -C doc docdistdir=$(abs_builddir) $(@)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..6c1dad8
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,19 @@
+OpenThread is an open source implementation of the Thread 1.0.1 Final Specification.
+The Thread 1.0.1 Final Specification is promulgated by the Thread Group. The Thread
+Group is a non-profit organization formed for the purposes of defining one or
+more specifications, best practices, reference architectures, implementation
+guidelines and certification programs to promote the availability of compliant
+implementations of the Thread protocol. Information on becoming a Member, including
+information about the benefits thereof, can be found at http://threadgroup.org.
+
+OpenThread is not affiliated with or endorsed by the Thread Group. Implementation
+of this OpenThread code does not assure compliance with the Thread 1.0.1 Final
+Specification and does not convey the right to identify any final product as Thread
+certified. Members of the Thread Group may hold patents and other intellectual
+property rights relating to the Thread 1.0.1 Final Specification, ownership and
+licenses of which are subject to the Thread Group’s IP Policies, and not this license.
+
+The included copyright to the OpenThread code is subject to the license in the
+LICENSE file, and all other rights and licenses are expressly reserved.
+No warranty or assurance is made with respect to additional rights that may be
+required to implement this code.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..213bf7f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,184 @@
+[![OpenThread][ot-logo]][ot-repo]  
+[![Build Status][ot-travis-svg]][ot-travis]
+[![Build Status][ot-appveyor-svg]][ot-appveyor]
+[![Coverage Status][ot-codecov-svg]][ot-codecov]
+
+---
+
+# What is OpenThread?  
+
+OpenThread is...
+<a href="http://threadgroup.org/technology/ourtechnology#certifiedproducts">
+<img src="https://cdn.rawgit.com/openthread/openthread/ab4c4e1e/doc/images/certified.svg" alt="Thread Certified Component" width="150px" align="right">
+</a>
+
+**...an open-source implementation of the [Thread](http://threadgroup.org/technology/ourtechnology) networking protocol.** Nest has released OpenThread to make the technology used in Nest products more broadly available to developers to accelerate the development of products for the connected home.
+
+**...OS and platform agnostic**, with a narrow platform abstraction layer and a small memory footprint, making it highly portable.
+
+**...a Thread Certified Component**, implementing all features defined in the [Thread 1.1.1 specification](http://threadgroup.org/technology/ourtechnology#specifications). This specification defines an IPv6-based reliable, secure and low-power wireless device-to-device communication protocol for home applications.
+
+More information about Thread can be found on [threadgroup.org](http://threadgroup.org/).
+
+[thread]: http://threadgroup.org/technology/ourtechnology
+[ot-repo]: https://github.com/openthread/openthread
+[ot-logo]: doc/images/openthread_logo.png
+[ot-travis]: https://travis-ci.org/openthread/openthread
+[ot-travis-svg]: https://travis-ci.org/openthread/openthread.svg?branch=master
+[ot-appveyor]: https://ci.appveyor.com/project/jwhui/openthread
+[ot-appveyor-svg]: https://ci.appveyor.com/api/projects/status/r5qwyhn9p26nmfk3?svg=true
+[ot-codecov]: https://codecov.io/gh/openthread/openthread
+[ot-codecov-svg]: https://codecov.io/gh/openthread/openthread/branch/master/graph/badge.svg
+
+# Get started with OpenThread
+
+<a href="https://codelabs.developers.google.com/codelabs/openthread-simulation/index.html">
+<img src="doc/images/ot-codelab.png" alt="OpenThread Codelab" width="300px" align="right">
+</a>
+
+Want to try OpenThread? The quickest way to get started is to run through our [Simulation Codelab](https://codelabs.developers.google.com/codelabs/openthread-simulation/index.html), which covers all the basics, without the need for test hardware. Using VirtualBox and Vagrant on a Mac or Linux machine, you will learn:
+
+* How to set up the OpenThread build toolchain
+* How to simulate a Thread network
+* How to authenticate Thread nodes with Commissioning
+* How to use `wpantund` to manage a simulated Thread network featuring an NCP
+
+### Next Steps
+
+The Codelab shows you how easy it is use to OpenThread to simulate a Thread network. Once complete:
+
+1. Learn more about the [OpenThread architecture and features](#openthread-features)
+1. Get familiar with [platforms and devices that support OpenThread](#who-supports-openthread)
+1. See what [testing tools](#what-tools-are-available-for-testing) are available
+1. Learn [where to get help](#need-help) and [how to contribute](#want-to-contribute) to the ongoing development of OpenThread
+
+# OpenThread Features
+
+OpenThread implements all features defined in the [Thread 1.1.1 specification](http://threadgroup.org/technology/ourtechnology#specifications), including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as [Border Router](https://github.com/openthread/borderrouter) support.
+
+OpenThread supports both system-on-chip (SoC) and network co-processor (NCP) designs. Other features and enhancements include:
+
+* Application support and services
+    * IPv6 configuration and raw data interface
+    * UDP sockets
+    * CoAP client and server
+    * DHCPv6 client and server
+    * DNSv6 client
+    * Command Line Interface (CLI)
+* NCP support
+    * Spinel - general purpose NCP protocol
+    * `wpantund` - user-space NCP network interface driver/daemon
+    * Sniffer support via NCP Spinel nodes
+* Border Router
+    * Web UI for configuration and management
+    * Thread Border Agent to support an External Commissioner
+    * NAT64 for connecting to IPv4 networks
+    * Thread interface driver using `wpantund`
+
+### What's coming?
+
+The development of OpenThread is ongoing to provide additional features not available in the standard. Check back regularly for new updates, or visit the [openthread-announce](https://groups.google.com/forum/#!forum/openthread-announce) Google Group.
+
+# Who supports OpenThread?
+
+Led by Nest, the following companies are contributing to the ongoing development of OpenThread:
+
+<a href="https://www.arm.com/"><img src="doc/images/ot-contrib-arm.png" alt="ARM" width="200px"></a><a href="http://www.atmel.com/"><img src="doc/images/ot-contrib-atmel.png" alt="Atmel" width="200px"></a><a href="http://www.dialog-semiconductor.com/"><img src="doc/images/ot-contrib-dialog.png" alt="Dialog" width="200px"></a><a href="https://www.microsoft.com/en-us/"><img src="doc/images/ot-contrib-ms.png" alt="Microsoft" width="200px"></a><a href="https://nest.com/"><img src="doc/images/ot-contrib-nest.png" alt="Nest" width="200px"></a><a href="http://www.nordicsemi.com/"><img src="doc/images/ot-contrib-nordic.png" alt="Nordic" width="200px"></a><a href="http://www.nxp.com/"><img src="doc/images/ot-contrib-nxp.png" alt="NXP" width="200px"></a><a href="https://www.qualcomm.com/"><img src="doc/images/ot-contrib-qc.png" alt="Qualcomm" width="200px"></a><a href="https://www.synopsys.com/"><img src="doc/images/ot-contrib-synopsys.png" alt="Synopsys" width="200px"></a><a href="https://www.ti.com/"><img src="doc/images/ot-contrib-ti.png" alt="Texas Instruments" width="200px"></a>
+
+OpenThread has been ported to several devices and platforms by both the OpenThread team and the community. Build examples for all supported platforms are included in the OpenThread project.
+
+### IEEE 802.15.4 Platform Support
+
+* [Dialog Semiconductor DA15000](https://github.com/openthread/openthread/wiki/Platforms#dialog-da15000)
+* [Nordic Semiconductor nRF52840](https://github.com/openthread/openthread/wiki/Platforms#nordic-semiconductor-nrf52840)
+* [NXP KW41Z](https://github.com/openthread/openthread/wiki/Platforms#nxp-kw41z)
+* [Silicon Labs EFR32](https://github.com/openthread/openthread/wiki/Platforms#silicon-labs-efr32)
+* [Synopsys ARC EMSK with Microchip MRF24J40](https://github.com/openthread/openthread/wiki/Platforms#synopsys-arc-em-with-microchip-mrf24j40)
+* [Texas Instruments CC2538](https://github.com/openthread/openthread/wiki/Platforms#texas-instruments-cc2538)
+* [Texas Instruments CC2650](https://github.com/openthread/openthread/wiki/Platforms#texas-instruments-cc2650)
+* [POSIX Emulation](https://github.com/openthread/openthread/wiki/Platforms#posix-emulation)
+
+See the [Wiki Platform page](https://github.com/openthread/openthread/wiki/Platforms) for more detailed information on supported platforms.
+
+### Desktop Support
+
+Desktop platforms can also be used to control and interface with a Thread network using OpenThread:
+
+* **Unix** — [`wpantund`](https://github.com/openthread/wpantund) provides an interface to an NCP
+* **Windows 10** — [universal drivers](https://github.com/openthread/openthread/wiki/OpenThread-on%C2%A0Windows) to interface with devices running OpenThread
+
+### Porting
+
+If you are interested in porting OpenThread to a new platform, see the [Porting Guide](https://github.com/openthread/openthread/wiki/Porting-Guide) for hardware requirements and detailed porting instructions.
+
+### Border Router
+
+A Border Router connects a Thread network to networks at different layers, such as WiFi or Ethernet. [OpenThread Border Router](https://github.com/openthread/borderrouter) provides end-to-end IP via routing between Thread devices and other external IP networks, as well as external Thread Commissioning.
+
+# What tools are available for testing?
+
+### Certification Testing
+
+Certification testing is done with the [GRL Thread Test Harness software](http://graniteriverlabs.com/thread/), available for download to Thread member companies.
+
+Additional tools that extend the Test Harness are included in the OpenThread project:
+
+* [Thread Harness Automation](https://github.com/openthread/openthread/tree/master/tools/harness-automation) — automates the Thread Test Harness software
+* [Thread Harness THCI for OpenThread](https://github.com/openthread/openthread/tree/master/tools/harness-thci) — allows the Thread Test Harness to control OpenThread-based reference devices directly
+    * CC2538 example included in the GRL Thread Test Hardness software
+    * Library version can be modified by developers for use on other platforms
+
+### Sniffer
+
+OpenThread also provides a [sniffer](https://github.com/openthread/openthread/blob/master/tools/spinel-cli/SNIFFER.md) on the NCP build. The sniffer is exposed by the Spinel protocol and features:
+
+* Monitor mode — capture packets during operation
+* Promiscuous mode — dedicated sniffer
+* Host-side support — `wpantund`
+* pcap stream output
+
+# Need help?
+
+### Wiki
+
+Explore the [OpenThread Wiki](https://github.com/openthread/openthread/wiki) for more in-depth documentation on building, testing, automation and tools.
+
+### Interact
+
+There are numerous avenues for OpenThread support:
+
+* Bugs and feature requests — [submit to the Issue Tracker](https://github.com/openthread/openthread/issues)
+* Stack Overflow — [post questions using the `openthread` tag](http://stackoverflow.com/questions/tagged/openthread)
+* Google Groups — discussion and announcements
+    * [openthread-announce](https://groups.google.com/forum/#!forum/openthread-announce) — release notes and new updates on OpenThread
+    * [openthread-users](https://groups.google.com/forum/#!forum/openthread-users) — the best place for users to discuss OpenThread and interact with the OpenThread team
+
+### Directory Structure
+
+The OpenThread repository is structured as follows:
+
+Folder   | Contents
+--------------|----------------------------------------------------------------
+`doc`         | Spinel docs and Doxygen build file
+`etc`         | Configuration files for other build systems (e.g. Visual Studio)
+`examples`    | Sample applications and platforms demonstrating OpenThread
+`include`     | Public API header files
+`src`         | Core implementation of the Thread standard and related add-ons
+`tests`       | Unit and Thread conformance tests
+`third_party` | Third-party code used by OpenThread
+`tools`       | Helpful utilities related to the OpenThread project
+
+
+# Want to contribute?
+
+We would love for you to contribute to OpenThread and help make it even better than it is today! See the [`CONTRIBUTING.md`](https://github.com/openthread/openthread/blob/master/CONTRIBUTING.md) file for more information.
+
+# Versioning
+
+OpenThread follows the [Semantic Versioning guidelines](http://semver.org/) for release cycle transparency and to maintain backwards compatibility. OpenThread's versioning is independent of the Thread protocol specification version but will clearly indicate which version of the specification it currently supports.
+
+# License
+
+OpenThread is released under the [BSD 3-Clause license](https://github.com/openthread/openthread/blob/master/LICENSE). See the [`LICENSE`](https://github.com/openthread/openthread/blob/master/LICENSE) file for more information.  
+  
+Please only use the OpenThread name and marks when accurately referencing this software distribution. Do not use the marks in a way that suggests you are endorsed by or otherwise affiliated with Nest, Google, or The Thread Group.
diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md
new file mode 100644
index 0000000..eca3494
--- /dev/null
+++ b/STYLE_GUIDE.md
@@ -0,0 +1,182 @@
+# OpenThread Coding Conventions and Style
+
+* [1 C and C++](#c-and-c)
+  * [1.1 Standards](#standards)
+  * [1.2 Conventions and Best Practices](#conventions-and-best-practices)
+  * [1.3 Tightly-constrained Systems and Shared Infrastructure](#tightly-constrained-systems-and-shared-infrastructure)
+  * [1.4 Format and Style](#format-and-style)
+  * [1.5 Comments](#comments)
+* [2 Python](#python)
+  * [2.1 Standards](#standards)
+  * [2.2 Conventions and Best Practices](#conventions-and-best-practices)
+  * [2.3 Format and Style](#format-and-style)
+
+# C and C++
+
+## Standards
+
+- C
+  - OpenThread uses and enforces the ISO9899:1999 (aka ISO C99, C99) C language standard as the minimum.
+- C++
+  - OpenThread uses and enforces the ISO14882:2003 (aka ISO C++03, C++03) C++ language standard as the minimum.
+- Extensions
+  - Wherever possible, toolchain-specific (e.g GCC/GNU) extensions or the use of later standards shall be avoided or shall be leveraged through toolchain-compatibility preprocessor macros.
+
+## Conventions and Best Practices
+
+### Language Independent
+
+- Inline functions should be used judiciously.
+  - The use of code in headers and, more specifically, the use of the non-local scope inline functions should be avoided.  Exception: Simple setters and getters are fine since the compiler can efficiently optimize these and make their overhead as low as a direct data member access.
+- Return Statements
+  - There should be one return statement per free function or method at the end of the free function or method.
+- Non-local Goto
+  - There should be no calls to the functions `setjmp` or `longjmp`.
+- Local Goto
+  - There should be no calls to the C/C++ keyword goto.  Exception: The use of local gotos for the purposes of common error handling blocks and single points of function return at the bottom of a function.
+- C Preprocessor
+  - Use of the C preprocessor should be limited to file inclusion and simple macros.
+  - Macros shall not be defined within a function or a block and should be defined at the top of a file.
+  - All `#else`, `#elif`, and `#endif` preprocessor directives shall reside in the same file as the `#if` or `#ifdef` directive to which they are related.
+  - All `#endif` directives equal to or greater than 20 lines away from the `#if` or `#ifdef` directive to which they are related shall be decorated by language comment indicating the conditional they are associated with.
+  - Preprocessor `#include` directives in a file shall only be preceded by other preprocessor directives or comments.
+  - Preprocessor `#include` directives shall use brace (“<”) and (“>”) style for all public headers, including C and C++ standard library, or other first- and third-party public library headers.
+  - Preprocessor `#include` directives should use double quote (‘“‘) and (‘“‘) style for all private or relative headers.
+  - Preprocessor `#include` directives should be grouped, ordered, or sorted as follows:
+    - This compilation unit's corresponding header, if any.
+    - C++ Standard Library headers
+    - C Standard Library headers
+    - Third-party library headers
+    - First-party library headers
+    - Private or local headers
+    - Alphanumeric order within each subgroup
+  - The preprocessor shall not be used to redefine reserved language keywords.
+  - Unused code shall not be disabled by commenting it out with C- or C++-style comments or with preprocessor `#if 0 ... #endif` semantics.
+  - Use of the preprocessor token concatenation operator '##' should be avoided.
+  - The `undef` preprocessor directive should be avoided and shall never be used to undefine a symbol from a foreign module.
+- Object Scope
+  - Data objects shall be declared at the smallest possible level of scope.
+  - No declaration in an inner scope shall hide or shadow a declaration in an outer scope. Compiler flags shall be set to flag and enforce this.
+- Unbounded Recursion
+  - There shall be no direct or indirect use of unbounded recursive function calls.
+- Symmetric APIs
+  - Wherever possible and appropriate, particularly around the management of resources, APIs should be symmetric.  For example, if there is a free function or object method that allocates a resource, then there should be one that deallocates it. If there is a free function or object method that opens a file or network stream, then there should be one that closes it.
+- Use C stdint.h or C++ cstdint for Plain Old Data Types
+  - Standard, scalar data types defined in stdint.h (C) or cstdint (C++) should be used for basic signed and unsigned integer types, especially when size and serialization to non-volatile storage or across a network is concerned.  Examples of these are: `uint8_t`, `int8_t`, etc.
+- Constant Qualifiers
+  - Read-only methods, global variables, stack variables, or data members are read-only should be qualified using the C or C++ `const` qualifier.
+  - Pointers or references to read-only objects or storage, including but not limited to function parameters, should be qualified using the C or C++ `const` qualifier.
+- Header Include Guard
+  - All C and C++ headers shall use preprocessor header include guards.
+  - The terminating endif preprocessor directive shall have a comment, C or C++ depending on the header type, containing the preprocessor symbol introduced by the ifndef directive starting the guard.
+  - The symbol used for the guard should be the file name, converted to all uppercase, with any spaces (“ “) or dots (“.”) converted to underscores (“_”).
+- Function and Method Prototypes
+  - All void functions or methods shall explicitly declare and specify the void type keyword.
+
+### C
+
+- C / C++ Linkage Wrappers
+  - All header files intended to have C symbol linkage shall use “extern C” linkage wrappers.
+
+### C++
+
+- Prefer Passing Parameters by Reference to Pointer
+  - Unlike C, C++ offers an alternate way to alias data over and above a pointer, the reference, indicated by the & symbol.  Where appropriate, the reference should be preferred to the pointer.
+- Passing Base Scalars
+  - Size- and call frequency-based considerations should be made when passing scalars as to whether they should be passed by value or by constant reference; however, pass-by-value should generally be preferred.
+- Eliminate Unnecessary Destructors
+  - The creation of empty or useless destructors should be avoided.  Empty or useless destructors should be removed.
+- Default Parameters
+  - When you declare C++ free functions and object methods, you should avoid or minimize using default parameters.
+  - When you declare C++ virtual object methods, you shall avoid using default parameters.
+- Global and Scoped Static Construction
+  - There shall be no use of global, static or otherwise, object construction.  The use of scoped static object construction should be avoided.
+- C++-style Casts
+  - Wherever possible and practical, C++ style casts should be used and preferred to the C style cast equivalent.
+- Avoid `using namespace` Statements in Headers
+  - The C++ `using namespace` statement should not be used outside of object scope inside header files.
+
+## Tightly-constrained Systems and Shared Infrastructure
+
+- Heap-based resource allocation should be avoided.
+- There shall be no direct or indirect use of recursive function calls.
+- The use of virtual functions should be avoided.
+- The use of the C++ Standard Library shall be avoided.
+- The use of the C++ Standard Template Library (STL) should be avoided or minimized.
+- The use of the C++ templates should be avoided or minimized.
+- Code shall not use exceptions.
+- Code shall not use C++ runtime type information (RTTI), including facilities that rely upon it, such as `dynamic_cast` and `typeid`.
+
+## Format and Style
+
+- OpenThread uses the `make pretty` build target to reformat code and enforce code format and style.  The `make pretty-check` build target is included in OpenThread's continuous integration and must pass before a pull request is merged.
+
+### File Names
+- File names should match the names and types of what is described in the file.  If a file contains many declarations and definitions, the author should choose the one that predominantly describes or that makes the most sense.
+- File contents and names should be limited in the scope of what they contain. It may also be possible that there is too much stuff in one file and you need to break it up into multiple files.
+- File names should be all lower case.
+- File extensions shall be indicative and appropriate for the type and usage of the source or header file.
+
+### Naming
+- Names should be descriptive but not overly so and they should give some idea of scope and should be selected such that *wrong code looks wrong*.
+- Names shall not give any idea of type, such as is done with System Hungarian notation.
+- Case
+  - C preprocessor symbols should be all uppercase.
+  - All OpenThread names in the C language shall be in *snake case*.
+  - All OpenThread class, namespace, structure, method, function, enumeration, and type names in the C++ language shall be in *upper camel case*.  Exception: the top level OpenThread namespace 'ot'.
+  - All OpenThread instantiated names of instances of classes, namespaces, structures, methods, functions, enumerations, and types as well as method and function parameters in the C++ language shall be in *lower camel case*.
+- Symbol Qualification
+  - All OpenThread C public data types and free functions should have `ot` prepended to their name.
+  - All OpenThread C++ code should be in the ‘ot’ top-level namespace.
+- Scope
+  - All global data shall have a `g` prepended to the name to denote global scope.
+  - All static data shall have a `s` prepended to the name to denote static scope.
+  - All class or structure data members shall have a `m` prepended to the name to denote member scope.
+  - All free function or method parameters should have an `a` prepended to the name to denote function parameter scope.
+  - All variables that do not have such prefixes shall be assumed to be function local scope.
+
+### White Space
+- Indentation shall be 4 space characters.
+- Conditionals shall always appear on a separate line from the code to execute as a result of the condition.
+- Scoped Variable declarations
+  - All scoped (i.e. stack) variable declarations should be placed together at the top of the enclosing scope in which they are used.
+  - There shall be an empty line after all such variable declarations.
+  - The names of all variable declarations should be left aligned.
+- Data Member declarations
+  - All data member declarations should be placed together.
+  - The names of all data member declarations should be left aligned.
+  - The data member declarations for C++ classes should be placed at the end or tail of the class.
+- Braces
+  - Braces should go on their own lines.
+  - Statements should never be on the same line following a closing brace.
+- Keywords
+  - There should be a single space after language-reserved keywords (for, while, if, etc).
+
+## Comments
+
+- All code should use Doxygen to:
+  - Detail what the various source and header files are and how they fit into the broader context.
+  - Detail what the various C++ namespaces are.
+  - Detail what the constants, C preprocessor definitions, and enumerations are.
+  - Detail what the globals are and how they are to be used.
+  - Detail what the free function and object / class methods are and how they are to be used, what their parameters are, and what their return values are.
+  - Detail any other important technical information or theory of operation unique and relevant to the stack that is not otherwise captured in architecture, design, or protocol documentation.
+- Every public, and ideally private, free function and class method should likewise have a prologue comment that:
+  - Briefly describes what it is and what it does.
+  - Describes in detail, optionally, what it is and what it does.
+  - Describes the purpose, function, and influence of each parameter as well as whether it is an input, an output, or both.
+  - Describes the return value, if present, and the expected range or constraints of it.
+
+# Python
+
+## Standards
+
+- OpenThread uses and enfores both Python 2 and Python 3.  Support for Python 2 is a result of the fact that some current Linux distributions and Macs are still using 2.x as default.
+
+## Conventions and Best Practices
+
+- Run `pylint` over your code.  `pylint` is a tool for finding bugs and style problems in Python source code. It finds problems that are typically caught by a compiler for less dynamic languages like C and C++. Because of the dynamic nature of Python, some warnings may be incorrect; however, spurious warnings should be fairly infrequent.
+
+## Format and Style
+
+- All code should adhere to [PEP 8](https://www.python.org/dev/peps/pep-0008/).
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..b6f8ee0
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+#    Description:
+#      This file is a trampoline script to the nlbuild-autotools
+#      bootstrap script and augments it by providing the path to the
+#      nlbuild-autotools repository for this project.
+#
+
+# Set this to the relative location of nlbuild-autotools to this script
+
+nlbuild_autotools_stem="third_party/nlbuild-autotools/repo"
+
+# Establish some key directories
+
+srcdir=`dirname ${0}`
+abs_srcdir=`pwd`
+abs_top_srcdir="${abs_srcdir}"
+
+exec ${srcdir}/${nlbuild_autotools_stem}/scripts/bootstrap -I "${abs_top_srcdir}/${nlbuild_autotools_stem}" $*
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..3f43a29
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1438 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# Declare autoconf version requirements
+#
+AC_PREREQ([2.68])
+
+#
+# Initialize autoconf for the package
+#
+AC_INIT([OPENTHREAD],
+        m4_esyscmd([third_party/nlbuild-autotools/repo/scripts/mkversion -b `cat .default-version` .]),
+        [openthread-devel@googlegroups.com],
+        [openthread],
+        [http://github.com/openthread/openthread])
+
+# Tell the rest of the build system the absolute path where the
+# nlbuild-autotools repository is rooted at.
+
+AC_SUBST(nlbuild_autotools_stem,[third_party/nlbuild-autotools/repo])
+AC_SUBST(abs_top_nlbuild_autotools_dir,[\${abs_top_srcdir}/\${nlbuild_autotools_stem}])
+
+#
+# OPENTHREAD interface current, revision, and age versions.
+#
+# Maintainters: Please manage these fields as follows:
+#
+#   Interfaces removed:    CURRENT++, AGE = 0, REVISION = 0
+#   Interfaces added:      CURRENT++, AGE++,   REVISION = 0
+#   No interfaces changed:                     REVISION++
+#
+#
+AC_SUBST(LIBOPENTHREAD_VERSION_CURRENT,  [0])
+AC_SUBST(LIBOPENTHREAD_VERSION_AGE,      [5])
+AC_SUBST(LIBOPENTHREAD_VERSION_REVISION, [0])
+AC_SUBST(LIBOPENTHREAD_VERSION_INFO,     [${LIBOPENTHREAD_VERSION_CURRENT}:${LIBOPENTHREAD_VERSION_REVISION}:${LIBOPENTHREAD_VERSION_AGE}])
+
+#
+# Check the sanity of the source directory by checking for the
+# presence of a key watch file
+#
+AC_CONFIG_SRCDIR([include/openthread/openthread.h])
+
+#
+# Tell autoconf where to find auxilliary build tools (e.g. config.guess,
+# install-sh, missing, etc.)
+#
+AC_CONFIG_AUX_DIR([third_party/nlbuild-autotools/repo/autoconf])
+
+#
+# Tell autoconf where to find auxilliary M4 macros
+#
+AC_CONFIG_MACRO_DIR([third_party/nlbuild-autotools/repo/autoconf/m4])
+
+#
+# Tell autoconf what file the package is using to aggregate C preprocessor
+# defines.
+#
+AC_CONFIG_HEADERS([include/openthread-config-generic.h])
+
+#
+# Figure out what the canonical build and host tuples are.
+#
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+
+#
+# Mac OS X / Darwin ends up putting some versioning cruft on the end of its
+# tuple that we don't care about in this script. Create "clean" variables
+# devoid of it.
+#
+
+NL_FILTERED_CANONICAL_BUILD
+NL_FILTERED_CANONICAL_HOST
+
+#
+# Configure automake with the desired options, indicating that this is not
+# a native GNU package, that we want "silent" build rules, and that we want
+# objects built in the same subdirectory as their source rather than collapsed
+# together at the top-level directory.
+#
+# Disable silent build rules by either passing --disable-silent-rules to
+# configure or passing V=1 to make
+#
+AM_INIT_AUTOMAKE([1.14 foreign silent-rules subdir-objects tar-pax])
+
+#
+# Silent build rules requires at least automake-1.11. Employ
+# techniques for not breaking earlier versions of automake.
+#
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AM_SILENT_RULES([yes])
+
+#
+# Enable maintainer mode to prevent the package from constantly trying
+# to rebuild configure, Makefile.in, etc. Rebuilding such files rarely,
+# if ever, needs to be done "in the field".
+#
+# Use the included 'bootstrap' script instead when necessary.
+#
+AM_MAINTAINER_MODE
+
+#
+# Host-os-specific checks
+#
+
+case ${host_os} in
+
+    *darwin*)
+        OPENTHREAD_TARGET=darwin
+        OPENTHREAD_TARGET_DEFINES="-DOPENTHREAD_TARGET_DARWIN"
+        ;;
+
+    *linux*)
+        OPENTHREAD_TARGET=linux
+        OPENTHREAD_TARGET_DEFINES="-DOPENTHREAD_TARGET_LINUX"
+        ;;
+
+esac
+
+AC_SUBST(OPENTHREAD_TARGET_DARWIN)
+AM_CONDITIONAL([OPENTHREAD_TARGET_DARWIN], [test "${OPENTHREAD_TARGET}" = "darwin"])
+
+AC_SUBST(OPENTHREAD_TARGET_LINUX)
+AM_CONDITIONAL([OPENTHREAD_TARGET_LINUX], [test "${OPENTHREAD_TARGET}" = "linux"])
+
+AC_SUBST(OPENTHREAD_TARGET_DEFINES)
+
+#
+# Checks for build host programs
+#
+
+# This is a hack to restore some old broken behavior that was
+# removed in pull request #1527. It's use is highly discouraged,
+# you should try to fix your build environment instead.
+AC_ARG_ENABLE(no-executables-hack,
+    [AS_HELP_STRING([--enable-no-executables-hack],
+        [Enable hack that prevents link checks at configure time. Highly discouraged.])])
+AC_MSG_CHECKING([whether to disable executable checking])
+if test "${enable_no_executables_hack}" = "yes"
+then
+    AC_MSG_RESULT([yes])
+    AC_NO_EXECUTABLES
+    # Here we guess conservative values for tests that require link checks
+    # to test for these features. This will prevent these checks from
+    # being performed later in the configuration process.
+    ac_cv_func_strlcat=${ac_cv_func_strlcat-no}
+    ac_cv_func_strlcpy=${ac_cv_func_strlcpy-no}
+    ac_cv_func_strnlen=${ac_cv_func_strnlen-no}
+else
+    AC_MSG_RESULT([no])
+fi
+
+# Passing -Werror to GCC-based or -compatible compilers breaks some
+# autoconf tests (see
+# http://lists.gnu.org/archive/html/autoconf-patches/2008-09/msg00014.html).
+#
+# If -Werror has been passed transform it into -Wno-error. We'll
+# transform it back later with NL_RESTORE_WERROR.
+
+NL_SAVE_WERROR
+
+# Check for compilers.
+#
+# These should be checked BEFORE we check for and, implicitly,
+# initialize libtool such that libtool knows what languages it has to
+# work with.
+
+AC_PROG_CPP
+AC_PROG_CPP_WERROR
+
+AC_PROG_CC
+AC_PROG_CC_C_O
+
+AC_PROG_CXXCPP
+
+AC_PROG_CXX
+AC_PROG_CXX_C_O
+
+AM_PROG_AS
+
+# Check for other compiler toolchain tools.
+
+AC_CHECK_TOOL(AR, ar)
+AC_CHECK_TOOL(RANLIB, ranlib)
+AC_CHECK_TOOL(OBJCOPY, objcopy)
+AC_CHECK_TOOL(STRIP, strip)
+
+# Check for other host tools.
+
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+AC_PATH_PROG(CMP, cmp)
+
+#
+# Checks for specific compiler characteristics
+#
+
+#
+# Common compiler flags we would like to have.
+#
+#   -Wall                        CC, CXX
+#
+
+PROSPECTIVE_CFLAGS="-Wall -Wextra -Wshadow -Werror -std=c99 -pedantic-errors"
+PROSPECTIVE_CXXFLAGS="-Wall -Wextra -Wshadow -Werror -std=gnu++98 -Wno-c++14-compat"
+
+AC_CACHE_CHECK([whether $CC is Clang],
+    [nl_cv_clang],
+    [nl_cv_clang=no
+    if test "x${GCC}" = "xyes"; then
+        AC_EGREP_CPP([NL_CC_IS_CLANG],
+            [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+#            if defined(__clang__) && defined(__llvm__)
+             NL_CC_IS_CLANG
+#            endif
+            ],
+            [nl_cv_clang=yes])
+    fi
+    ])
+
+if test "${nl_cv_clang}" = "yes"; then
+    PROSPECTIVE_CFLAGS="${PROSPECTIVE_CFLAGS} -Wconversion"
+    PROSPECTIVE_CXXFLAGS="${PROSPECTIVE_CXXFLAGS} -Wconversion"
+fi
+
+AX_CHECK_COMPILER_OPTIONS([C],   ${PROSPECTIVE_CFLAGS})
+AX_CHECK_COMPILER_OPTIONS([C++], ${PROSPECTIVE_CXXFLAGS})
+
+# Check for and initialize libtool
+
+LT_INIT
+AC_PROG_LIBTOOL
+
+# Disable building shared libraries by default (can be enabled with --enable-shared)
+
+AC_DISABLE_SHARED
+
+#
+# Debug instances
+#
+AC_MSG_NOTICE([checking whether to build debug instances])
+
+# Debug
+
+NL_ENABLE_DEBUG([no])
+
+AM_CONDITIONAL([OPENTHREAD_BUILD_DEBUG], [test "${nl_cv_build_debug}" = "yes"])
+
+#
+# Code coverage and compiler optimization
+#
+
+# Coverage
+
+NL_ENABLE_COVERAGE([no])
+
+AM_CONDITIONAL([OPENTHREAD_BUILD_COVERAGE], [test "${nl_cv_build_coverage}" = "yes"])
+
+NL_ENABLE_COVERAGE_REPORTS([auto])
+
+AM_CONDITIONAL([OPENTHREAD_BUILD_COVERAGE_REPORTS], [test "${nl_cv_build_coverage_reports}" = "yes"])
+
+# Optimization
+
+NL_ENABLE_OPTIMIZATION([yes])
+
+AM_CONDITIONAL([OPENTHREAD_BUILD_OPTIMIZED], [test "${nl_cv_build_optimized}" = "yes"])
+
+# Address Sanitizer
+
+AC_MSG_CHECKING([whether to build with Address Sanitizer support])
+AC_ARG_ENABLE(address-sanitizer,
+    [AS_HELP_STRING([--enable-address-sanitizer],[Enable Address Sanitizer support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_address_sanitizer=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enableval} for --enable-address-sanitizer])
+            ;;
+
+        esac
+    ],
+    [enable_address_sanitizer=no])
+AC_MSG_RESULT(${enable_address_sanitizer})
+
+AM_CONDITIONAL([OPENTHREAD_WITH_ADDRESS_SANITIZER], [test "${enable_address_sanitizer}" = "yes"])
+
+if test "${enable_address_sanitizer}" = "yes" ; then
+
+    PROSPECTIVE_CFLAGS="-fsanitize=address"
+
+    # Check if the compilers support address sanitizer
+    AX_CHECK_COMPILER_OPTIONS([C],   ${PROSPECTIVE_CFLAGS})
+    AX_CHECK_COMPILER_OPTIONS([C++], ${PROSPECTIVE_CFLAGS})
+
+fi
+
+#
+# Code style
+#
+
+AC_SUBST(PRETTY, ["\${abs_top_srcdir}/.astyle/astyle-wrap.sh"])
+AC_SUBST(PRETTY_ARGS, ["astyle --options=\${abs_top_srcdir}/.astyle/astyle-opts"])
+AC_SUBST(PRETTY_CHECK, ["\${abs_top_srcdir}/.astyle/astyle-wrap.sh"])
+AC_SUBST(PRETTY_CHECK_ARGS, ["astyle --options=\${abs_top_srcdir}/.astyle/astyle-opts --dry-run"])
+
+#
+# Tests
+#
+AC_MSG_NOTICE([checking whether to build tests])
+
+# Tests
+
+NL_ENABLE_TESTS([yes])
+
+AM_CONDITIONAL([OPENTHREAD_BUILD_TESTS], [test "${nl_cv_build_tests}" = "yes"])
+
+#
+# CLI Library
+#
+AC_ARG_ENABLE(cli-app,
+    [AS_HELP_STRING([--enable-cli-app],[Enable CLI support (no|mtd|ftd|all) @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+        all|both)
+            enable_cli_app=yes
+            enable_cli_app_mtd=yes
+            enable_cli_app_ftd=yes
+            ;;
+        no|yes)
+            enable_cli_app=${enableval}
+            enable_cli_app_mtd=${enableval}
+            enable_cli_app_ftd=${enableval}
+            ;;
+        mtd)
+            enable_cli_app=yes
+            enable_cli_app_mtd=yes
+            enable_cli_app_ftd=no
+            ;;
+        ftd)
+            enable_cli_app=yes
+            enable_cli_app_mtd=no
+            enable_cli_app_ftd=yes
+            ;;
+        *)
+            AC_MSG_ERROR([Invalid value ${enableval} for --enable-cli-app])
+            ;;
+        esac
+    ],
+    [
+     enable_cli_app=no
+     enable_cli_app_mtd=no
+     enable_cli_app_ftd=no])
+
+
+AC_MSG_CHECKING([cli-app modes])
+AC_MSG_RESULT(${enable_cli_app})
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI],     [test "${enable_cli_app}" == "yes"])
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI_MTD], [test "${enable_cli_app_mtd}" == "yes"])
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CLI_FTD], [test "${enable_cli_app_ftd}" == "yes"])
+AC_SUBST(OPENTHREAD_ENABLE_CLI)
+AC_SUBST(OPENTHREAD_ENABLE_CLI_FTD)
+AC_SUBST(OPENTHREAD_ENABLE_CLI_MTD)
+
+
+#
+# NCP app
+#
+AC_ARG_ENABLE(ncp-app,
+    [AS_HELP_STRING([--enable-ncp-app],[Enable NCP support (no|mtd|ftd|all) @<:@default=no@:>@.])],
+    [
+        # Map all & both to yes
+        case "${enableval}" in
+        all|both)
+            enableval=yes
+            ;;
+
+        *)
+            ;;
+
+        esac
+
+        case "${enableval}" in
+
+        no|yes)
+            enable_ncp_app=${enableval}
+            enable_ncp_app_mtd=${enableval}
+            enable_ncp_app_ftd=${enableval}
+            ;;
+
+        mtd)
+            enable_ncp_app=yes
+            enable_ncp_app_mtd=yes
+            enable_ncp_app_ftd=no
+            ;;
+
+        ftd)
+            enable_ncp_app=yes
+            enable_ncp_app_mtd=no
+            enable_ncp_app_ftd=yes
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enableval} for --enable-ncp-app])
+            ;;
+        esac
+    ],
+    [
+     enable_ncp_app=no
+     enable_ncp_app_ftd=no
+     enable_ncp_app_mtd=no
+    ]
+    )
+
+
+AC_MSG_CHECKING([ncp-app modes])
+AC_MSG_RESULT(${enable_ncp_app})
+AC_MSG_CHECKING([should NCP support ftd])
+AC_MSG_RESULT(${enable_ncp_app_ftd})
+AC_MSG_CHECKING([should NCP support mtd])
+AC_MSG_RESULT(${enable_ncp_app_mtd})
+
+AC_SUBST(OPENTHREAD_ENABLE_NCP)
+AC_SUBST(OPENTHREAD_ENABLE_NCP_MTD)
+AC_SUBST(OPENTHREAD_ENABLE_NCP_FTD)
+
+AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP],      [test "${enable_ncp_app}" == "yes"])
+AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_MTD],  [test "${enable_ncp_app_mtd}" == "yes"])
+AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_FTD],  [test "${enable_ncp_app_ftd}" == "yes"])
+
+#
+# NCP BUS - how does the NCP talk to the host?
+#
+
+AC_ARG_WITH(
+    [ncp-bus],
+    [AS_HELP_STRING([--with-ncp-bus],[Specify the NCP bus (none|spi|uart) @<:@default=none@:>@.])],
+    [
+        case "${with_ncp_bus}" in
+        "none")
+            OPENTHREAD_ENABLE_NCP_SPI=0
+            OPENTHREAD_ENABLE_NCP_UART=0
+            ;;
+        "spi")
+            OPENTHREAD_ENABLE_NCP_SPI=1
+            OPENTHREAD_ENABLE_NCP_UART=0
+            ;;
+        "uart")
+            OPENTHREAD_ENABLE_NCP_SPI=0
+            OPENTHREAD_ENABLE_NCP_UART=1
+            ;;
+        *)
+            AC_MSG_ERROR([unexpected --with-ncp-bus=${with_ncp_bus}])
+            ;;
+        esac
+    ],
+    [
+       OPENTHREAD_ENABLE_NCP_SPI=0
+       OPENTHREAD_ENABLE_NCP_UART=0
+    ])
+
+AC_SUBST(OPENTHREAD_ENABLE_NCP_SPI)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_SPI],  [test "${with_ncp_bus}"  = "spi"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_NCP_SPI],[${OPENTHREAD_ENABLE_NCP_SPI}],[Define to 1 to enable the NCP SPI interface.])
+AC_MSG_CHECKING([should NCP support SPI])
+AC_MSG_RESULT([${OPENTHREAD_ENABLE_SPI}])
+
+AC_SUBST(OPENTHREAD_ENABLE_NCP_UART)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP_UART], [test "${with_ncp_bus}"  = "uart"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_NCP_UART],[${OPENTHREAD_ENABLE_NCP_UART}],[Define to 1 to enable the NCP UART interface.])
+AC_MSG_CHECKING([sould NCP support UART])
+AC_MSG_RESULT([${OPENTHREAD_ENABLE_UART}])
+
+#
+# Multiple OpenThread Instances
+#
+
+AC_ARG_ENABLE(multiple-instances,
+    [AS_HELP_STRING([--enable-multiple-instances],[Enable support for multiple OpenThread instances @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_multiple_instances=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_multiple_instances} for --enable-multiple-instances])
+            ;;
+        esac
+    ],
+    [enable_multiple_instances=no])
+
+if test "$enable_multiple_instances" = "yes"; then
+    OPENTHREAD_ENABLE_MULTIPLE_INSTANCES=1
+else
+    OPENTHREAD_ENABLE_MULTIPLE_INSTANCES=0
+fi
+
+AC_MSG_RESULT(${enable_multiple_instances})
+AC_SUBST(OPENTHREAD_ENABLE_MULTIPLE_INSTANCES)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_MULTIPLE_INSTANCES], [test "${enable_multiple_instances}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_MULTIPLE_INSTANCES],[${OPENTHREAD_ENABLE_MULTIPLE_INSTANCES}],[Define to 1 if you want to enable support for multiple OpenThread instances.])
+
+#
+# Builtin mbedtls
+#
+
+AC_ARG_ENABLE(builtin-mbedtls,
+    [AS_HELP_STRING([--enable-builtin-mbedtls],[Enable builtin mbedtls @<:@default=yes@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_builtin_mbedtls=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_builtin_mbedtls} for --enable-builtin-mbedtls])
+            ;;
+        esac
+    ],
+    [enable_builtin_mbedtls=yes])
+
+if test "$enable_builtin_mbedtls" = "yes" -a ! "${MBEDTLS_CPPFLAGS}"; then
+    MBEDTLS_CPPFLAGS="-I\${abs_top_srcdir}/third_party/mbedtls"
+    MBEDTLS_CPPFLAGS="${MBEDTLS_CPPFLAGS} -I\${abs_top_srcdir}/third_party/mbedtls/repo/include"
+    MBEDTLS_CPPFLAGS="${MBEDTLS_CPPFLAGS} -I\${abs_top_srcdir}/third_party/mbedtls/repo/include/mbedtls"
+    MBEDTLS_CPPFLAGS="${MBEDTLS_CPPFLAGS} -DMBEDTLS_CONFIG_FILE=\\\"mbedtls-config.h\\\""
+fi
+AC_MSG_CHECKING([whether mbed TLS should be enabled])
+AC_MSG_RESULT(${enable_builtin_mbedtls})
+AM_CONDITIONAL([OPENTHREAD_ENABLE_BUILTIN_MBEDTLS], [test "${enable_builtin_mbedtls}" = "yes"])
+
+#
+# Thread TMF Proxy
+#
+
+AC_MSG_CHECKING([whether to enable TMF proxy])
+AC_ARG_ENABLE(tmf_proxy,
+    [AS_HELP_STRING([--enable-tmf-proxy],[Enable TMF proxy support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_tmf_proxy=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_tmf_proxy} for --enable-tmf-proxy])
+            ;;
+        esac
+    ],
+    [enable_tmf_proxy=no])
+
+if test "$enable_tmf_proxy" = "yes"; then
+    OPENTHREAD_ENABLE_TMF_PROXY=1
+else
+    OPENTHREAD_ENABLE_TMF_PROXY=0
+fi
+
+AC_MSG_RESULT(${enable_tmf_proxy})
+AC_SUBST(OPENTHREAD_ENABLE_TMF_PROXY)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_TMF_PROXY], [test "${enable_tmf_proxy}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_TMF_PROXY],[${OPENTHREAD_ENABLE_TMF_PROXY}],[Define to 1 to enable the TMF proxy feature.])
+
+#
+# Thread Network Diagnostic for MTD
+#
+
+AC_ARG_ENABLE(mtd_network_diagnostic,
+    [AS_HELP_STRING([--enable-mtd-network-diagnostic],[Enable network diagnostic support for MTD @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_mtd_network_diagnostic=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_mtd_network_diagnostic} for --enable-mtd-network-diagnostic])
+            ;;
+        esac
+    ],
+    [enable_mtd_network_diagnostic=no])
+
+if test "$enable_mtd_network_diagnostic" = "yes"; then
+    OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC=1
+else
+    OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC=0
+fi
+
+AC_MSG_CHECKING([whether to enable the network diagnostic for MTD])
+AC_MSG_RESULT(${enable_mtd_network_diagnostic})
+AC_SUBST(OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC], [test "${enable_mtd_network_diagnostic}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC],[${OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC}],[Define to 1 to enable network diagnostic for MTD.])
+
+#
+# Thread Commissioner
+#
+
+AC_ARG_ENABLE(commissioner,
+    [AS_HELP_STRING([--enable-commissioner],[Enable commissioner support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_commissioner=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_commissioner} for --enable-commissioner])
+            ;;
+        esac
+    ],
+    [enable_commissioner=no])
+
+if test "$enable_commissioner" = "yes"; then
+    OPENTHREAD_ENABLE_COMMISSIONER=1
+else
+    OPENTHREAD_ENABLE_COMMISSIONER=0
+fi
+
+AC_MSG_CHECKING([whether to enable the commissioner])
+AC_MSG_RESULT(${enable_commissioner})
+AC_SUBST(OPENTHREAD_ENABLE_COMMISSIONER)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_COMMISSIONER], [test "${enable_commissioner}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_COMMISSIONER],[${OPENTHREAD_ENABLE_COMMISSIONER}],[Define to 1 to enable the commissioner role.])
+
+#
+# Thread Joiner
+#
+
+AC_ARG_ENABLE(joiner,
+    [AS_HELP_STRING([--enable-joiner],[Enable joiner support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_joiner=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_joiner} for --enable-joiner])
+            ;;
+        esac
+    ],
+    [enable_joiner=no])
+
+if test "$enable_joiner" = "yes"; then
+    OPENTHREAD_ENABLE_JOINER=1
+else
+    OPENTHREAD_ENABLE_JOINER=0
+fi
+
+AC_MSG_CHECKING([whether to enable the joiner feature])
+AC_MSG_RESULT(${enable_joiner})
+AC_SUBST(OPENTHREAD_ENABLE_JOINER)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_JOINER], [test "${enable_joiner}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_JOINER],[${OPENTHREAD_ENABLE_JOINER}],[Define to 1 to enable the joiner role.])
+
+if test "${enable_commissioner}" = "yes" -o "${enable_joiner}" = "yes"; then
+    enable_dtls="yes"
+    OPENTHREAD_ENABLE_DTLS=1
+else
+    enable_dtls="no"
+    OPENTHREAD_ENABLE_DTLS=0
+fi
+
+AC_MSG_CHECKING([whether to enable DTLS due to joiner/commissioner])
+AC_MSG_RESULT(${enable_dtls})
+AC_SUBST(OPENTHREAD_ENABLE_DTLS)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_DTLS], [test "${enable_dtls}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DTLS],[${OPENTHREAD_ENABLE_DTLS}],[Define to 1 to enable dtls support.])
+
+#
+# Jam Detection
+#
+
+AC_ARG_ENABLE(jam_detection,
+    [AS_HELP_STRING([--enable-jam-detection],[Enable Jam Detection support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_jam_detection=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_jam_detection} for --enable-jam-detection])
+            ;;
+        esac
+    ],
+    [enable_jam_detection=no])
+
+if test "$enable_jam_detection" = "yes"; then
+    OPENTHREAD_ENABLE_JAM_DETECTION=1
+else
+    OPENTHREAD_ENABLE_JAM_DETECTION=0
+fi
+
+AC_MSG_CHECKING([whether to enable jam detection])
+AC_MSG_RESULT(${enable_jam_detection})
+AC_SUBST(OPENTHREAD_ENABLE_JAM_DETECTION)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_JAM_DETECTION], [test "${enable_jam_detection}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_JAM_DETECTION],[${OPENTHREAD_ENABLE_JAM_DETECTION}],[Define to 1 if you want to use jam detection feature])
+
+#
+# MAC Whitelist and Blacklist
+#
+
+AC_ARG_ENABLE(mac_whitelist,
+    [AS_HELP_STRING([--enable-mac-whitelist],[Enable MAC whitelist/blacklist support @<:@default=yes@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_mac_whitelist=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_mac_whitelist} for --enable-mac-whitelist])
+            ;;
+        esac
+    ],
+    [enable_mac_whitelist=yes])
+
+if test "$enable_mac_whitelist" = "yes"; then
+    OPENTHREAD_ENABLE_MAC_WHITELIST=1
+else
+    OPENTHREAD_ENABLE_MAC_WHITELIST=0
+fi
+
+AC_MSG_CHECKING([whether to enable mac whitelist])
+AC_MSG_RESULT(${enable_mac_whitelist})
+AC_SUBST(OPENTHREAD_ENABLE_MAC_WHITELIST)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_MAC_WHITELIST], [test "${enable_mac_whitelist}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_MAC_WHITELIST],[${OPENTHREAD_ENABLE_MAC_WHITELIST}],[Define to 1 if you want to use MAC whitelist/blacklist feature])
+
+#
+# Diagnostics Library
+#
+
+AC_ARG_ENABLE(diag,
+    [AS_HELP_STRING([--enable-diag],[Enable diagnostics support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_diag=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_diag} for --enable-diag])
+            ;;
+        esac
+    ],
+    [enable_diag=no])
+
+if test "$enable_diag" = "yes"; then
+    OPENTHREAD_ENABLE_DIAG=1
+else
+    OPENTHREAD_ENABLE_DIAG=0
+fi
+
+AC_MSG_CHECKING([whether to enable diag])
+AC_MSG_RESULT(${enable_diag})
+AC_SUBST(OPENTHREAD_ENABLE_DIAG)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_DIAG], [test "${enable_diag}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DIAG],[${OPENTHREAD_ENABLE_DIAG}],[Define to 1 if you want to use diagnostics module])
+
+#
+# Legacy Network
+#
+
+AC_ARG_ENABLE(legacy,
+    [AS_HELP_STRING([--enable-legacy],[Enable legacy network support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_legacy=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_legacy} for --enable-legacy])
+            ;;
+        esac
+    ],
+    [enable_legacy=no])
+
+if test "$enable_legacy" = "yes"; then
+    OPENTHREAD_ENABLE_LEGACY=1
+else
+    OPENTHREAD_ENABLE_LEGACY=0
+fi
+
+AC_MSG_CHECKING([whether to enable legacy])
+AC_MSG_RESULT(${enable_legacy})
+AC_SUBST(OPENTHREAD_ENABLE_LEGACY)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_LEGACY], [test "${enable_legacy}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_LEGACY],[${OPENTHREAD_ENABLE_LEGACY}],[Define to 1 if you want to use legacy network support])
+
+#
+# Child Supervision
+#
+
+AC_ARG_ENABLE(child_supervision,
+    [AS_HELP_STRING([--enable-child-supervision],[Enable child supervision feature @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_child_supervision=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_child_supervision} for --enable-child-supervision])
+            ;;
+        esac
+    ],
+    [enable_child_supervision=no])
+
+if test "$enable_child_supervision" = "yes"; then
+    OPENTHREAD_ENABLE_CHILD_SUPERVISION=1
+else
+    OPENTHREAD_ENABLE_CHILD_SUPERVISION=0
+fi
+
+AC_MSG_RESULT(${enable_child_supervision})
+AC_SUBST(OPENTHREAD_ENABLE_CHILD_SUPERVISION)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CHILD_SUPERVISION], [test "${enable_child_supervision}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CHILD_SUPERVISION],[${OPENTHREAD_ENABLE_CHILD_SUPERVISION}],[Define to 1 if you want to use child supervision feature])
+
+#
+# Log for certification test
+#
+
+AC_ARG_ENABLE(cert_log,
+    [AS_HELP_STRING([--enable-cert-log],[Enable certification log support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_cert_log=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_cert_log} for --enable-cert-log])
+            ;;
+        esac
+    ],
+    [enable_cert_log=no])
+
+if test "$enable_cert_log" = "yes"; then
+    OPENTHREAD_ENABLE_CERT_LOG=1
+else
+    OPENTHREAD_ENABLE_CERT_LOG=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_CERT_LOG)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_CERT_LOG], [test "${enable_cert_log}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CERT_LOG],[${OPENTHREAD_ENABLE_CERT_LOG}],[Define to 1 if you want to enable log for certification test])
+
+#
+# DHCPv6 Client
+#
+
+AC_ARG_ENABLE(dhcp6_client,
+    [AS_HELP_STRING([--enable-dhcp6-client],[Enable DHCPv6 client support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_dhcp6_client=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_dhcp6_client} for --enable-dhcp6-client])
+            ;;
+        esac
+    ],
+    [enable_dhcp6_client=no])
+
+if test "$enable_dhcp6_client" = "yes"; then
+    OPENTHREAD_ENABLE_DHCP6_CLIENT=1
+else
+    OPENTHREAD_ENABLE_DHCP6_CLIENT=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_DHCP6_CLIENT)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_DHCP6_CLIENT], [test "${enable_dhcp6_client}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DHCP6_CLIENT],[${OPENTHREAD_ENABLE_DHCP6_CLIENT}],[Define to 1 if you want to enable DHCPv6 Client])
+
+#
+# DHCPv6 Server
+#
+
+AC_ARG_ENABLE(dhcp6_server,
+    [AS_HELP_STRING([--enable-dhcp6-server],[Enable DHCPv6 server support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_dhcp6_server=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_dhcp6_server} for --enable-dhcp6-server])
+            ;;
+        esac
+    ],
+    [enable_dhcp6_server=no])
+
+if test "$enable_dhcp6_server" = "yes"; then
+    OPENTHREAD_ENABLE_DHCP6_SERVER=1
+else
+    OPENTHREAD_ENABLE_DHCP6_SERVER=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_DHCP6_SERVER)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_DHCP6_SERVER], [test "${enable_dhcp6_server}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DHCP6_SERVER],[${OPENTHREAD_ENABLE_DHCP6_SERVER}],[Define to 1 if you want to enable DHCPv6 Server])
+
+#
+# DNS Client
+#
+
+AC_ARG_ENABLE(dns_client,
+    [AS_HELP_STRING([--enable-dns-client],[Enable DNS client support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_dns_client=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_dns_client} for --enable-dns-client])
+            ;;
+        esac
+    ],
+    [enable_dns_client=no])
+
+if test "$enable_dns_client" = "yes"; then
+    OPENTHREAD_ENABLE_DNS_CLIENT=1
+else
+    OPENTHREAD_ENABLE_DNS_CLIENT=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_DNS_CLIENT)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_DNS_CLIENT], [test "${enable_dns_client}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DNS_CLIENT],[${OPENTHREAD_ENABLE_DNS_CLIENT}],[Define to 1 if you want to enable DNS Client])
+
+#
+# Application CoAP
+#
+
+AC_ARG_ENABLE(application_coap,
+    [AS_HELP_STRING([--enable-application-coap],[Enable CoAP to an application.@<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_application_coap=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_application_coap} for --enable-application-coap])
+            ;;
+        esac
+    ],
+    [enable_application_coap=no])
+
+if test "$enable_application_coap" = "yes"; then
+    OPENTHREAD_ENABLE_APPLICATION_COAP=1
+else
+    OPENTHREAD_ENABLE_APPLICATION_COAP=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_APPLICATION_COAP)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_APPLICATION_COAP], [test "${enable_application_coap}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_APPLICATION_COAP],[${OPENTHREAD_ENABLE_APPLICATION_COAP}],[Define to 1 if you want to enable CoAP to an application.])
+
+#
+# otLinkRaw API
+#
+
+AC_ARG_ENABLE(raw_link_api,
+    [AS_HELP_STRING([--enable-raw-link-api],[Enable raw link-layer API support @<:@default=yes@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_raw_link_api=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_raw_link_api} for --enable-raw-link-api])
+            ;;
+        esac
+    ],
+    [enable_raw_link_api=yes])
+
+if test "$enable_raw_link_api" = "yes"; then
+    OPENTHREAD_ENABLE_RAW_LINK_API=1
+else
+    OPENTHREAD_ENABLE_RAW_LINK_API=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_RAW_LINK_API)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_RAW_LINK_API], [test "${enable_raw_link_api}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_RAW_LINK_API],[${OPENTHREAD_ENABLE_RAW_LINK_API}],[Define to 1 if you want to enable raw link-layer API])
+
+#
+# Border Router
+#
+
+AC_ARG_ENABLE(border_router,
+    [AS_HELP_STRING([--enable-border-router],[Enable Border Router support @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            enable_border_router=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enable_border_router} for --enable-border-router])
+            ;;
+        esac
+    ],
+    [enable_border_router=no])
+
+if test "$enable_border_router" = "yes"; then
+    OPENTHREAD_ENABLE_BORDER_ROUTER=1
+else
+    OPENTHREAD_ENABLE_BORDER_ROUTER=0
+fi
+
+AC_SUBST(OPENTHREAD_ENABLE_BORDER_ROUTER)
+AM_CONDITIONAL([OPENTHREAD_ENABLE_BORDER_ROUTER], [test "${enable_border_router}" = "yes"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_BORDER_ROUTER],[${OPENTHREAD_ENABLE_BORDER_ROUTER}],[Define to 1 if you want to enable Border Router])
+
+#
+# Examples
+#
+
+AC_ARG_WITH(examples,
+    [AS_HELP_STRING([--with-examples=TARGET],
+        [Specify the examples from one of: none, posix, cc2538, cc2650, da15000, efr32, emsk, kw41z, nrf52840 @<:@default=none@:>@.])],
+    [
+        case "${with_examples}" in
+
+        none|posix|cc2538|cc2650|da15000|efr32|emsk|kw41z|nrf52840)
+            ;;
+        *)
+            AC_MSG_ERROR([Invalid value ${with_examples} for --with-examples])
+            ;;
+        esac
+    ],
+    [with_examples=none])
+
+OPENTHREAD_EXAMPLES=${with_examples}
+
+case ${with_examples} in
+
+    posix)
+        OPENTHREAD_EXAMPLES_POSIX=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_POSIX],[${OPENTHREAD_EXAMPLES_POSIX}],[Define to 1 if you want to use posix examples])
+        ;;
+
+    cc2538)
+        OPENTHREAD_EXAMPLES_CC2538=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_CC2538],[${OPENTHREAD_EXAMPLES_CC2538}],[Define to 1 if you want to use cc2538 examples])
+        ;;
+
+    cc2650)
+        OPENTHREAD_EXAMPLES_CC2650=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_CC2650],[${OPENTHREAD_EXAMPLES_CC2650}],[Define to 1 if you want to use cc2650 examples])
+        ;;
+
+    da15000)
+        OPENTHREAD_EXAMPLES_DA15000=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_DA15000],[${OPENTHREAD_EXAMPLES_DA15000}],[Define to 1 if you want to use da15000 examples])
+        ;;
+
+    efr32)
+        OPENTHREAD_EXAMPLES_EFR32=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_EFR32],[${OPENTHREAD_EXAMPLES_EFR32}],[Define to 1 if you want to use efr32 examples])
+        ;;
+
+     emsk)
+        OPENTHREAD_EXAMPLES_EMSK=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_EMSK],[${OPENTHREAD_EXAMPLES_EMSK}],[Define to 1 if you want to use emsk examples])
+        ;;
+
+    kw41z)
+        OPENTHREAD_EXAMPLES_KW41Z=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_KW41Z],[${OPENTHREAD_EXAMPLES_KW41Z}],[Define to 1 if you want to use kw41z examples])
+        ;;
+
+    nrf52840)
+        OPENTHREAD_EXAMPLES_NRF52840=1
+        AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES_NRF52840],[${OPENTHREAD_EXAMPLES_NRF52840}],[Define to 1 if you want to use nrf52840 examples])
+        ;;
+
+esac
+
+AC_MSG_CHECKING([whether to enable examples])
+AC_MSG_RESULT(${OPENTHREAD_EXAMPLES})
+
+AC_SUBST(OPENTHREAD_EXAMPLES)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES], [test ${with_examples} != "none"])
+AC_DEFINE_UNQUOTED([OPENTHREAD_EXAMPLES],[${OPENTHREAD_EXAMPLES}],[OpenThread examples])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_POSIX)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_POSIX], [test "${OPENTHREAD_EXAMPLES}" = "posix"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_CC2538)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_CC2538], [test "${OPENTHREAD_EXAMPLES}" = "cc2538"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_CC2650)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_CC2650], [test "${OPENTHREAD_EXAMPLES}" = "cc2650"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_DA15000)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_DA15000], [test "${OPENTHREAD_EXAMPLES}" = "da15000"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_EFR32)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_EFR32], [test "${OPENTHREAD_EXAMPLES}" = "efr32"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_EMSK)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_EMSK], [test "${OPENTHREAD_EXAMPLES}" = "emsk"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_KW41Z)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_KW41Z], [test "${OPENTHREAD_EXAMPLES}" = "kw41z"])
+
+AC_SUBST(OPENTHREAD_EXAMPLES_NRF52840)
+AM_CONDITIONAL([OPENTHREAD_EXAMPLES_NRF52840], [test "${OPENTHREAD_EXAMPLES}" = "nrf52840"])
+
+#
+# Platform Information
+#
+
+AC_ARG_WITH(platform-info,
+    [AS_HELP_STRING([--with-platform-info=PLATFORM],
+        [Specify the platform information @<:@default=none@:>@.])],
+    [],
+    [with_platform_info=none])
+
+PLATFORM_INFO=${with_platform_info}
+
+AC_MSG_CHECKING([with platform info])
+AC_MSG_RESULT(${PLATFORM_INFO})
+
+AC_SUBST(PLATFORM_INFO)
+AC_DEFINE_UNQUOTED([PLATFORM_INFO],["${PLATFORM_INFO}"],[OpenThread platform information])
+
+#
+# Tools
+#
+
+AC_MSG_CHECKING([whether to build tools])
+AC_ARG_ENABLE(tools,
+    [AS_HELP_STRING([--disable-tools],[Disable building of tools @<:@default=no@:>@.])],
+    [
+        case "${enableval}" in
+
+        no|yes)
+            build_tools=${enableval}
+            ;;
+
+        *)
+            AC_MSG_ERROR([Invalid value ${enableval} for --enable-tools])
+            ;;
+
+        esac
+    ],
+    [build_tools=yes])
+AC_MSG_RESULT(${build_tools})
+AM_CONDITIONAL([OPENTHREAD_BUILD_TOOLS], [test "${build_tools}" = "yes"])
+
+#
+# Documentation
+#
+
+# Determine whether or not documentation (via Doxygen) should be built
+# or not, with 'auto' as the default and establish a default support
+# value for GraphViz 'dot' support.
+
+NL_ENABLE_DOCS([auto],[NO])
+
+AM_CONDITIONAL(OPENTHREAD_BUILD_DOCS, [test "${nl_cv_build_docs}" = "yes"])
+
+#
+# Checks for libraries and packages.
+#
+# At minimum, the following packages are optional, depending on
+# configuration:
+#
+#   * TBD
+#
+AC_MSG_NOTICE([checking required package dependencies])
+
+# NL_WITH_PACKAGE(...)
+
+#
+# Check for headers
+#
+
+#---------------------------------------------------
+# Enable BSD Security Features
+# This enables strlcpy() and other friends in GNU land.
+# While the references below generally speak of: "glibc"
+# The ARM Embedded platform uses the nano instance of NEWLIB
+# Which greatly follows and mirrors glibc.
+# --------------------------------------------------
+#
+# References:
+# 1) http://stackoverflow.com/questions/29201515/what-does-d-default-source-do
+# 2) http://man7.org/linux/man-pages/man7/feature_test_macros.7.html
+#
+CFLAGS="${CFLAGS} -D_BSD_SOURCE=1 -D_DEFAULT_SOURCE=1"
+CXXFLAGS="${CXXFLAGS} -D_BSD_SOURCE=1 -D_DEFAULT_SOURCE=1"
+
+OLD_CFLAGS="${CFLAGS}"
+CFLAGS="${CFLAGS} -Wno-error=address"
+AC_HEADER_STDBOOL
+CFLAGS="${OLD_CFLAGS}"
+AC_HEADER_STDC
+
+AC_CHECK_HEADERS([stdint.h])
+AC_CHECK_HEADERS([string.h])
+
+#
+# Missing Functions
+#
+AC_CHECK_FUNC([strlcpy], [AC_DEFINE([HAVE_STRLCPY], [1], [Define if strlcpy exists.])])
+AC_CHECK_FUNC([strlcat], [AC_DEFINE([HAVE_STRLCAT], [1], [Define if strlcat exists.])])
+AC_CHECK_FUNC([strnlen], [AC_DEFINE([HAVE_STRNLEN], [1], [Define if strnlen exists.])])
+
+#
+# Check for types and structures
+#
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+
+#
+# Checks for library functions
+#
+
+if test "${ac_no_link}" != "yes"; then
+    AC_CHECK_FUNCS([memcpy])
+fi
+
+# Add any mbedtls CPPFLAGS
+
+CPPFLAGS="${CPPFLAGS} ${MBEDTLS_CPPFLAGS}"
+
+# Add any code coverage CPPFLAGS and LDFLAGS
+
+CPPFLAGS="${CPPFLAGS} ${NL_COVERAGE_CPPFLAGS}"
+LDFLAGS="${LDFLAGS} ${NL_COVERAGE_LDFLAGS}"
+
+# At this point, we can restore the compiler flags to whatever the
+# user passed in, now that we're clear of an -Werror issues by
+# transforming -Wno-error back to -Werror.
+
+NL_RESTORE_WERROR
+
+#
+# Identify the various makefiles and auto-generated files for the package
+#
+AC_CONFIG_FILES([
+Makefile
+include/Makefile
+include/openthread/Makefile
+include/openthread/platform/Makefile
+src/Makefile
+src/cli/Makefile
+src/ncp/Makefile
+src/core/Makefile
+src/diag/Makefile
+third_party/Makefile
+third_party/mbedtls/Makefile
+examples/Makefile
+examples/apps/Makefile
+examples/apps/cli/Makefile
+examples/apps/ncp/Makefile
+examples/platforms/Makefile
+examples/platforms/cc2538/Makefile
+examples/platforms/cc2650/Makefile
+examples/platforms/da15000/Makefile
+examples/platforms/efr32/Makefile
+examples/platforms/emsk/Makefile
+examples/platforms/kw41z/Makefile
+examples/platforms/nrf52840/Makefile
+examples/platforms/posix/Makefile
+examples/platforms/utils/Makefile
+tools/Makefile
+tools/harness-automation/Makefile
+tools/harness-thci/Makefile
+tools/spi-hdlc-adapter/Makefile
+tests/Makefile
+tests/scripts/Makefile
+tests/scripts/thread-cert/Makefile
+tests/unit/Makefile
+doc/Makefile
+])
+
+#
+# Generate the auto-generated files for the package
+#
+AC_OUTPUT
+
+#
+# Summarize the package configuration
+#
+
+AC_MSG_NOTICE([
+
+  Configuration Summary
+  ---------------------
+  Package                                   : ${PACKAGE_NAME}
+  Version                                   : ${PACKAGE_VERSION}
+  Interface                                 : ${LIBOPENTHREAD_VERSION_INFO//:/.}
+  Build system                              : ${build}
+  Host system                               : ${host}
+  Host architecture                         : ${host_cpu}
+  Host OS                                   : ${host_os}
+  Cross compiling                           : ${cross_compiling}
+  Build shared libraries                    : ${enable_shared}
+  Build static libraries                    : ${enable_static}
+  Build debug libraries                     : ${nl_cv_build_debug}
+  Build optimized libraries                 : ${nl_cv_build_optimized}
+  Build coverage libraries                  : ${nl_cv_build_coverage}
+  Build coverage reports                    : ${nl_cv_build_coverage_reports}
+  Address sanitizer support                 : ${enable_address_sanitizer}
+  Lcov                                      : ${LCOV:--}
+  Genhtml                                   : ${GENHTML:--}
+  Build tests                               : ${nl_cv_build_tests}
+  Build tools                               : ${build_tools}
+  OpenThread tests                          : ${with_tests}
+  Prefix                                    : ${prefix}
+  Documentation support                     : ${nl_cv_build_docs}
+  Doxygen                                   : ${DOXYGEN:--}
+  GraphViz dot                              : ${DOT:--}
+  C Preprocessor                            : ${CPP}
+  C Compiler                                : ${CC}
+  C++ Preprocessor                          : ${CXXCPP}
+  C++ Compiler                              : ${CXX}
+  Assembler Compiler                        : ${CCAS}
+  Archiver                                  : ${AR}
+  Archive Indexer                           : ${RANLIB}
+  Symbol Stripper                           : ${STRIP}
+  Object Copier                             : ${OBJCOPY}
+  C Preprocessor flags                      : ${CPPFLAGS:--}
+  C Compile flags                           : ${CFLAGS:--}
+  C++ Compile flags                         : ${CXXFLAGS:--}
+  Assembler flags                           : ${CCASFLAGS:--}
+  Link flags                                : ${LDFLAGS:--}
+  Link libraries                            : ${LIBS}
+  Pretty                                    : ${PRETTY:--}
+  Pretty args                               : ${PRETTY_ARGS:--}
+  Pretty check                              : ${PRETTY_CHECK:--}
+  Pretty check args                         : ${PRETTY_CHECK_ARGS:--}
+  OpenThread CLI support                    : ${enable_cli_app}
+  OpenThread CLI-MTD support                : ${enable_cli_app_mtd}
+  OpenThread CLI-FTD support                : ${enable_cli_app_ftd}
+  OpenThread NCP support                    : ${enable_ncp_app}
+  OpenThread NCP-MTD support                : ${enable_ncp_app_mtd}
+  OpenThread NCP-FTD support                : ${enable_ncp_app_ftd}
+  OpenThread NCP-BUS Configuration          : ${with_ncp_bus}
+  OpenThread Multiple Instances support     : ${enable_multiple_instances}
+  OpenThread MTD Network Diagnostic support : ${enable_mtd_network_diagnostic}
+  OpenThread builtin mbedtls support        : ${enable_builtin_mbedtls}
+  OpenThread TMF Proxy support              : ${enable_tmf_proxy}
+  OpenThread Commissioner support           : ${enable_commissioner}
+  OpenThread Joiner support                 : ${enable_joiner}
+  OpenThread DTLS support                   : ${enable_dtls}
+  OpenThread Jam Detection support          : ${enable_jam_detection}
+  OpenThread MAC Whitelist support          : ${enable_mac_whitelist}
+  OpenThread Diagnostics support            : ${enable_diag}
+  OpenThread Child Supervision support      : ${enable_child_supervision}
+  OpenThread Legacy network support         : ${enable_legacy}
+  OpenThread Certification log support      : ${enable_cert_log}
+  OpenThread DHCPv6 Server support          : ${enable_dhcp6_server}
+  OpenThread DHCPv6 Client support          : ${enable_dhcp6_client}
+  OpenThread DNS Client support             : ${enable_dns_client}
+  OpenThread Application CoAP support       : ${enable_application_coap}
+  OpenThread Raw Link-Layer support         : ${enable_raw_link_api}
+  OpenThread Border Router support          : ${enable_border_router}
+  OpenThread examples                       : ${OPENTHREAD_EXAMPLES}
+  OpenThread platform information           : ${PLATFORM_INFO}
+
+])
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
new file mode 100644
index 0000000..e8e7261
--- /dev/null
+++ b/doc/Doxyfile.in
@@ -0,0 +1,2384 @@
+# Doxyfile 1.8.6
+
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+#    Description:
+#      This file describes the settings to be used by the
+#      documentation system # doxygen (www.doxygen.org) for OpenThread.
+#
+#      This was initially autogenerated 'doxywizard' and then hand-tuned.
+#
+#      All text after a hash (#) is considered a comment and will be
+#      ignored.
+#
+#      The format is:
+#
+#          TAG = value [value, ...]
+#
+#      For lists items can also be appended using:
+#
+#          TAG += value [value, ...]
+#
+#      Values that contain spaces should be placed between quotes (" ")
+#
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = OpenThread
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           = @abs_srcdir@/images/Open-Thread-Logo-200x42.png
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @abs_builddir@
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = YES
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = @abs_top_srcdir@ \
+                         @abs_top_builddir@
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    = @abs_top_srcdir@
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = @abs_top_builddir@/src \
+                         @abs_top_srcdir@/include \
+                         @abs_top_srcdir@/doc \
+                         @abs_top_srcdir@/README.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.for \
+                         *.vhd \
+                         *.vhdl
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                = ../src/ncp/ncp.pb-c.h \
+                         ../src/ncp/ncp.pb-c.c
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             = @abs_srcdir@/images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            = @abs_srcdir@/header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 1
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           = @abs_top_srcdir@/include
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = __attribute__(x)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = @DOXYGEN_USE_DOT@
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = YES
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..3272f64
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,114 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+EXTRA_DIST                                      = \
+    $(srcdir)/Doxyfile.in                         \
+    $(srcdir)/header.html                         \
+    $(srcdir)/images/Open-Thread-Logo-200x42.png  \
+    $(srcdir)/images/openthread_contrib.png       \
+    $(srcdir)/images/openthread_logo.png          \
+    draft-rquattle-spinel-unified.html            \
+    draft-rquattle-spinel-unified.txt             \
+    $(NULL)
+
+#
+# Override autotool's default notion of the package version variables.
+# This ensures that when we create a doc distribution that the
+# version is always the current version, not the version when the
+# package was bootstrapped.
+#
+PACKAGE_VERSION                                 = $(shell cat $(top_builddir)/.local-version)
+VERSION                                         = $(PACKAGE_VERSION)
+
+
+docdistdir                                     ?= .
+
+openthread_docdist_alias                        = \
+    $(PACKAGE_TARNAME)-docs
+
+openthread_docdist_name                         = \
+    $(openthread_docdist_alias)-$(VERSION)
+
+openthread_docdist_archive                      = \
+    $(docdistdir)/$(openthread_docdist_name).tar.gz
+
+CLEANFILES                                      = \
+    Doxyfile                                      \
+    $(openthread_docdist_archive)                 \
+    $(NULL)
+
+if OPENTHREAD_BUILD_DOCS
+
+all-local: html/index.html
+
+#
+# We choose to manually transform Doxyfile.in into Doxyfile here in
+# the makefile rather than in the configure script so that we can take
+# advantage of live, at build time (rather than at configure time),
+# updating of the package version number.
+#
+
+Doxyfile: $(srcdir)/Doxyfile.in Makefile
+	$(AM_V_GEN)$(SED)                                     \
+	    -e "s,\@DOXYGEN_USE_DOT\@,$(DOXYGEN_USE_DOT),g"   \
+	    -e "s,\@PACKAGE_NAME\@,$(PACKAGE_NAME),g"         \
+	    -e "s,\@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g"   \
+	    -e "s,\@PERL\@,$(PERL),g"                         \
+	    -e "s,\@abs_builddir\@,$(abs_builddir),g"         \
+	    -e "s,\@abs_srcdir\@,$(abs_srcdir),g"             \
+	    -e "s,\@abs_top_builddir\@,$(abs_top_builddir),g" \
+	    -e "s,\@abs_top_srcdir\@,$(abs_top_srcdir),g"     \
+	    < "$(srcdir)/Doxyfile.in" > "$(@)"
+
+html/index.html: Doxyfile
+	$(AM_V_GEN)$(DOXYGEN) $(<)
+
+#
+# Addition rules and commands to create a documentation-only
+# distribution of openthread
+#
+
+$(openthread_docdist_name): html/index.html
+	$(AM_V_at)rm -f -r $(@)
+	$(call create-directory)
+	$(AM_V_at)cp -R html $(@)
+
+$(openthread_docdist_archive): $(openthread_docdist_name)
+	$(AM_V_at)echo "  TAR      $(@)"
+	$(AM_V_at)tardir="$(<)" && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c > "$(@)" && rm -rf $(<)
+
+docdist $(openthread_docdist_alias): $(openthread_docdist_archive)
+
+clean-local:
+	$(AM_V_at)rm -f -r html
+
+endif # OPENTHREAD_BUILD_DOCS
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/doc/draft-rquattle-spinel-unified.html b/doc/draft-rquattle-spinel-unified.html
new file mode 100644
index 0000000..1c4d49a
--- /dev/null
+++ b/doc/draft-rquattle-spinel-unified.html
@@ -0,0 +1,4925 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head profile="http://www.w3.org/2006/03/hcard http://dublincore.org/documents/2008/08/04/dc-html/">
+  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
+
+  <title>Spinel Host-Controller Protocol</title>
+
+  <style type="text/css" title="Xml2Rfc (sans serif)">
+  /*<![CDATA[*/
+	  a {
+	  text-decoration: none;
+	  }
+      /* info code from SantaKlauss at http://www.madaboutstyle.com/tooltip2.html */
+      a.info {
+          /* This is the key. */
+          position: relative;
+          z-index: 24;
+          text-decoration: none;
+      }
+      a.info:hover {
+          z-index: 25;
+          color: #FFF; background-color: #900;
+      }
+      a.info span { display: none; }
+      a.info:hover span.info {
+          /* The span will display just on :hover state. */
+          display: block;
+          position: absolute;
+          font-size: smaller;
+          top: 2em; left: -5em; width: 15em;
+          padding: 2px; border: 1px solid #333;
+          color: #900; background-color: #EEE;
+          text-align: left;
+      }
+	  a.smpl {
+	  color: black;
+	  }
+	  a:hover {
+	  text-decoration: underline;
+	  }
+	  a:active {
+	  text-decoration: underline;
+	  }
+	  address {
+	  margin-top: 1em;
+	  margin-left: 2em;
+	  font-style: normal;
+	  }
+	  body {
+	  color: black;
+	  font-family: verdana, helvetica, arial, sans-serif;
+	  font-size: 10pt;
+	  max-width: 55em;
+	  
+	  }
+	  cite {
+	  font-style: normal;
+	  }
+	  dd {
+	  margin-right: 2em;
+	  }
+	  dl {
+	  margin-left: 2em;
+	  }
+	
+	  ul.empty {
+	  list-style-type: none;
+	  }
+	  ul.empty li {
+	  margin-top: .5em;
+	  }
+	  dl p {
+	  margin-left: 0em;
+	  }
+	  dt {
+	  margin-top: .5em;
+	  }
+	  h1 {
+	  font-size: 14pt;
+	  line-height: 21pt;
+	  page-break-after: avoid;
+	  }
+	  h1.np {
+	  page-break-before: always;
+	  }
+	  h1 a {
+	  color: #333333;
+	  }
+	  h2 {
+	  font-size: 12pt;
+	  line-height: 15pt;
+	  page-break-after: avoid;
+	  }
+	  h3, h4, h5, h6 {
+	  font-size: 10pt;
+	  page-break-after: avoid;
+	  }
+	  h2 a, h3 a, h4 a, h5 a, h6 a {
+	  color: black;
+	  }
+	  img {
+	  margin-left: 3em;
+	  }
+	  li {
+	  margin-left: 2em;
+	  margin-right: 2em;
+	  }
+	  ol {
+	  margin-left: 2em;
+	  margin-right: 2em;
+	  }
+	  ol p {
+	  margin-left: 0em;
+	  }
+	  p {
+	  margin-left: 2em;
+	  margin-right: 2em;
+	  }
+	  pre {
+	  margin-left: 3em;
+	  background-color: lightyellow;
+	  padding: .25em;
+	  }
+	  pre.text2 {
+	  border-style: dotted;
+	  border-width: 1px;
+	  background-color: #f0f0f0;
+	  width: 69em;
+	  }
+	  pre.inline {
+	  background-color: white;
+	  padding: 0em;
+	  }
+	  pre.text {
+	  border-style: dotted;
+	  border-width: 1px;
+	  background-color: #f8f8f8;
+	  width: 69em;
+	  }
+	  pre.drawing {
+	  border-style: solid;
+	  border-width: 1px;
+	  background-color: #f8f8f8;
+	  padding: 2em;
+	  }
+	  table {
+	  margin-left: 2em;
+	  }
+	  table.tt {
+	  vertical-align: top;
+	  }
+	  table.full {
+	  border-style: outset;
+	  border-width: 1px;
+	  }
+	  table.headers {
+	  border-style: outset;
+	  border-width: 1px;
+	  }
+	  table.tt td {
+	  vertical-align: top;
+	  }
+	  table.full td {
+	  border-style: inset;
+	  border-width: 1px;
+	  }
+	  table.tt th {
+	  vertical-align: top;
+	  }
+	  table.full th {
+	  border-style: inset;
+	  border-width: 1px;
+	  }
+	  table.headers th {
+	  border-style: none none inset none;
+	  border-width: 1px;
+	  }
+	  table.left {
+	  margin-right: auto;
+	  }
+	  table.right {
+	  margin-left: auto;
+	  }
+	  table.center {
+	  margin-left: auto;
+	  margin-right: auto;
+	  }
+	  caption {
+	  caption-side: bottom;
+	  font-weight: bold;
+	  font-size: 9pt;
+	  margin-top: .5em;
+	  }
+	
+	  table.header {
+	  border-spacing: 1px;
+	  width: 95%;
+	  font-size: 10pt;
+	  color: white;
+	  }
+	  td.top {
+	  vertical-align: top;
+	  }
+	  td.topnowrap {
+	  vertical-align: top;
+	  white-space: nowrap; 
+	  }
+	  table.header td {
+	  background-color: gray;
+	  width: 50%;
+	  }
+	  table.header a {
+	  color: white;
+	  }
+	  td.reference {
+	  vertical-align: top;
+	  white-space: nowrap;
+	  padding-right: 1em;
+	  }
+	  thead {
+	  display:table-header-group;
+	  }
+	  ul.toc, ul.toc ul {
+	  list-style: none;
+	  margin-left: 1.5em;
+	  margin-right: 0em;
+	  padding-left: 0em;
+	  }
+	  ul.toc li {
+	  line-height: 150%;
+	  font-weight: bold;
+	  font-size: 10pt;
+	  margin-left: 0em;
+	  margin-right: 0em;
+	  }
+	  ul.toc li li {
+	  line-height: normal;
+	  font-weight: normal;
+	  font-size: 9pt;
+	  margin-left: 0em;
+	  margin-right: 0em;
+	  }
+	  li.excluded {
+	  font-size: 0pt;
+	  }
+	  ul p {
+	  margin-left: 0em;
+	  }
+	
+	  .comment {
+	  background-color: yellow;
+	  }
+	  .center {
+	  text-align: center;
+	  }
+	  .error {
+	  color: red;
+	  font-style: italic;
+	  font-weight: bold;
+	  }
+	  .figure {
+	  font-weight: bold;
+	  text-align: center;
+	  font-size: 9pt;
+	  }
+	  .filename {
+	  color: #333333;
+	  font-weight: bold;
+	  font-size: 12pt;
+	  line-height: 21pt;
+	  text-align: center;
+	  }
+	  .fn {
+	  font-weight: bold;
+	  }
+	  .hidden {
+	  display: none;
+	  }
+	  .left {
+	  text-align: left;
+	  }
+	  .right {
+	  text-align: right;
+	  }
+	  .title {
+	  color: #990000;
+	  font-size: 18pt;
+	  line-height: 18pt;
+	  font-weight: bold;
+	  text-align: center;
+	  margin-top: 36pt;
+	  }
+	  .vcardline {
+	  display: block;
+	  }
+	  .warning {
+	  font-size: 14pt;
+	  background-color: yellow;
+	  }
+	
+	
+	  @media print {
+	  .noprint {
+		display: none;
+	  }
+	
+	  a {
+		color: black;
+		text-decoration: none;
+	  }
+	
+	  table.header {
+		width: 90%;
+	  }
+	
+	  td.header {
+		width: 50%;
+		color: black;
+		background-color: white;
+		vertical-align: top;
+		font-size: 12pt;
+	  }
+	
+	  ul.toc a::after {
+		content: leader('.') target-counter(attr(href), page);
+	  }
+	
+	  ul.ind li li a {
+		content: target-counter(attr(href), page);
+	  }
+	
+	  .print2col {
+		column-count: 2;
+		-moz-column-count: 2;
+		column-fill: auto;
+	  }
+	  }
+	
+	  @page {
+	  @top-left {
+		   content: "Internet-Draft"; 
+	  } 
+	  @top-right {
+		   content: "December 2010"; 
+	  } 
+	  @top-center {
+		   content: "Abbreviated Title";
+	  } 
+	  @bottom-left {
+		   content: "Doe"; 
+	  } 
+	  @bottom-center {
+		   content: "Expires June 2011"; 
+	  } 
+	  @bottom-right {
+		   content: "[Page " counter(page) "]"; 
+	  } 
+	  }
+	
+	  @page:first { 
+		@top-left {
+		  content: normal;
+		}
+		@top-right {
+		  content: normal;
+		}
+		@top-center {
+		  content: normal;
+		}
+	  }
+  /*]]>*/
+  </style>
+
+  <link href="#rfc.toc" rel="Contents"/>
+<link href="#rfc.section.1" rel="Chapter" title="1 Introduction"/>
+<link href="#rfc.section.1.1" rel="Chapter" title="1.1 About this Draft"/>
+<link href="#rfc.section.1.1.1" rel="Chapter" title="1.1.1 Scope"/>
+<link href="#rfc.section.1.1.2" rel="Chapter" title="1.1.2 Renumbering"/>
+<link href="#rfc.section.2" rel="Chapter" title="2 Frame Format"/>
+<link href="#rfc.section.2.1" rel="Chapter" title="2.1 Header Format"/>
+<link href="#rfc.section.2.1.1" rel="Chapter" title="2.1.1 FLG: Flag"/>
+<link href="#rfc.section.2.1.2" rel="Chapter" title="2.1.2 NLI: Network Link Identifier"/>
+<link href="#rfc.section.2.1.3" rel="Chapter" title="2.1.3 TID: Transaction Identifier"/>
+<link href="#rfc.section.2.1.4" rel="Chapter" title="2.1.4 Command Identifier (CMD)"/>
+<link href="#rfc.section.2.1.5" rel="Chapter" title="2.1.5 Command Payload (Optional)"/>
+<link href="#rfc.section.3" rel="Chapter" title="3 Data Packing"/>
+<link href="#rfc.section.3.1" rel="Chapter" title="3.1 Primitive Types"/>
+<link href="#rfc.section.3.2" rel="Chapter" title="3.2 Packed Unsigned Integer"/>
+<link href="#rfc.section.3.3" rel="Chapter" title="3.3 Data Blobs"/>
+<link href="#rfc.section.3.4" rel="Chapter" title="3.4 Structured Data"/>
+<link href="#rfc.section.3.5" rel="Chapter" title="3.5 Arrays"/>
+<link href="#rfc.section.4" rel="Chapter" title="4 Commands"/>
+<link href="#rfc.section.4.1" rel="Chapter" title="4.1 CMD 0: (Host-&gt;NCP) CMD_NOOP"/>
+<link href="#rfc.section.4.2" rel="Chapter" title="4.2 CMD 1: (Host-&gt;NCP) CMD_RESET"/>
+<link href="#rfc.section.4.3" rel="Chapter" title="4.3 CMD 2: (Host-&gt;NCP) CMD_PROP_VALUE_GET"/>
+<link href="#rfc.section.4.4" rel="Chapter" title="4.4 CMD 3: (Host-&gt;NCP) CMD_PROP_VALUE_SET"/>
+<link href="#rfc.section.4.5" rel="Chapter" title="4.5 CMD 4: (Host-&gt;NCP) CMD_PROP_VALUE_INSERT"/>
+<link href="#rfc.section.4.6" rel="Chapter" title="4.6 CMD 5: (Host-&gt;NCP) CMD_PROP_VALUE_REMOVE"/>
+<link href="#rfc.section.4.7" rel="Chapter" title="4.7 CMD 6: (NCP-&gt;Host) CMD_PROP_VALUE_IS"/>
+<link href="#rfc.section.4.8" rel="Chapter" title="4.8 CMD 7: (NCP-&gt;Host) CMD_PROP_VALUE_INSERTED"/>
+<link href="#rfc.section.4.9" rel="Chapter" title="4.9 CMD 8: (NCP-&gt;Host) CMD_PROP_VALUE_REMOVED"/>
+<link href="#rfc.section.4.10" rel="Chapter" title="4.10 CMD 18: (Host-&gt;NCP) CMD_PEEK"/>
+<link href="#rfc.section.4.11" rel="Chapter" title="4.11 CMD 19: (NCP-&gt;Host) CMD_PEEK_RET"/>
+<link href="#rfc.section.4.12" rel="Chapter" title="4.12 CMD 20: (Host-&gt;NCP) CMD_POKE"/>
+<link href="#rfc.section.4.13" rel="Chapter" title="4.13 CMD 21: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_GET"/>
+<link href="#rfc.section.4.14" rel="Chapter" title="4.14 CMD 22: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_SET"/>
+<link href="#rfc.section.4.15" rel="Chapter" title="4.15 CMD 23: (NCP-&gt;Host) CMD_PROP_VALUES_ARE"/>
+<link href="#rfc.section.5" rel="Chapter" title="5 Properties"/>
+<link href="#rfc.section.5.1" rel="Chapter" title="5.1 Property Methods"/>
+<link href="#rfc.section.5.2" rel="Chapter" title="5.2 Property Types"/>
+<link href="#rfc.section.5.2.1" rel="Chapter" title="5.2.1 Single-Value Properties"/>
+<link href="#rfc.section.5.2.2" rel="Chapter" title="5.2.2 Multiple-Value Properties"/>
+<link href="#rfc.section.5.2.3" rel="Chapter" title="5.2.3 Stream Properties"/>
+<link href="#rfc.section.5.3" rel="Chapter" title="5.3 Property Numbering"/>
+<link href="#rfc.section.5.4" rel="Chapter" title="5.4 Property Sections"/>
+<link href="#rfc.section.5.5" rel="Chapter" title="5.5 Core Properties"/>
+<link href="#rfc.section.5.5.1" rel="Chapter" title="5.5.1 PROP 0: PROP_LAST_STATUS"/>
+<link href="#rfc.section.5.5.2" rel="Chapter" title="5.5.2 PROP 1: PROP_PROTOCOL_VERSION"/>
+<link href="#rfc.section.5.5.3" rel="Chapter" title="5.5.3 PROP 2: PROP_NCP_VERSION"/>
+<link href="#rfc.section.5.5.4" rel="Chapter" title="5.5.4 PROP 3: PROP_INTERFACE_TYPE"/>
+<link href="#rfc.section.5.5.5" rel="Chapter" title="5.5.5 PROP 4: PROP_INTERFACE_VENDOR_ID"/>
+<link href="#rfc.section.5.5.6" rel="Chapter" title="5.5.6 PROP 5: PROP_CAPS"/>
+<link href="#rfc.section.5.5.7" rel="Chapter" title="5.5.7 PROP 6: PROP_INTERFACE_COUNT"/>
+<link href="#rfc.section.5.5.8" rel="Chapter" title="5.5.8 PROP 7: PROP_POWER_STATE"/>
+<link href="#rfc.section.5.5.9" rel="Chapter" title="5.5.9 PROP 8: PROP_HWADDR"/>
+<link href="#rfc.section.5.5.10" rel="Chapter" title="5.5.10 PROP 9: PROP_LOCK"/>
+<link href="#rfc.section.5.5.11" rel="Chapter" title="5.5.11 PROP 10: PROP_HOST_POWER_STATE"/>
+<link href="#rfc.section.5.5.12" rel="Chapter" title="5.5.12 PROP 4104: PROP_UNSOL_UPDATE_FILTER"/>
+<link href="#rfc.section.5.5.13" rel="Chapter" title="5.5.13 PROP 4105: PROP_UNSOL_UPDATE_LIST"/>
+<link href="#rfc.section.5.6" rel="Chapter" title="5.6 Stream Properties"/>
+<link href="#rfc.section.5.6.1" rel="Chapter" title="5.6.1 PROP 112: PROP_STREAM_DEBUG"/>
+<link href="#rfc.section.5.6.2" rel="Chapter" title="5.6.2 PROP 113: PROP_STREAM_RAW"/>
+<link href="#rfc.section.5.6.3" rel="Chapter" title="5.6.3 PROP 114: PROP_STREAM_NET"/>
+<link href="#rfc.section.5.6.4" rel="Chapter" title="5.6.4 PROP 115: PROP_STREAM_NET_INSECURE"/>
+<link href="#rfc.section.5.7" rel="Chapter" title="5.7 PHY Properties"/>
+<link href="#rfc.section.5.7.1" rel="Chapter" title="5.7.1 PROP 32: PROP_PHY_ENABLED"/>
+<link href="#rfc.section.5.7.2" rel="Chapter" title="5.7.2 PROP 33: PROP_PHY_CHAN"/>
+<link href="#rfc.section.5.7.3" rel="Chapter" title="5.7.3 PROP 34: PROP_PHY_CHAN_SUPPORTED"/>
+<link href="#rfc.section.5.7.4" rel="Chapter" title="5.7.4 PROP 35: PROP_PHY_FREQ"/>
+<link href="#rfc.section.5.7.5" rel="Chapter" title="5.7.5 PROP 36: PROP_PHY_CCA_THRESHOLD"/>
+<link href="#rfc.section.5.7.6" rel="Chapter" title="5.7.6 PROP 37: PROP_PHY_TX_POWER"/>
+<link href="#rfc.section.5.7.7" rel="Chapter" title="5.7.7 PROP 38: PROP_PHY_RSSI"/>
+<link href="#rfc.section.5.7.8" rel="Chapter" title="5.7.8 PROP 39: PROP_PHY_RX_SENSITIVITY"/>
+<link href="#rfc.section.5.8" rel="Chapter" title="5.8 MAC Properties"/>
+<link href="#rfc.section.5.8.1" rel="Chapter" title="5.8.1 PROP 48: PROP_MAC_SCAN_STATE"/>
+<link href="#rfc.section.5.8.2" rel="Chapter" title="5.8.2 PROP 49: PROP_MAC_SCAN_MASK"/>
+<link href="#rfc.section.5.8.3" rel="Chapter" title="5.8.3 PROP 50: PROP_MAC_SCAN_PERIOD"/>
+<link href="#rfc.section.5.8.4" rel="Chapter" title="5.8.4 PROP 51: PROP_MAC_SCAN_BEACON"/>
+<link href="#rfc.section.5.8.5" rel="Chapter" title="5.8.5 PROP 52: PROP_MAC_15_4_LADDR"/>
+<link href="#rfc.section.5.8.6" rel="Chapter" title="5.8.6 PROP 53: PROP_MAC_15_4_SADDR"/>
+<link href="#rfc.section.5.8.7" rel="Chapter" title="5.8.7 PROP 54: PROP_MAC_15_4_PANID"/>
+<link href="#rfc.section.5.8.8" rel="Chapter" title="5.8.8 PROP 55: PROP_MAC_RAW_STREAM_ENABLED"/>
+<link href="#rfc.section.5.8.9" rel="Chapter" title="5.8.9 PROP 56: PROP_MAC_PROMISCUOUS_MODE"/>
+<link href="#rfc.section.5.8.10" rel="Chapter" title="5.8.10 PROP 57: PROP_MAC_ENERGY_SCAN_RESULT"/>
+<link href="#rfc.section.5.8.11" rel="Chapter" title="5.8.11 PROP 4864: PROP_MAC_WHITELIST"/>
+<link href="#rfc.section.5.8.12" rel="Chapter" title="5.8.12 PROP 4865: PROP_MAC_WHITELIST_ENABLED"/>
+<link href="#rfc.section.5.8.13" rel="Chapter" title="5.8.13 PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED"/>
+<link href="#rfc.section.5.8.14" rel="Chapter" title="5.8.14 PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES"/>
+<link href="#rfc.section.5.8.15" rel="Chapter" title="5.8.15 PROP 4869: SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES"/>
+<link href="#rfc.section.5.8.16" rel="Chapter" title="5.8.16 PROP 4870: PROP_MAC_BLACKLIST"/>
+<link href="#rfc.section.5.8.17" rel="Chapter" title="5.8.17 PROP 4871: PROP_MAC_BLACKLIST_ENABLED"/>
+<link href="#rfc.section.5.9" rel="Chapter" title="5.9 NET Properties"/>
+<link href="#rfc.section.5.9.1" rel="Chapter" title="5.9.1 PROP 64: PROP_NET_SAVED"/>
+<link href="#rfc.section.5.9.2" rel="Chapter" title="5.9.2 PROP 65: PROP_NET_IF_UP"/>
+<link href="#rfc.section.5.9.3" rel="Chapter" title="5.9.3 PROP 66: PROP_NET_STACK_UP"/>
+<link href="#rfc.section.5.9.4" rel="Chapter" title="5.9.4 PROP 67: PROP_NET_ROLE"/>
+<link href="#rfc.section.5.9.5" rel="Chapter" title="5.9.5 PROP 68: PROP_NET_NETWORK_NAME"/>
+<link href="#rfc.section.5.9.6" rel="Chapter" title="5.9.6 PROP 69: PROP_NET_XPANID"/>
+<link href="#rfc.section.5.9.7" rel="Chapter" title="5.9.7 PROP 70: PROP_NET_MASTER_KEY"/>
+<link href="#rfc.section.5.9.8" rel="Chapter" title="5.9.8 PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER"/>
+<link href="#rfc.section.5.9.9" rel="Chapter" title="5.9.9 PROP 72: PROP_NET_PARTITION_ID"/>
+<link href="#rfc.section.5.9.10" rel="Chapter" title="5.9.10 PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING"/>
+<link href="#rfc.section.5.9.11" rel="Chapter" title="5.9.11 PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME"/>
+<link href="#rfc.section.5.9.12" rel="Chapter" title="5.9.12 PROP 75: PROP_NET_PSKC"/>
+<link href="#rfc.section.5.10" rel="Chapter" title="5.10 IPv6 Properties"/>
+<link href="#rfc.section.5.10.1" rel="Chapter" title="5.10.1 PROP 96: PROP_IPV6_LL_ADDR"/>
+<link href="#rfc.section.5.10.2" rel="Chapter" title="5.10.2 PROP 97: PROP_IPV6_ML_ADDR"/>
+<link href="#rfc.section.5.10.3" rel="Chapter" title="5.10.3 PROP 98: PROP_IPV6_ML_PREFIX"/>
+<link href="#rfc.section.5.10.4" rel="Chapter" title="5.10.4 PROP 99: PROP_IPV6_ADDRESS_TABLE"/>
+<link href="#rfc.section.5.10.5" rel="Chapter" title="5.10.5 PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD"/>
+<link href="#rfc.section.5.11" rel="Chapter" title="5.11 Debug Properties"/>
+<link href="#rfc.section.5.11.1" rel="Chapter" title="5.11.1 PROP 16384: PROP_DEBUG_TEST_ASSERT"/>
+<link href="#rfc.section.5.11.2" rel="Chapter" title="5.11.2 PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL"/>
+<link href="#rfc.section.6" rel="Chapter" title="6 Status Codes"/>
+<link href="#rfc.section.7" rel="Chapter" title="7 Technology: Thread(R)"/>
+<link href="#rfc.section.7.1" rel="Chapter" title="7.1 Capabilities"/>
+<link href="#rfc.section.7.2" rel="Chapter" title="7.2 Properties"/>
+<link href="#rfc.section.7.2.1" rel="Chapter" title="7.2.1 PROP 80: PROP_THREAD_LEADER_ADDR"/>
+<link href="#rfc.section.7.2.2" rel="Chapter" title="7.2.2 PROP 81: PROP_THREAD_PARENT"/>
+<link href="#rfc.section.7.2.3" rel="Chapter" title="7.2.3 PROP 82: PROP_THREAD_CHILD_TABLE"/>
+<link href="#rfc.section.7.2.4" rel="Chapter" title="7.2.4 PROP 83: PROP_THREAD_LEADER_RID"/>
+<link href="#rfc.section.7.2.5" rel="Chapter" title="7.2.5 PROP 84: PROP_THREAD_LEADER_WEIGHT"/>
+<link href="#rfc.section.7.2.6" rel="Chapter" title="7.2.6 PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT"/>
+<link href="#rfc.section.7.2.7" rel="Chapter" title="7.2.7 PROP 86: PROP_THREAD_NETWORK_DATA"/>
+<link href="#rfc.section.7.2.8" rel="Chapter" title="7.2.8 PROP 87: PROP_THREAD_NETWORK_DATA_VERSION"/>
+<link href="#rfc.section.7.2.9" rel="Chapter" title="7.2.9 PROP 88: PROP_THREAD_STABLE_NETWORK_DATA"/>
+<link href="#rfc.section.7.2.10" rel="Chapter" title="7.2.10 PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION"/>
+<link href="#rfc.section.7.2.11" rel="Chapter" title="7.2.11 PROP 90: PROP_THREAD_ON_MESH_NETS"/>
+<link href="#rfc.section.7.2.12" rel="Chapter" title="7.2.12 PROP 91: PROP_THREAD_OFF_MESH_ROUTES"/>
+<link href="#rfc.section.7.2.13" rel="Chapter" title="7.2.13 PROP 92: PROP_THREAD_ASSISTING_PORTS"/>
+<link href="#rfc.section.7.2.14" rel="Chapter" title="7.2.14 PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE"/>
+<link href="#rfc.section.7.2.15" rel="Chapter" title="7.2.15 PROP 94: PROP_THREAD_MODE"/>
+<link href="#rfc.section.7.2.16" rel="Chapter" title="7.2.16 PROP 5376: PROP_THREAD_CHILD_TIMEOUT"/>
+<link href="#rfc.section.7.2.17" rel="Chapter" title="7.2.17 PROP 5377: PROP_THREAD_RLOC16"/>
+<link href="#rfc.section.7.2.18" rel="Chapter" title="7.2.18 PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD"/>
+<link href="#rfc.section.7.2.19" rel="Chapter" title="7.2.19 PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY"/>
+<link href="#rfc.section.7.2.20" rel="Chapter" title="7.2.20 PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT"/>
+<link href="#rfc.section.7.2.21" rel="Chapter" title="7.2.21 PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS"/>
+<link href="#rfc.section.7.2.22" rel="Chapter" title="7.2.22 PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU"/>
+<link href="#rfc.section.7.2.23" rel="Chapter" title="7.2.23 PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED"/>
+<link href="#rfc.section.7.2.24" rel="Chapter" title="7.2.24 PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD"/>
+<link href="#rfc.section.7.2.25" rel="Chapter" title="7.2.25 PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER"/>
+<link href="#rfc.section.7.2.26" rel="Chapter" title="7.2.26 PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID"/>
+<link href="#rfc.section.7.2.27" rel="Chapter" title="7.2.27 PROP 5387: PROP_THREAD_NEIGHBOR_TABLE"/>
+<link href="#rfc.section.7.2.28" rel="Chapter" title="7.2.28 PROP 5388: PROP_THREAD_CHILD_COUNT_MAX"/>
+<link href="#rfc.section.7.2.29" rel="Chapter" title="7.2.29 PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA"/>
+<link href="#rfc.section.7.2.30" rel="Chapter" title="7.2.30 PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA"/>
+<link href="#rfc.section.7.2.31" rel="Chapter" title="7.2.31 PROP 5391: PROP_THREAD_JOINERS"/>
+<link href="#rfc.section.7.2.32" rel="Chapter" title="7.2.32 PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED"/>
+<link href="#rfc.section.7.2.33" rel="Chapter" title="7.2.33 PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED"/>
+<link href="#rfc.section.7.2.34" rel="Chapter" title="7.2.34 PROP 5394: PROP_THREAD_TMF_PROXY_STREAM"/>
+<link href="#rfc.section.7.2.35" rel="Chapter" title="7.2.35 PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG"/>
+<link href="#rfc.section.7.2.36" rel="Chapter" title="7.2.36 PROP 5396: PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING"/>
+<link href="#rfc.section.7.2.37" rel="Chapter" title="7.2.37 PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID"/>
+<link href="#rfc.section.7.2.38" rel="Chapter" title="7.2.38 PROP 5398: PROP_THREAD_STEERING_DATA"/>
+<link href="#rfc.section.8" rel="Chapter" title="8 Feature: Network Save"/>
+<link href="#rfc.section.8.1" rel="Chapter" title="8.1 Commands"/>
+<link href="#rfc.section.8.1.1" rel="Chapter" title="8.1.1 CMD 9: (Host-&gt;NCP) CMD_NET_SAVE"/>
+<link href="#rfc.section.8.1.2" rel="Chapter" title="8.1.2 CMD 10: (Host-&gt;NCP) CMD_NET_CLEAR"/>
+<link href="#rfc.section.8.1.3" rel="Chapter" title="8.1.3 CMD 11: (Host-&gt;NCP) CMD_NET_RECALL"/>
+<link href="#rfc.section.9" rel="Chapter" title="9 Feature: Host Buffer Offload"/>
+<link href="#rfc.section.9.1" rel="Chapter" title="9.1 Commands"/>
+<link href="#rfc.section.9.1.1" rel="Chapter" title="9.1.1 CMD 12: (NCP-&gt;Host) CMD_HBO_OFFLOAD"/>
+<link href="#rfc.section.9.1.2" rel="Chapter" title="9.1.2 CMD 13: (NCP-&gt;Host) CMD_HBO_RECLAIM"/>
+<link href="#rfc.section.9.1.3" rel="Chapter" title="9.1.3 CMD 14: (NCP-&gt;Host) CMD_HBO_DROP"/>
+<link href="#rfc.section.9.1.4" rel="Chapter" title="9.1.4 CMD 15: (Host-&gt;NCP) CMD_HBO_OFFLOADED"/>
+<link href="#rfc.section.9.1.5" rel="Chapter" title="9.1.5 CMD 16: (Host-&gt;NCP) CMD_HBO_RECLAIMED"/>
+<link href="#rfc.section.9.1.6" rel="Chapter" title="9.1.6 CMD 17: (Host-&gt;NCP) CMD_HBO_DROPPED"/>
+<link href="#rfc.section.9.2" rel="Chapter" title="9.2 Properties"/>
+<link href="#rfc.section.9.2.1" rel="Chapter" title="9.2.1 PROP 10: PROP_HBO_MEM_MAX"/>
+<link href="#rfc.section.9.2.2" rel="Chapter" title="9.2.2 PROP 11: PROP_HBO_BLOCK_MAX"/>
+<link href="#rfc.section.10" rel="Chapter" title="10 Feature: Jam Detection"/>
+<link href="#rfc.section.10.1" rel="Chapter" title="10.1 Properties"/>
+<link href="#rfc.section.10.1.1" rel="Chapter" title="10.1.1 PROP 4608: PROP_JAM_DETECT_ENABLE"/>
+<link href="#rfc.section.10.1.2" rel="Chapter" title="10.1.2 PROP 4609: PROP_JAM_DETECTED"/>
+<link href="#rfc.section.10.1.3" rel="Chapter" title="10.1.3 PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD"/>
+<link href="#rfc.section.10.1.4" rel="Chapter" title="10.1.4 PROP 4611: PROP_JAM_DETECT_WINDOW"/>
+<link href="#rfc.section.10.1.5" rel="Chapter" title="10.1.5 PROP 4612: PROP_JAM_DETECT_BUSY"/>
+<link href="#rfc.section.10.1.6" rel="Chapter" title="10.1.6 PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP"/>
+<link href="#rfc.section.11" rel="Chapter" title="11 Feature: GPIO Access"/>
+<link href="#rfc.section.11.1" rel="Chapter" title="11.1 Properties"/>
+<link href="#rfc.section.11.1.1" rel="Chapter" title="11.1.1 PROP 4096: PROP_GPIO_CONFIG"/>
+<link href="#rfc.section.11.1.2" rel="Chapter" title="11.1.2 PROP 4098: PROP_GPIO_STATE"/>
+<link href="#rfc.section.11.1.3" rel="Chapter" title="11.1.3 PROP 4099: PROP_GPIO_STATE_SET"/>
+<link href="#rfc.section.11.1.4" rel="Chapter" title="11.1.4 PROP 4100: PROP_GPIO_STATE_CLEAR"/>
+<link href="#rfc.section.12" rel="Chapter" title="12 Feature: True Random Number Generation"/>
+<link href="#rfc.section.12.1" rel="Chapter" title="12.1 Properties"/>
+<link href="#rfc.section.12.1.1" rel="Chapter" title="12.1.1 PROP 4101: PROP_TRNG_32"/>
+<link href="#rfc.section.12.1.2" rel="Chapter" title="12.1.2 PROP 4102: PROP_TRNG_128"/>
+<link href="#rfc.section.12.1.3" rel="Chapter" title="12.1.3 PROP 4103: PROP_TRNG_RAW_32"/>
+<link href="#rfc.section.13" rel="Chapter" title="13 Security Considerations"/>
+<link href="#rfc.section.13.1" rel="Chapter" title="13.1 Raw Application Access"/>
+<link href="#rfc.appendix.A" rel="Chapter" title="A Framing Protocol"/>
+<link href="#rfc.appendix.A.1" rel="Chapter" title="A.1 UART Recommendations"/>
+<link href="#rfc.appendix.A.1.1" rel="Chapter" title="A.1.1 UART Bit Rate Detection"/>
+<link href="#rfc.appendix.A.1.2" rel="Chapter" title="A.1.2 HDLC-Lite"/>
+<link href="#rfc.appendix.A.2" rel="Chapter" title="A.2 SPI Recommendations"/>
+<link href="#rfc.appendix.A.2.1" rel="Chapter" title="A.2.1 SPI Framing Protocol"/>
+<link href="#rfc.appendix.A.3" rel="Chapter" title="A.3 I&#xB2;C Recommendations"/>
+<link href="#rfc.appendix.A.4" rel="Chapter" title="A.4 Native USB Recommendations"/>
+<link href="#rfc.appendix.B" rel="Chapter" title="B Test Vectors"/>
+<link href="#rfc.appendix.B.1" rel="Chapter" title="B.1 Test Vector: Packed Unsigned Integer"/>
+<link href="#rfc.appendix.B.2" rel="Chapter" title="B.2 Test Vector: Reset Command"/>
+<link href="#rfc.appendix.B.3" rel="Chapter" title="B.3 Test Vector: Reset Notification"/>
+<link href="#rfc.appendix.B.4" rel="Chapter" title="B.4 Test Vector: Scan Beacon"/>
+<link href="#rfc.appendix.B.5" rel="Chapter" title="B.5 Test Vector: Inbound IPv6 Packet"/>
+<link href="#rfc.appendix.B.6" rel="Chapter" title="B.6 Test Vector: Outbound IPv6 Packet"/>
+<link href="#rfc.appendix.B.7" rel="Chapter" title="B.7 Test Vector: Fetch list of on-mesh networks"/>
+<link href="#rfc.appendix.B.8" rel="Chapter" title="B.8 Test Vector: Returned list of on-mesh networks"/>
+<link href="#rfc.appendix.B.9" rel="Chapter" title="B.9 Test Vector: Adding an on-mesh network"/>
+<link href="#rfc.appendix.B.10" rel="Chapter" title="B.10 Test Vector: Insertion notification of an on-mesh network"/>
+<link href="#rfc.appendix.B.11" rel="Chapter" title="B.11 Test Vector: Removing a local on-mesh network"/>
+<link href="#rfc.appendix.B.12" rel="Chapter" title="B.12 Test Vector: Removal notification of an on-mesh network"/>
+<link href="#rfc.appendix.C" rel="Chapter" title="C Example Sessions"/>
+<link href="#rfc.appendix.C.1" rel="Chapter" title="C.1 NCP Initialization"/>
+<link href="#rfc.appendix.C.2" rel="Chapter" title="C.2 Attaching to a network"/>
+<link href="#rfc.appendix.C.3" rel="Chapter" title="C.3 Successfully joining a pre-existing network"/>
+<link href="#rfc.appendix.C.4" rel="Chapter" title="C.4 Unsuccessfully joining a pre-existing network"/>
+<link href="#rfc.appendix.C.5" rel="Chapter" title="C.5 Detaching from a network"/>
+<link href="#rfc.appendix.C.6" rel="Chapter" title="C.6 Attaching to a saved network"/>
+<link href="#rfc.appendix.C.7" rel="Chapter" title="C.7 NCP Software Reset"/>
+<link href="#rfc.appendix.C.8" rel="Chapter" title="C.8 Adding an on-mesh prefix"/>
+<link href="#rfc.appendix.C.9" rel="Chapter" title="C.9 Entering low-power modes"/>
+<link href="#rfc.appendix.C.10" rel="Chapter" title="C.10 Sniffing raw packets"/>
+<link href="#rfc.appendix.D" rel="Chapter" title="D Glossary"/>
+<link href="#rfc.appendix.E" rel="Chapter" title="E Acknowledgments"/>
+<link href="#rfc.authors" rel="Chapter"/>
+
+
+  <meta name="generator" content="xml2rfc version 2.5.2 - http://tools.ietf.org/tools/xml2rfc" />
+  <link rel="schema.dct" href="http://purl.org/dc/terms/" />
+
+  <meta name="dct.creator" content="Quattlebaum, R. and J. Woodyatt, Ed." />
+  <meta name="dct.identifier" content="urn:ietf:id:draft-rquattle-spinel-unified-ab5628a5" />
+  <meta name="dct.issued" scheme="ISO8601" content="2017-6-22" />
+  <meta name="dct.abstract" content="This document describes the Spinel protocol, which facilitates the control and management of IPv6 network interfaces on devices where general purpose application processors offload network functions at their interfaces to network co-processors (NCP) connected by simple communication links like serial data channels. While initially developed to support Thread(R), Spinel's layered design allows it to be easily adapted to other similar network technologies.  " />
+  <meta name="description" content="This document describes the Spinel protocol, which facilitates the control and management of IPv6 network interfaces on devices where general purpose application processors offload network functions at their interfaces to network co-processors (NCP) connected by simple communication links like serial data channels. While initially developed to support Thread(R), Spinel's layered design allows it to be easily adapted to other similar network technologies.  " />
+
+</head>
+
+<body>
+
+  <table class="header">
+    <tbody>
+    
+    	<tr>
+  <td class="left">Network Working Group</td>
+  <td class="right">R. Quattlebaum</td>
+</tr>
+<tr>
+  <td class="left">Internet-Draft</td>
+  <td class="right">J. Woodyatt, Ed.</td>
+</tr>
+<tr>
+  <td class="left">Intended status: Informational</td>
+  <td class="right">Nest Labs, Inc.</td>
+</tr>
+<tr>
+  <td class="left">Expires: December 24, 2017</td>
+  <td class="right">June 22, 2017</td>
+</tr>
+
+    	
+    </tbody>
+  </table>
+
+  <p class="title">Spinel Host-Controller Protocol<br />
+  <span class="filename">draft-rquattle-spinel-unified-ab5628a5</span></p>
+  
+  <h1 id="rfc.abstract">
+  <a href="#rfc.abstract">Abstract</a>
+</h1>
+<p>This document describes the Spinel protocol, which facilitates the control and management of IPv6 network interfaces on devices where general purpose application processors offload network functions at their interfaces to network co-processors (NCP) connected by simple communication links like serial data channels. While initially developed to support Thread(R), Spinel's layered design allows it to be easily adapted to other similar network technologies.  </p>
+<p>This document also describes various Spinel specializations, including support for the Thread(R) low-power mesh network technology.  </p>
+<h1 id="rfc.status">
+  <a href="#rfc.status">Status of This Memo</a>
+</h1>
+<p>This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.</p>
+<p>Internet-Drafts are working documents of the Internet Engineering Task Force (IETF).  Note that other groups may also distribute working documents as Internet-Drafts.  The list of current Internet-Drafts is at http://datatracker.ietf.org/drafts/current/.</p>
+<p>Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time.  It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."</p>
+<p>This Internet-Draft will expire on December 24, 2017.</p>
+<h1 id="rfc.copyrightnotice">
+  <a href="#rfc.copyrightnotice">Copyright Notice</a>
+</h1>
+<p>Copyright (c) 2017 IETF Trust and the persons identified as the document authors.  All rights reserved.</p>
+<p>This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document.  Please review these documents carefully, as they describe your rights and restrictions with respect to this document.  Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.</p>
+<p>This document may not be modified, and derivative works of it may not be created, and it may not be published except as an Internet-Draft.</p>
+
+  
+  <hr class="noprint" />
+  <h1 class="np" id="rfc.toc"><a href="#rfc.toc">Table of Contents</a></h1>
+  <ul class="toc">
+
+  	<li>1.   <a href="#rfc.section.1">Introduction</a></li>
+<ul><li>1.1.   <a href="#rfc.section.1.1">About this Draft</a></li>
+<ul><li>1.1.1.   <a href="#rfc.section.1.1.1">Scope</a></li>
+<li>1.1.2.   <a href="#rfc.section.1.1.2">Renumbering</a></li>
+</ul></ul><li>2.   <a href="#rfc.section.2">Frame Format</a></li>
+<ul><li>2.1.   <a href="#rfc.section.2.1">Header Format</a></li>
+<ul><li>2.1.1.   <a href="#rfc.section.2.1.1">FLG: Flag</a></li>
+<li>2.1.2.   <a href="#rfc.section.2.1.2">NLI: Network Link Identifier</a></li>
+<li>2.1.3.   <a href="#rfc.section.2.1.3">TID: Transaction Identifier</a></li>
+<li>2.1.4.   <a href="#rfc.section.2.1.4">Command Identifier (CMD)</a></li>
+<li>2.1.5.   <a href="#rfc.section.2.1.5">Command Payload (Optional)</a></li>
+</ul></ul><li>3.   <a href="#rfc.section.3">Data Packing</a></li>
+<ul><li>3.1.   <a href="#rfc.section.3.1">Primitive Types</a></li>
+<li>3.2.   <a href="#rfc.section.3.2">Packed Unsigned Integer</a></li>
+<li>3.3.   <a href="#rfc.section.3.3">Data Blobs</a></li>
+<li>3.4.   <a href="#rfc.section.3.4">Structured Data</a></li>
+<li>3.5.   <a href="#rfc.section.3.5">Arrays</a></li>
+</ul><li>4.   <a href="#rfc.section.4">Commands</a></li>
+<ul><li>4.1.   <a href="#rfc.section.4.1">CMD 0: (Host-&gt;NCP) CMD_NOOP</a></li>
+<li>4.2.   <a href="#rfc.section.4.2">CMD 1: (Host-&gt;NCP) CMD_RESET</a></li>
+<li>4.3.   <a href="#rfc.section.4.3">CMD 2: (Host-&gt;NCP) CMD_PROP_VALUE_GET</a></li>
+<li>4.4.   <a href="#rfc.section.4.4">CMD 3: (Host-&gt;NCP) CMD_PROP_VALUE_SET</a></li>
+<li>4.5.   <a href="#rfc.section.4.5">CMD 4: (Host-&gt;NCP) CMD_PROP_VALUE_INSERT</a></li>
+<li>4.6.   <a href="#rfc.section.4.6">CMD 5: (Host-&gt;NCP) CMD_PROP_VALUE_REMOVE</a></li>
+<li>4.7.   <a href="#rfc.section.4.7">CMD 6: (NCP-&gt;Host) CMD_PROP_VALUE_IS</a></li>
+<li>4.8.   <a href="#rfc.section.4.8">CMD 7: (NCP-&gt;Host) CMD_PROP_VALUE_INSERTED</a></li>
+<li>4.9.   <a href="#rfc.section.4.9">CMD 8: (NCP-&gt;Host) CMD_PROP_VALUE_REMOVED</a></li>
+<li>4.10.   <a href="#rfc.section.4.10">CMD 18: (Host-&gt;NCP) CMD_PEEK</a></li>
+<li>4.11.   <a href="#rfc.section.4.11">CMD 19: (NCP-&gt;Host) CMD_PEEK_RET</a></li>
+<li>4.12.   <a href="#rfc.section.4.12">CMD 20: (Host-&gt;NCP) CMD_POKE</a></li>
+<li>4.13.   <a href="#rfc.section.4.13">CMD 21: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_GET</a></li>
+<li>4.14.   <a href="#rfc.section.4.14">CMD 22: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_SET</a></li>
+<li>4.15.   <a href="#rfc.section.4.15">CMD 23: (NCP-&gt;Host) CMD_PROP_VALUES_ARE</a></li>
+</ul><li>5.   <a href="#rfc.section.5">Properties</a></li>
+<ul><li>5.1.   <a href="#rfc.section.5.1">Property Methods</a></li>
+<li>5.2.   <a href="#rfc.section.5.2">Property Types</a></li>
+<ul><li>5.2.1.   <a href="#rfc.section.5.2.1">Single-Value Properties</a></li>
+<li>5.2.2.   <a href="#rfc.section.5.2.2">Multiple-Value Properties</a></li>
+<li>5.2.3.   <a href="#rfc.section.5.2.3">Stream Properties</a></li>
+</ul><li>5.3.   <a href="#rfc.section.5.3">Property Numbering</a></li>
+<li>5.4.   <a href="#rfc.section.5.4">Property Sections</a></li>
+<li>5.5.   <a href="#rfc.section.5.5">Core Properties</a></li>
+<ul><li>5.5.1.   <a href="#rfc.section.5.5.1">PROP 0: PROP_LAST_STATUS</a></li>
+<li>5.5.2.   <a href="#rfc.section.5.5.2">PROP 1: PROP_PROTOCOL_VERSION</a></li>
+<li>5.5.3.   <a href="#rfc.section.5.5.3">PROP 2: PROP_NCP_VERSION</a></li>
+<li>5.5.4.   <a href="#rfc.section.5.5.4">PROP 3: PROP_INTERFACE_TYPE</a></li>
+<li>5.5.5.   <a href="#rfc.section.5.5.5">PROP 4: PROP_INTERFACE_VENDOR_ID</a></li>
+<li>5.5.6.   <a href="#rfc.section.5.5.6">PROP 5: PROP_CAPS</a></li>
+<li>5.5.7.   <a href="#rfc.section.5.5.7">PROP 6: PROP_INTERFACE_COUNT</a></li>
+<li>5.5.8.   <a href="#rfc.section.5.5.8">PROP 7: PROP_POWER_STATE</a></li>
+<li>5.5.9.   <a href="#rfc.section.5.5.9">PROP 8: PROP_HWADDR</a></li>
+<li>5.5.10.   <a href="#rfc.section.5.5.10">PROP 9: PROP_LOCK</a></li>
+<li>5.5.11.   <a href="#rfc.section.5.5.11">PROP 10: PROP_HOST_POWER_STATE</a></li>
+<li>5.5.12.   <a href="#rfc.section.5.5.12">PROP 4104: PROP_UNSOL_UPDATE_FILTER</a></li>
+<li>5.5.13.   <a href="#rfc.section.5.5.13">PROP 4105: PROP_UNSOL_UPDATE_LIST</a></li>
+</ul><li>5.6.   <a href="#rfc.section.5.6">Stream Properties</a></li>
+<ul><li>5.6.1.   <a href="#rfc.section.5.6.1">PROP 112: PROP_STREAM_DEBUG</a></li>
+<li>5.6.2.   <a href="#rfc.section.5.6.2">PROP 113: PROP_STREAM_RAW</a></li>
+<li>5.6.3.   <a href="#rfc.section.5.6.3">PROP 114: PROP_STREAM_NET</a></li>
+<li>5.6.4.   <a href="#rfc.section.5.6.4">PROP 115: PROP_STREAM_NET_INSECURE</a></li>
+</ul><li>5.7.   <a href="#rfc.section.5.7">PHY Properties</a></li>
+<ul><li>5.7.1.   <a href="#rfc.section.5.7.1">PROP 32: PROP_PHY_ENABLED</a></li>
+<li>5.7.2.   <a href="#rfc.section.5.7.2">PROP 33: PROP_PHY_CHAN</a></li>
+<li>5.7.3.   <a href="#rfc.section.5.7.3">PROP 34: PROP_PHY_CHAN_SUPPORTED</a></li>
+<li>5.7.4.   <a href="#rfc.section.5.7.4">PROP 35: PROP_PHY_FREQ</a></li>
+<li>5.7.5.   <a href="#rfc.section.5.7.5">PROP 36: PROP_PHY_CCA_THRESHOLD</a></li>
+<li>5.7.6.   <a href="#rfc.section.5.7.6">PROP 37: PROP_PHY_TX_POWER</a></li>
+<li>5.7.7.   <a href="#rfc.section.5.7.7">PROP 38: PROP_PHY_RSSI</a></li>
+<li>5.7.8.   <a href="#rfc.section.5.7.8">PROP 39: PROP_PHY_RX_SENSITIVITY</a></li>
+</ul><li>5.8.   <a href="#rfc.section.5.8">MAC Properties</a></li>
+<ul><li>5.8.1.   <a href="#rfc.section.5.8.1">PROP 48: PROP_MAC_SCAN_STATE</a></li>
+<li>5.8.2.   <a href="#rfc.section.5.8.2">PROP 49: PROP_MAC_SCAN_MASK</a></li>
+<li>5.8.3.   <a href="#rfc.section.5.8.3">PROP 50: PROP_MAC_SCAN_PERIOD</a></li>
+<li>5.8.4.   <a href="#rfc.section.5.8.4">PROP 51: PROP_MAC_SCAN_BEACON</a></li>
+<li>5.8.5.   <a href="#rfc.section.5.8.5">PROP 52: PROP_MAC_15_4_LADDR</a></li>
+<li>5.8.6.   <a href="#rfc.section.5.8.6">PROP 53: PROP_MAC_15_4_SADDR</a></li>
+<li>5.8.7.   <a href="#rfc.section.5.8.7">PROP 54: PROP_MAC_15_4_PANID</a></li>
+<li>5.8.8.   <a href="#rfc.section.5.8.8">PROP 55: PROP_MAC_RAW_STREAM_ENABLED</a></li>
+<li>5.8.9.   <a href="#rfc.section.5.8.9">PROP 56: PROP_MAC_PROMISCUOUS_MODE</a></li>
+<li>5.8.10.   <a href="#rfc.section.5.8.10">PROP 57: PROP_MAC_ENERGY_SCAN_RESULT</a></li>
+<li>5.8.11.   <a href="#rfc.section.5.8.11">PROP 4864: PROP_MAC_WHITELIST</a></li>
+<li>5.8.12.   <a href="#rfc.section.5.8.12">PROP 4865: PROP_MAC_WHITELIST_ENABLED</a></li>
+<li>5.8.13.   <a href="#rfc.section.5.8.13">PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED</a></li>
+<li>5.8.14.   <a href="#rfc.section.5.8.14">PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES</a></li>
+<li>5.8.15.   <a href="#rfc.section.5.8.15">PROP 4869: SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES</a></li>
+<li>5.8.16.   <a href="#rfc.section.5.8.16">PROP 4870: PROP_MAC_BLACKLIST</a></li>
+<li>5.8.17.   <a href="#rfc.section.5.8.17">PROP 4871: PROP_MAC_BLACKLIST_ENABLED</a></li>
+</ul><li>5.9.   <a href="#rfc.section.5.9">NET Properties</a></li>
+<ul><li>5.9.1.   <a href="#rfc.section.5.9.1">PROP 64: PROP_NET_SAVED</a></li>
+<li>5.9.2.   <a href="#rfc.section.5.9.2">PROP 65: PROP_NET_IF_UP</a></li>
+<li>5.9.3.   <a href="#rfc.section.5.9.3">PROP 66: PROP_NET_STACK_UP</a></li>
+<li>5.9.4.   <a href="#rfc.section.5.9.4">PROP 67: PROP_NET_ROLE</a></li>
+<li>5.9.5.   <a href="#rfc.section.5.9.5">PROP 68: PROP_NET_NETWORK_NAME</a></li>
+<li>5.9.6.   <a href="#rfc.section.5.9.6">PROP 69: PROP_NET_XPANID</a></li>
+<li>5.9.7.   <a href="#rfc.section.5.9.7">PROP 70: PROP_NET_MASTER_KEY</a></li>
+<li>5.9.8.   <a href="#rfc.section.5.9.8">PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER</a></li>
+<li>5.9.9.   <a href="#rfc.section.5.9.9">PROP 72: PROP_NET_PARTITION_ID</a></li>
+<li>5.9.10.   <a href="#rfc.section.5.9.10">PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING</a></li>
+<li>5.9.11.   <a href="#rfc.section.5.9.11">PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME</a></li>
+<li>5.9.12.   <a href="#rfc.section.5.9.12">PROP 75: PROP_NET_PSKC</a></li>
+</ul><li>5.10.   <a href="#rfc.section.5.10">IPv6 Properties</a></li>
+<ul><li>5.10.1.   <a href="#rfc.section.5.10.1">PROP 96: PROP_IPV6_LL_ADDR</a></li>
+<li>5.10.2.   <a href="#rfc.section.5.10.2">PROP 97: PROP_IPV6_ML_ADDR</a></li>
+<li>5.10.3.   <a href="#rfc.section.5.10.3">PROP 98: PROP_IPV6_ML_PREFIX</a></li>
+<li>5.10.4.   <a href="#rfc.section.5.10.4">PROP 99: PROP_IPV6_ADDRESS_TABLE</a></li>
+<li>5.10.5.   <a href="#rfc.section.5.10.5">PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD</a></li>
+</ul><li>5.11.   <a href="#rfc.section.5.11">Debug Properties</a></li>
+<ul><li>5.11.1.   <a href="#rfc.section.5.11.1">PROP 16384: PROP_DEBUG_TEST_ASSERT</a></li>
+<li>5.11.2.   <a href="#rfc.section.5.11.2">PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL</a></li>
+</ul></ul><li>6.   <a href="#rfc.section.6">Status Codes</a></li>
+<li>7.   <a href="#rfc.section.7">Technology: Thread(R)</a></li>
+<ul><li>7.1.   <a href="#rfc.section.7.1">Capabilities</a></li>
+<li>7.2.   <a href="#rfc.section.7.2">Properties</a></li>
+<ul><li>7.2.1.   <a href="#rfc.section.7.2.1">PROP 80: PROP_THREAD_LEADER_ADDR</a></li>
+<li>7.2.2.   <a href="#rfc.section.7.2.2">PROP 81: PROP_THREAD_PARENT</a></li>
+<li>7.2.3.   <a href="#rfc.section.7.2.3">PROP 82: PROP_THREAD_CHILD_TABLE</a></li>
+<li>7.2.4.   <a href="#rfc.section.7.2.4">PROP 83: PROP_THREAD_LEADER_RID</a></li>
+<li>7.2.5.   <a href="#rfc.section.7.2.5">PROP 84: PROP_THREAD_LEADER_WEIGHT</a></li>
+<li>7.2.6.   <a href="#rfc.section.7.2.6">PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT</a></li>
+<li>7.2.7.   <a href="#rfc.section.7.2.7">PROP 86: PROP_THREAD_NETWORK_DATA</a></li>
+<li>7.2.8.   <a href="#rfc.section.7.2.8">PROP 87: PROP_THREAD_NETWORK_DATA_VERSION</a></li>
+<li>7.2.9.   <a href="#rfc.section.7.2.9">PROP 88: PROP_THREAD_STABLE_NETWORK_DATA</a></li>
+<li>7.2.10.   <a href="#rfc.section.7.2.10">PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION</a></li>
+<li>7.2.11.   <a href="#rfc.section.7.2.11">PROP 90: PROP_THREAD_ON_MESH_NETS</a></li>
+<li>7.2.12.   <a href="#rfc.section.7.2.12">PROP 91: PROP_THREAD_OFF_MESH_ROUTES</a></li>
+<li>7.2.13.   <a href="#rfc.section.7.2.13">PROP 92: PROP_THREAD_ASSISTING_PORTS</a></li>
+<li>7.2.14.   <a href="#rfc.section.7.2.14">PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE</a></li>
+<li>7.2.15.   <a href="#rfc.section.7.2.15">PROP 94: PROP_THREAD_MODE</a></li>
+<li>7.2.16.   <a href="#rfc.section.7.2.16">PROP 5376: PROP_THREAD_CHILD_TIMEOUT</a></li>
+<li>7.2.17.   <a href="#rfc.section.7.2.17">PROP 5377: PROP_THREAD_RLOC16</a></li>
+<li>7.2.18.   <a href="#rfc.section.7.2.18">PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD</a></li>
+<li>7.2.19.   <a href="#rfc.section.7.2.19">PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY</a></li>
+<li>7.2.20.   <a href="#rfc.section.7.2.20">PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT</a></li>
+<li>7.2.21.   <a href="#rfc.section.7.2.21">PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS</a></li>
+<li>7.2.22.   <a href="#rfc.section.7.2.22">PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU</a></li>
+<li>7.2.23.   <a href="#rfc.section.7.2.23">PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED</a></li>
+<li>7.2.24.   <a href="#rfc.section.7.2.24">PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD</a></li>
+<li>7.2.25.   <a href="#rfc.section.7.2.25">PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER</a></li>
+<li>7.2.26.   <a href="#rfc.section.7.2.26">PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID</a></li>
+<li>7.2.27.   <a href="#rfc.section.7.2.27">PROP 5387: PROP_THREAD_NEIGHBOR_TABLE</a></li>
+<li>7.2.28.   <a href="#rfc.section.7.2.28">PROP 5388: PROP_THREAD_CHILD_COUNT_MAX</a></li>
+<li>7.2.29.   <a href="#rfc.section.7.2.29">PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA</a></li>
+<li>7.2.30.   <a href="#rfc.section.7.2.30">PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA</a></li>
+<li>7.2.31.   <a href="#rfc.section.7.2.31">PROP 5391: PROP_THREAD_JOINERS</a></li>
+<li>7.2.32.   <a href="#rfc.section.7.2.32">PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED</a></li>
+<li>7.2.33.   <a href="#rfc.section.7.2.33">PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED</a></li>
+<li>7.2.34.   <a href="#rfc.section.7.2.34">PROP 5394: PROP_THREAD_TMF_PROXY_STREAM</a></li>
+<li>7.2.35.   <a href="#rfc.section.7.2.35">PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG</a></li>
+<li>7.2.36.   <a href="#rfc.section.7.2.36">PROP 5396: PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING</a></li>
+<li>7.2.37.   <a href="#rfc.section.7.2.37">PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID</a></li>
+<li>7.2.38.   <a href="#rfc.section.7.2.38">PROP 5398: PROP_THREAD_STEERING_DATA</a></li>
+</ul></ul><li>8.   <a href="#rfc.section.8">Feature: Network Save</a></li>
+<ul><li>8.1.   <a href="#rfc.section.8.1">Commands</a></li>
+<ul><li>8.1.1.   <a href="#rfc.section.8.1.1">CMD 9: (Host-&gt;NCP) CMD_NET_SAVE</a></li>
+<li>8.1.2.   <a href="#rfc.section.8.1.2">CMD 10: (Host-&gt;NCP) CMD_NET_CLEAR</a></li>
+<li>8.1.3.   <a href="#rfc.section.8.1.3">CMD 11: (Host-&gt;NCP) CMD_NET_RECALL</a></li>
+</ul></ul><li>9.   <a href="#rfc.section.9">Feature: Host Buffer Offload</a></li>
+<ul><li>9.1.   <a href="#rfc.section.9.1">Commands</a></li>
+<ul><li>9.1.1.   <a href="#rfc.section.9.1.1">CMD 12: (NCP-&gt;Host) CMD_HBO_OFFLOAD</a></li>
+<li>9.1.2.   <a href="#rfc.section.9.1.2">CMD 13: (NCP-&gt;Host) CMD_HBO_RECLAIM</a></li>
+<li>9.1.3.   <a href="#rfc.section.9.1.3">CMD 14: (NCP-&gt;Host) CMD_HBO_DROP</a></li>
+<li>9.1.4.   <a href="#rfc.section.9.1.4">CMD 15: (Host-&gt;NCP) CMD_HBO_OFFLOADED</a></li>
+<li>9.1.5.   <a href="#rfc.section.9.1.5">CMD 16: (Host-&gt;NCP) CMD_HBO_RECLAIMED</a></li>
+<li>9.1.6.   <a href="#rfc.section.9.1.6">CMD 17: (Host-&gt;NCP) CMD_HBO_DROPPED</a></li>
+</ul><li>9.2.   <a href="#rfc.section.9.2">Properties</a></li>
+<ul><li>9.2.1.   <a href="#rfc.section.9.2.1">PROP 10: PROP_HBO_MEM_MAX</a></li>
+<li>9.2.2.   <a href="#rfc.section.9.2.2">PROP 11: PROP_HBO_BLOCK_MAX</a></li>
+</ul></ul><li>10.   <a href="#rfc.section.10">Feature: Jam Detection</a></li>
+<ul><li>10.1.   <a href="#rfc.section.10.1">Properties</a></li>
+<ul><li>10.1.1.   <a href="#rfc.section.10.1.1">PROP 4608: PROP_JAM_DETECT_ENABLE</a></li>
+<li>10.1.2.   <a href="#rfc.section.10.1.2">PROP 4609: PROP_JAM_DETECTED</a></li>
+<li>10.1.3.   <a href="#rfc.section.10.1.3">PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD</a></li>
+<li>10.1.4.   <a href="#rfc.section.10.1.4">PROP 4611: PROP_JAM_DETECT_WINDOW</a></li>
+<li>10.1.5.   <a href="#rfc.section.10.1.5">PROP 4612: PROP_JAM_DETECT_BUSY</a></li>
+<li>10.1.6.   <a href="#rfc.section.10.1.6">PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP</a></li>
+</ul></ul><li>11.   <a href="#rfc.section.11">Feature: GPIO Access</a></li>
+<ul><li>11.1.   <a href="#rfc.section.11.1">Properties</a></li>
+<ul><li>11.1.1.   <a href="#rfc.section.11.1.1">PROP 4096: PROP_GPIO_CONFIG</a></li>
+<li>11.1.2.   <a href="#rfc.section.11.1.2">PROP 4098: PROP_GPIO_STATE</a></li>
+<li>11.1.3.   <a href="#rfc.section.11.1.3">PROP 4099: PROP_GPIO_STATE_SET</a></li>
+<li>11.1.4.   <a href="#rfc.section.11.1.4">PROP 4100: PROP_GPIO_STATE_CLEAR</a></li>
+</ul></ul><li>12.   <a href="#rfc.section.12">Feature: True Random Number Generation</a></li>
+<ul><li>12.1.   <a href="#rfc.section.12.1">Properties</a></li>
+<ul><li>12.1.1.   <a href="#rfc.section.12.1.1">PROP 4101: PROP_TRNG_32</a></li>
+<li>12.1.2.   <a href="#rfc.section.12.1.2">PROP 4102: PROP_TRNG_128</a></li>
+<li>12.1.3.   <a href="#rfc.section.12.1.3">PROP 4103: PROP_TRNG_RAW_32</a></li>
+</ul></ul><li>13.   <a href="#rfc.section.13">Security Considerations</a></li>
+<ul><li>13.1.   <a href="#rfc.section.13.1">Raw Application Access</a></li>
+</ul><li>Appendix A.   <a href="#rfc.appendix.A">Framing Protocol</a></li>
+<ul><li>A.1.   <a href="#rfc.appendix.A.1">UART Recommendations</a></li>
+<ul><li>A.1.1.   <a href="#rfc.appendix.A.1.1">UART Bit Rate Detection</a></li>
+<li>A.1.2.   <a href="#rfc.appendix.A.1.2">HDLC-Lite</a></li>
+</ul><li>A.2.   <a href="#rfc.appendix.A.2">SPI Recommendations</a></li>
+<ul><li>A.2.1.   <a href="#rfc.appendix.A.2.1">SPI Framing Protocol</a></li>
+</ul><li>A.3.   <a href="#rfc.appendix.A.3">I&#178;C Recommendations</a></li>
+<li>A.4.   <a href="#rfc.appendix.A.4">Native USB Recommendations</a></li>
+</ul><li>Appendix B.   <a href="#rfc.appendix.B">Test Vectors</a></li>
+<ul><li>B.1.   <a href="#rfc.appendix.B.1">Test Vector: Packed Unsigned Integer</a></li>
+<li>B.2.   <a href="#rfc.appendix.B.2">Test Vector: Reset Command</a></li>
+<li>B.3.   <a href="#rfc.appendix.B.3">Test Vector: Reset Notification</a></li>
+<li>B.4.   <a href="#rfc.appendix.B.4">Test Vector: Scan Beacon</a></li>
+<li>B.5.   <a href="#rfc.appendix.B.5">Test Vector: Inbound IPv6 Packet</a></li>
+<li>B.6.   <a href="#rfc.appendix.B.6">Test Vector: Outbound IPv6 Packet</a></li>
+<li>B.7.   <a href="#rfc.appendix.B.7">Test Vector: Fetch list of on-mesh networks</a></li>
+<li>B.8.   <a href="#rfc.appendix.B.8">Test Vector: Returned list of on-mesh networks</a></li>
+<li>B.9.   <a href="#rfc.appendix.B.9">Test Vector: Adding an on-mesh network</a></li>
+<li>B.10.   <a href="#rfc.appendix.B.10">Test Vector: Insertion notification of an on-mesh network</a></li>
+<li>B.11.   <a href="#rfc.appendix.B.11">Test Vector: Removing a local on-mesh network</a></li>
+<li>B.12.   <a href="#rfc.appendix.B.12">Test Vector: Removal notification of an on-mesh network</a></li>
+</ul><li>Appendix C.   <a href="#rfc.appendix.C">Example Sessions</a></li>
+<ul><li>C.1.   <a href="#rfc.appendix.C.1">NCP Initialization</a></li>
+<li>C.2.   <a href="#rfc.appendix.C.2">Attaching to a network</a></li>
+<li>C.3.   <a href="#rfc.appendix.C.3">Successfully joining a pre-existing network</a></li>
+<li>C.4.   <a href="#rfc.appendix.C.4">Unsuccessfully joining a pre-existing network</a></li>
+<li>C.5.   <a href="#rfc.appendix.C.5">Detaching from a network</a></li>
+<li>C.6.   <a href="#rfc.appendix.C.6">Attaching to a saved network</a></li>
+<li>C.7.   <a href="#rfc.appendix.C.7">NCP Software Reset</a></li>
+<li>C.8.   <a href="#rfc.appendix.C.8">Adding an on-mesh prefix</a></li>
+<li>C.9.   <a href="#rfc.appendix.C.9">Entering low-power modes</a></li>
+<li>C.10.   <a href="#rfc.appendix.C.10">Sniffing raw packets</a></li>
+</ul><li>Appendix D.   <a href="#rfc.appendix.D">Glossary</a></li>
+<li>Appendix E.   <a href="#rfc.appendix.E">Acknowledgments</a></li>
+<li><a href="#rfc.authors">Authors' Addresses</a></li>
+
+
+  </ul>
+
+  <h1 id="rfc.section.1"><a href="#rfc.section.1">1.</a> <a href="#introduction" id="introduction">Introduction</a></h1>
+<p id="rfc.section.1.p.1">Spinel is a host-controller protocol designed to enable interoperation over simple serial connections between general purpose device operating systems (OS) and network co-processors (NCP) for the purpose of controlling and managing their IPv6 network interfaces, achieving the following goals: </p>
+<p/>
+
+<ul>
+  <li>Adopt a layered approach to the protocol design, allowing future support for other network protocols.</li>
+  <li>Minimize the number of required commands/methods by providing a rich, property-based API.</li>
+  <li>Support NCPs capable of being connected to more than one network at a time.</li>
+  <li>Gracefully handle the addition of new features and capabilities without necessarily breaking backward compatibility.</li>
+  <li>Be as minimal and light-weight as possible without unnecessarily sacrificing flexibility.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.1.p.3">On top of this core framework, we define the properties and commands to enable various features and network protocols.  </p>
+<h1 id="rfc.section.1.1"><a href="#rfc.section.1.1">1.1.</a> <a href="#about-this-draft" id="about-this-draft">About this Draft</a></h1>
+<p id="rfc.section.1.1.p.1">This document is currently in a draft status and is changing often.  This section discusses some ideas for changes to the protocol that haven't yet been fully specified, as well as some of the impetus for the current design.  </p>
+<h1 id="rfc.section.1.1.1"><a href="#rfc.section.1.1.1">1.1.1.</a> <a href="#scope" id="scope">Scope</a></h1>
+<p id="rfc.section.1.1.1.p.1">The eventual intent is to have two documents: A Spinel basis document which discusses the network-technology-agnostic mechanisms and a Thread(R) specialization document which describes all of the Thread(R)-specific implementation details. Currently, this document covers both.  </p>
+<h1 id="rfc.section.1.1.2"><a href="#rfc.section.1.1.2">1.1.2.</a> <a href="#renumbering" id="renumbering">Renumbering</a></h1>
+<p id="rfc.section.1.1.2.p.1">Efforts are currently maintained to try to prevent overtly backward-incompatible changes to the existing protocol, but if you are implementing Spinel in your own products you should expect there to be at least one large renumbering event and major version number change before the standard is considered "baked". All changes will be clearly marked and documented to make such a transition as easy as possible.  </p>
+<p id="rfc.section.1.1.2.p.2">To allow conclusive detection of protocol (in)compatibility between the host and the NCP, the following commands and properties are already considered to be "baked" and will not change: </p>
+<p/>
+
+<ul>
+  <li>Command IDs zero through eight. (Reset, No-op, and Property-Value Commands)</li>
+  <li>Property IDs zero through two. (Last status, Protocol Version, and NCP Version)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.1.1.2.p.4">Renumbering would be undertaken in order to better organize the allocation of property IDs and capability IDs. One of the initial goals of this protocol was for it to be possible for a host or NCP to only implement properties with values less than 127 and for the NCP to still be usable---relegating all larger property values for extra features or other capabilities that aren't strictly necessary. This would allow simple implementations to avoid the need to implement support for PUIs (<a href="#packed-unsigned-integer">Section 3.2</a>).  </p>
+<p id="rfc.section.1.1.2.p.5">As time has gone by and the protocol has become more fleshed out, it has become clear that some of the initial allocations were inadequate and should be revisited if we want to try to achieve the original goal.  </p>
+<h1 id="rfc.section.2"><a href="#rfc.section.2">2.</a> <a href="#frame-format" id="frame-format">Frame Format</a></h1>
+<p id="rfc.section.2.p.1">A frame is defined simply as the concatenation of </p>
+<p/>
+
+<ul>
+  <li>A header byte</li>
+  <li>A command (up to three bytes, see <a href="#packed-unsigned-integer">Section 3.2</a> for format)</li>
+  <li>An optional command payload</li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD</td>
+      <td class="center">CMD_PAYLOAD</td>
+    </tr>
+  </tbody>
+</table>
+<h1 id="rfc.section.2.1"><a href="#rfc.section.2.1">2.1.</a> <a href="#header-format" id="header-format">Header Format</a></h1>
+<p id="rfc.section.2.1.p.1">The header byte is broken down as follows: </p>
+<pre>
+  0   1   2   3   4   5   6   7
++---+---+---+---+---+---+---+---+
+|  FLG  |  NLI  |      TID      |
++---+---+---+---+---+---+---+---+
+</pre>
+<p>
+  <a id="CREF1" class="info">[CREF1]<span class="info">RQ: Eventually, when https://github.com/miekg/mmark/issues/95 is addressed, the above table should be swapped out with this: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |---|---|---|---|---|---|---|---| |  FLG ||  NLI ||      TID   ||||</span></a>
+</p>
+<h1 id="rfc.section.2.1.1"><a href="#rfc.section.2.1.1">2.1.1.</a> <a href="#flg-flag" id="flg-flag">FLG: Flag</a></h1>
+<p id="rfc.section.2.1.1.p.1">The flag field of the header byte (<samp>FLG</samp>) is always set to the value two (or <samp>10</samp> in binary). Any frame received with these bits set to any other value else MUST NOT be considered a Spinel frame.  </p>
+<p id="rfc.section.2.1.1.p.2">This convention allows Spinel to be line compatible with BTLE HCI. By defining the first two bit in this way we can disambiguate between Spinel frames and HCI frames (which always start with either <samp>0x01</samp> or <samp>0x04</samp>) without any additional framing overhead.  </p>
+<h1 id="rfc.section.2.1.2"><a href="#rfc.section.2.1.2">2.1.2.</a> <a href="#nli-network-link-identifier" id="nli-network-link-identifier">NLI: Network Link Identifier</a></h1>
+<p id="rfc.section.2.1.2.p.1">The Network Link Identifier (NLI) is a number between 0 and 3, which is associated by the OS with one of up to four IPv6 zone indices corresponding to conceptual IPv6 interfaces on the NCP. This allows the protocol to support IPv6 nodes connecting simultaneously to more than one IPv6 network link using a single NCP instance. The first Network Link Identifier (0) MUST refer to a distinguished conceptual interface provided by the NCP for its IPv6 link type. The other three Network Link Identifiers (1, 2 and 3) MAY be dissociated from any conceptual interface.  </p>
+<h1 id="rfc.section.2.1.3"><a href="#rfc.section.2.1.3">2.1.3.</a> <a href="#tid-transaction-identifier" id="tid-transaction-identifier">TID: Transaction Identifier</a></h1>
+<p id="rfc.section.2.1.3.p.1">The least significant bits of the header represent the Transaction Identifier(TID). The TID is used for correlating responses to the commands which generated them.  </p>
+<p id="rfc.section.2.1.3.p.2">When a command is sent from the host, any reply to that command sent by the NCP will use the same value for the TID. When the host receives a frame that matches the TID of the command it sent, it can easily recognize that frame as the actual response to that command.  </p>
+<p id="rfc.section.2.1.3.p.3">The TID value of zero (0) is used for commands to which a correlated response is not expected or needed, such as for unsolicited update commands sent to the host from the NCP.  </p>
+<h1 id="rfc.section.2.1.4"><a href="#rfc.section.2.1.4">2.1.4.</a> <a href="#command-identifier-cmd" id="command-identifier-cmd">Command Identifier (CMD)</a></h1>
+<p id="rfc.section.2.1.4.p.1">The command identifier is a 21-bit unsigned integer encoded in up to three bytes using the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>. This encoding allows for up to 2,097,152 individual commands, with the first 127 commands represented as a single byte.  Command identifiers larger than 2,097,151 are explicitly forbidden.  </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">CID Range</th>
+      <th class="center">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">0 - 63</td>
+      <td class="center">Reserved for core commands</td>
+    </tr>
+    <tr>
+      <td class="center">64 - 15,359</td>
+      <td class="center">
+        <em>UNALLOCATED</em>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">15,360 - 16,383</td>
+      <td class="center">Vendor-specific</td>
+    </tr>
+    <tr>
+      <td class="center">16,384 - 1,999,999</td>
+      <td class="center">
+        <em>UNALLOCATED</em>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">2,000,000 - 2,097,151</td>
+      <td class="center">Experimental use only</td>
+    </tr>
+  </tbody>
+</table>
+<h1 id="rfc.section.2.1.5"><a href="#rfc.section.2.1.5">2.1.5.</a> <a href="#command-payload-optional" id="command-payload-optional">Command Payload (Optional)</a></h1>
+<p id="rfc.section.2.1.5.p.1">Depending on the semantics of the command in question, a payload MAY be included in the frame. The exact composition and length of the payload is defined by the command identifier.  </p>
+<h1 id="rfc.section.3"><a href="#rfc.section.3">3.</a> <a href="#data-packing" id="data-packing">Data Packing</a></h1>
+<p id="rfc.section.3.p.1">Data serialization for properties is performed using a light-weight data packing format which was loosely inspired by D-Bus. The format of a serialization is defined by a specially formatted string.  </p>
+<p id="rfc.section.3.p.2">This packing format is used for notational convenience. While this string-based datatype format has been designed so that the strings may be directly used by a structured data parser, such a thing is not required to implement Spinel. Indeed, higly constrained applications may find such a thing to be too heavyweight.  </p>
+<p id="rfc.section.3.p.3">Goals: </p>
+<p/>
+
+<ul>
+  <li>Be lightweight and favor direct representation of values.</li>
+  <li>Use an easily readable and memorable format string.</li>
+  <li>Support lists and structures.</li>
+  <li>Allow properties to be appended to structures while maintaining backward compatibility.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.3.p.5">Each primitive datatype has an ASCII character associated with it.  Structures can be represented as strings of these characters. For example: </p>
+<p/>
+
+<ul>
+  <li><samp>C</samp>: A single unsigned byte.</li>
+  <li><samp>C6U</samp>: A single unsigned byte, followed by a 128-bit IPv6 address, followed by a zero-terminated UTF8 string.</li>
+  <li><samp>A(6)</samp>: An array of concatenated IPv6 addresses</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.3.p.7">In each case, the data is represented exactly as described. For example, an array of 10 IPv6 address is stored as 160 bytes.  </p>
+<h1 id="rfc.section.3.1"><a href="#rfc.section.3.1">3.1.</a> <a href="#primitive-types" id="primitive-types">Primitive Types</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Char</th>
+      <th class="left">Name</th>
+      <th class="left">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">
+        <samp>.</samp>
+      </td>
+      <td class="left">DATATYPE_VOID</td>
+      <td class="left">Empty data type. Used internally.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>b</samp>
+      </td>
+      <td class="left">DATATYPE_BOOL</td>
+      <td class="left">Boolean value. Encoded in 8-bits as either 0x00 or 0x01. All other values are illegal.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>C</samp>
+      </td>
+      <td class="left">DATATYPE_UINT8</td>
+      <td class="left">Unsigned 8-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>c</samp>
+      </td>
+      <td class="left">DATATYPE_INT8</td>
+      <td class="left">Signed 8-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>S</samp>
+      </td>
+      <td class="left">DATATYPE_UINT16</td>
+      <td class="left">Unsigned 16-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>s</samp>
+      </td>
+      <td class="left">DATATYPE_INT16</td>
+      <td class="left">Signed 16-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>L</samp>
+      </td>
+      <td class="left">DATATYPE_UINT32</td>
+      <td class="left">Unsigned 32-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>l</samp>
+      </td>
+      <td class="left">DATATYPE_INT32</td>
+      <td class="left">Signed 32-bit integer.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>i</samp>
+      </td>
+      <td class="left">DATATYPE_UINT_PACKED</td>
+      <td class="left">Packed Unsigned Integer. See <a href="#packed-unsigned-integer">Section 3.2</a>.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>6</samp>
+      </td>
+      <td class="left">DATATYPE_IPv6ADDR</td>
+      <td class="left">IPv6 Address. (Big-endian)</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>E</samp>
+      </td>
+      <td class="left">DATATYPE_EUI64</td>
+      <td class="left">EUI-64 Address. (Big-endian)</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>e</samp>
+      </td>
+      <td class="left">DATATYPE_EUI48</td>
+      <td class="left">EUI-48 Address. (Big-endian)</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>D</samp>
+      </td>
+      <td class="left">DATATYPE_DATA</td>
+      <td class="left">Arbitrary data. See <a href="#data-blobs">Section 3.3</a>.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>d</samp>
+      </td>
+      <td class="left">DATATYPE_DATA_WLEN</td>
+      <td class="left">Arbitrary data with prepended length. See <a href="#data-blobs">Section 3.3</a>.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>U</samp>
+      </td>
+      <td class="left">DATATYPE_UTF8</td>
+      <td class="left">Zero-terminated UTF8-encoded string.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>t(...)</samp>
+      </td>
+      <td class="left">DATATYPE_STRUCT</td>
+      <td class="left">Structured datatype with prepended length. See <a href="#structured-data">Section 3.4</a>.</td>
+    </tr>
+    <tr>
+      <td class="center">
+        <samp>A(...)</samp>
+      </td>
+      <td class="left">DATATYPE_ARRAY</td>
+      <td class="left">Array of datatypes. Compound type. See <a href="#arrays">Section 3.5</a>.</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.3.1.p.1">All multi-byte values are little-endian unless explicitly stated otherwise.  </p>
+<h1 id="rfc.section.3.2"><a href="#rfc.section.3.2">3.2.</a> <a href="#packed-unsigned-integer" id="packed-unsigned-integer">Packed Unsigned Integer</a></h1>
+<p id="rfc.section.3.2.p.1">For certain types of integers, such command or property identifiers, usually have a value on the wire that is less than 127. However, in order to not preclude the use of values larger than 255, we would need to add an extra byte. Doing this would add an extra byte to the majority of instances, which can add up in terms of bandwidth.  </p>
+<p id="rfc.section.3.2.p.2">The packed unsigned integer format is based on the <a href="https://www.w3.org/TR/exi/#encodingUnsignedInteger">unsigned integer format in EXI</a>, except that we limit the maximum value to the largest value that can be encoded into three bytes(2,097,151).  </p>
+<p id="rfc.section.3.2.p.3">For all values less than 127, the packed form of the number is simply a single byte which directly represents the number. For values larger than 127, the following process is used to encode the value: </p>
+<p/>
+
+<ol>
+  <li>The unsigned integer is broken up into <em>n</em> 7-bit chunks and placed into <em>n</em> octets, leaving the most significant bit of each octet unused.</li>
+  <li>Order the octets from least-significant to most-significant.  (Little-endian)</li>
+  <li>Clear the most significant bit of the most significant octet. Set the least significant bit on all other octets.</li>
+</ol>
+
+<p> </p>
+<p id="rfc.section.3.2.p.5">Where <em>n</em> is the smallest number of 7-bit chunks you can use to represent the given value.  </p>
+<p id="rfc.section.3.2.p.6">Take the value 1337, for example: </p>
+<pre>
+1337 =&gt; 0x0539
+     =&gt; [39 0A]
+     =&gt; [B9 0A]
+</pre>
+<p id="rfc.section.3.2.p.7">To decode the value, you collect the 7-bit chunks until you find an octet with the most significant bit clear.  </p>
+<h1 id="rfc.section.3.3"><a href="#rfc.section.3.3">3.3.</a> <a href="#data-blobs" id="data-blobs">Data Blobs</a></h1>
+<p id="rfc.section.3.3.p.1">There are two types for data blobs: <samp>d</samp> and <samp>D</samp>.  </p>
+<p/>
+
+<ul>
+  <li><samp>d</samp> has the length of the data (in bytes) prepended to the data (with the length encoded as type <samp>S</samp>). The size of the length field is not included in the length.</li>
+  <li><samp>D</samp> does not have a prepended length: the length of the data is implied by the bytes remaining to be parsed. It is an error for <samp>D</samp> to not be the last type in a type in a type signature.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.3.3.p.3">This dichotomy allows for more efficient encoding by eliminating redundency. If the rest of the buffer is a data blob, encoding the length would be redundant because we already know how many bytes are in the rest of the buffer.  </p>
+<p id="rfc.section.3.3.p.4">In some cases we use <samp>d</samp> even if it is the last field in a type signature.  We do this to allow for us to be able to append additional fields to the type signature if necessary in the future. This is usually the case with embedded structs, like in the scan results.  </p>
+<p id="rfc.section.3.3.p.5">For example, let's say we have a buffer that is encoded with the datatype signature of <samp>CLLD</samp>. In this case, it is pretty easy to tell where the start and end of the data blob is: the start is 9 bytes from the start of the buffer, and its length is the length of the buffer minus 9. (9 is the number of bytes taken up by a byte and two longs) </p>
+<p id="rfc.section.3.3.p.6">The datatype signature <samp>CLLDU</samp> is illegal because we can't determine where the last field (a zero-terminated UTF8 string) starts. But the datatype <samp>CLLdU</samp> <em>is</em> legal, because the parser can determine the exact length of the data blob-- allowing it to know where the start of the next field would be.  </p>
+<h1 id="rfc.section.3.4"><a href="#rfc.section.3.4">3.4.</a> <a href="#structured-data" id="structured-data">Structured Data</a></h1>
+<p id="rfc.section.3.4.p.1">The structure data type (<samp>t(...)</samp>) is a way of bundling together several fields into a single structure. It can be thought of as a <samp>d</samp> type except that instead of being opaque, the fields in the content are known. This is useful for things like scan results where you have substructures which are defined by different layers.  </p>
+<p id="rfc.section.3.4.p.2">For example, consider the type signature <samp>Lt(ES)t(6C)</samp>. In this hypothetical case, the first struct is defined by the MAC layer, and the second struct is defined by the PHY layer. Because of the use of structures, we know exactly what part comes from that layer.  Additionally, we can add fields to each structure without introducing backward compatability problems: Data encoded as <samp>Lt(ESU)t(6C)</samp> (Notice the extra <samp>U</samp>) will decode just fine as <samp>Lt(ES)t(6C)</samp>. Additionally, if we don't care about the MAC layer and only care about the network layer, we could parse as <samp>Lt()t(6C)</samp>.  </p>
+<p id="rfc.section.3.4.p.3">Note that data encoded as <samp>Lt(ES)t(6C)</samp> will also parse as <samp>Ldd</samp>, with the structures from both layers now being opaque data blobs.  </p>
+<h1 id="rfc.section.3.5"><a href="#rfc.section.3.5">3.5.</a> <a href="#arrays" id="arrays">Arrays</a></h1>
+<p id="rfc.section.3.5.p.1">An array is simply a concatenated set of <em>n</em> data encodings. For example, the type <samp>A(6)</samp> is simply a list of IPv6 addresses---one after the other.  The type <samp>A(6E)</samp> likewise a concatenation of IPv6-address/EUI-64 pairs.  </p>
+<p id="rfc.section.3.5.p.2">If an array contains many fields, the fields will often be surrounded by a structure (<samp>t(...)</samp>). This effectively prepends each item in the array with its length. This is useful for improving parsing performance or to allow additional fields to be added in the future in a backward compatible way. If there is a high certainty that additional fields will never be added, the struct may be omitted (saving two bytes per item).  </p>
+<p id="rfc.section.3.5.p.3">This specification does not define a way to embed an array as a field alongside other fields.  </p>
+<h1 id="rfc.section.4"><a href="#rfc.section.4">4.</a> <a href="#commands" id="commands">Commands</a></h1>
+<h1 id="rfc.section.4.1"><a href="#rfc.section.4.1">4.1.</a> <a href="#cmd-noop" id="cmd-noop">CMD 0: (Host-&gt;NCP) CMD_NOOP</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_NOOP</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.1.p.1">No-Operation command. Induces the NCP to send a success status back to the host. This is primarily used for liveliness checks.  </p>
+<p id="rfc.section.4.1.p.2">The command payload for this command SHOULD be empty. The receiver MUST ignore any non-empty command payload.  </p>
+<p id="rfc.section.4.1.p.3">There is no error condition for this command.  </p>
+<h1 id="rfc.section.4.2"><a href="#rfc.section.4.2">4.2.</a> <a href="#cmd-reset" id="cmd-reset">CMD 1: (Host-&gt;NCP) CMD_RESET</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_RESET</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.2.p.1">Reset NCP command. Causes the NCP to perform a software reset. Due to the nature of this command, the TID is ignored. The host should instead wait for a <samp>CMD_PROP_VALUE_IS</samp> command from the NCP indicating <samp>PROP_LAST_STATUS</samp> has been set to <samp>STATUS_RESET_SOFTWARE</samp>.  </p>
+<p id="rfc.section.4.2.p.2">The command payload for this command SHOULD be empty. The receiver MUST ignore any non-empty command payload.  </p>
+<p id="rfc.section.4.2.p.3">If an error occurs, the value of <samp>PROP_LAST_STATUS</samp> will be emitted instead with the value set to the generated status code for the error.  </p>
+<h1 id="rfc.section.4.3"><a href="#rfc.section.4.3">4.3.</a> <a href="#cmd-prop-value-get" id="cmd-prop-value-get">CMD 2: (Host-&gt;NCP) CMD_PROP_VALUE_GET</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_GET</td>
+      <td class="center">PROP_ID</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.3.p.1">Get property value command. Causes the NCP to emit a <samp>CMD_PROP_VALUE_IS</samp> command for the given property identifier.  </p>
+<p id="rfc.section.4.3.p.2">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>.  </p>
+<p id="rfc.section.4.3.p.3">If an error occurs, the value of <samp>PROP_LAST_STATUS</samp> will be emitted instead with the value set to the generated status code for the error.  </p>
+<h1 id="rfc.section.4.4"><a href="#rfc.section.4.4">4.4.</a> <a href="#cmd-prop-value-set" id="cmd-prop-value-set">CMD 3: (Host-&gt;NCP) CMD_PROP_VALUE_SET</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_SET</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.4.p.1">Set property value command. Instructs the NCP to set the given property to the specific given value, replacing any previous value.  </p>
+<p id="rfc.section.4.4.p.2">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the property value. The exact format of the property value is defined by the property.  </p>
+<p id="rfc.section.4.4.p.3">If an error occurs, the value of <samp>PROP_LAST_STATUS</samp> will be emitted with the value set to the generated status code for the error.  </p>
+<h1 id="rfc.section.4.5"><a href="#rfc.section.4.5">4.5.</a> <a href="#cmd-prop-value-insert" id="cmd-prop-value-insert">CMD 4: (Host-&gt;NCP) CMD_PROP_VALUE_INSERT</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_INSERT</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.5.p.1">Insert value into property command. Instructs the NCP to insert the given value into a list-oriented property, without removing other items in the list. The resulting order of items in the list is defined by the individual property being operated on.  </p>
+<p id="rfc.section.4.5.p.2">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the value to be inserted. The exact format of the value is defined by the property.  </p>
+<p id="rfc.section.4.5.p.3">If the type signature of the property specified by <samp>PROP_ID</samp> consists of a single structure enclosed by an array (<samp>A(t(...))</samp>), then the contents of <samp>VALUE</samp> MUST contain the contents of the structure (<samp>...</samp>) rather than the serialization of the whole item (<samp>t(...)</samp>).  Specifically, the length of the structure MUST NOT be prepended to <samp>VALUE</samp>. This helps to eliminate redundant data.  </p>
+<p id="rfc.section.4.5.p.4">If an error occurs, the value of <samp>PROP_LAST_STATUS</samp> will be emitted with the value set to the generated status code for the error.  </p>
+<h1 id="rfc.section.4.6"><a href="#rfc.section.4.6">4.6.</a> <a href="#cmd-prop-value-remove" id="cmd-prop-value-remove">CMD 5: (Host-&gt;NCP) CMD_PROP_VALUE_REMOVE</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_REMOVE</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.6.p.1">Remove value from property command. Instructs the NCP to remove the given value from a list-oriented property, without affecting other items in the list. The resulting order of items in the list is defined by the individual property being operated on.  </p>
+<p id="rfc.section.4.6.p.2">Note that this command operates <em>by value</em>, not by index! </p>
+<p id="rfc.section.4.6.p.3">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the value to be removed. The exact format of the value is defined by the property.  </p>
+<p id="rfc.section.4.6.p.4">If the type signature of the property specified by <samp>PROP_ID</samp> consists of a single structure enclosed by an array (<samp>A(t(...))</samp>), then the contents of <samp>VALUE</samp> MUST contain the contents of the structure (<samp>...</samp>) rather than the serialization of the whole item (<samp>t(...)</samp>).  Specifically, the length of the structure MUST NOT be prepended to <samp>VALUE</samp>. This helps to eliminate redundant data.  </p>
+<p id="rfc.section.4.6.p.5">If an error occurs, the value of <samp>PROP_LAST_STATUS</samp> will be emitted with the value set to the generated status code for the error.  </p>
+<h1 id="rfc.section.4.7"><a href="#rfc.section.4.7">4.7.</a> <a href="#cmd-prop-value-is" id="cmd-prop-value-is">CMD 6: (NCP-&gt;Host) CMD_PROP_VALUE_IS</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_IS</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.7.p.1">Property value notification command. This command can be sent by the NCP in response to a previous command from the host, or it can be sent by the NCP in an unsolicited fashion to notify the host of various state changes asynchronously.  </p>
+<p id="rfc.section.4.7.p.2">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the current value of the given property.  </p>
+<h1 id="rfc.section.4.8"><a href="#rfc.section.4.8">4.8.</a> <a href="#cmd-prop-value-inserted" id="cmd-prop-value-inserted">CMD 7: (NCP-&gt;Host) CMD_PROP_VALUE_INSERTED</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_INSERTED</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.8.p.1">Property value insertion notification command. This command can be sent by the NCP in response to the <samp>CMD_PROP_VALUE_INSERT</samp> command, or it can be sent by the NCP in an unsolicited fashion to notify the host of various state changes asynchronously.  </p>
+<p id="rfc.section.4.8.p.2">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the value that was inserted into the given property.  </p>
+<p id="rfc.section.4.8.p.3">If the type signature of the property specified by <samp>PROP_ID</samp> consists of a single structure enclosed by an array (<samp>A(t(...))</samp>), then the contents of <samp>VALUE</samp> MUST contain the contents of the structure (<samp>...</samp>) rather than the serialization of the whole item (<samp>t(...)</samp>).  Specifically, the length of the structure MUST NOT be prepended to <samp>VALUE</samp>. This helps to eliminate redundant data.  </p>
+<p id="rfc.section.4.8.p.4">The resulting order of items in the list is defined by the given property.  </p>
+<h1 id="rfc.section.4.9"><a href="#rfc.section.4.9">4.9.</a> <a href="#cmd-prop-value-removed" id="cmd-prop-value-removed">CMD 8: (NCP-&gt;Host) CMD_PROP_VALUE_REMOVED</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_REMOVED</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.9.p.1">Property value removal notification command. This command can be sent by the NCP in response to the <samp>CMD_PROP_VALUE_REMOVE</samp> command, or it can be sent by the NCP in an unsolicited fashion to notify the host of various state changes asynchronously.  </p>
+<p id="rfc.section.4.9.p.2">Note that this command operates <em>by value</em>, not by index! </p>
+<p id="rfc.section.4.9.p.3">The payload for this command is the property identifier encoded in the packed unsigned integer format described in <a href="#packed-unsigned-integer">Section 3.2</a>, followed by the value that was removed from the given property.  </p>
+<p id="rfc.section.4.9.p.4">If the type signature of the property specified by <samp>PROP_ID</samp> consists of a single structure enclosed by an array (<samp>A(t(...))</samp>), then the contents of <samp>VALUE</samp> MUST contain the contents of the structure (<samp>...</samp>) rather than the serialization of the whole item (<samp>t(...)</samp>).  Specifically, the length of the structure MUST NOT be prepended to <samp>VALUE</samp>. This helps to eliminate redundant data.  </p>
+<p id="rfc.section.4.9.p.5">The resulting order of items in the list is defined by the given property.  </p>
+<h1 id="rfc.section.4.10"><a href="#rfc.section.4.10">4.10.</a> <a href="#cmd-peek" id="cmd-peek">CMD 18: (Host-&gt;NCP) CMD_PEEK</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">4</th>
+      <th class="center">2</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PEEK</td>
+      <td class="center">ADDRESS</td>
+      <td class="center">COUNT</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.10.p.1">This command allows the NCP to fetch values from the RAM of the NCP for debugging purposes. Upon success, <samp>CMD_PEEK_RET</samp> is sent from the NCP to the host. Upon failure, <samp>PROP_LAST_STATUS</samp> is emitted with the appropriate error indication.  </p>
+<p id="rfc.section.4.10.p.2">Due to the low-level nature of this command, certain error conditions may induce the NCP to reset.  </p>
+<p id="rfc.section.4.10.p.3">The NCP MAY prevent certain regions of memory from being accessed.  </p>
+<p id="rfc.section.4.10.p.4">The implementation of this command has security implications.  See <a href="#security-considerations">Section 13</a> for more information.  </p>
+<p id="rfc.section.4.10.p.5">This command requires the capability <samp>CAP_PEEK_POKE</samp> to be present.  </p>
+<h1 id="rfc.section.4.11"><a href="#rfc.section.4.11">4.11.</a> <a href="#cmd-peek-ret" id="cmd-peek-ret">CMD 19: (NCP-&gt;Host) CMD_PEEK_RET</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">4</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PEEK_RET</td>
+      <td class="center">ADDRESS</td>
+      <td class="center">COUNT</td>
+      <td class="center">BYTES</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.11.p.1">This command contains the contents of memory that was requested by a previous call to <samp>CMD_PEEK</samp>.  </p>
+<p id="rfc.section.4.11.p.2">This command requires the capability <samp>CAP_PEEK_POKE</samp> to be present.  </p>
+<h1 id="rfc.section.4.12"><a href="#rfc.section.4.12">4.12.</a> <a href="#cmd-poke" id="cmd-poke">CMD 20: (Host-&gt;NCP) CMD_POKE</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">4</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_POKE</td>
+      <td class="center">ADDRESS</td>
+      <td class="center">COUNT</td>
+      <td class="center">BYTES</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.12.p.1">This command writes the bytes to the specified memory address for debugging purposes.  </p>
+<p id="rfc.section.4.12.p.2">Due to the low-level nature of this command, certain error conditions may induce the NCP to reset.  </p>
+<p id="rfc.section.4.12.p.3">The implementation of this command has security implications.  See <a href="#security-considerations">Section 13</a> for more information.  </p>
+<p id="rfc.section.4.12.p.4">This command requires the capability <samp>CAP_PEEK_POKE</samp> to be present.  </p>
+<h1 id="rfc.section.4.13"><a href="#rfc.section.4.13">4.13.</a> <a href="#cmd-prop-value-multi-get" id="cmd-prop-value-multi-get">CMD 21: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_GET</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>A(i)</samp></li>
+  <li>Required Capability: <samp>CAP_CMD_MULTI</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.4.13.p.2">Fetch the value of multiple properties in one command. Arguments are an array of property IDs. If all properties are fetched successfully, a <samp>CMD_PROP_VALUES_ARE</samp> command is sent back to the host containing the propertyid and value of each fetched property. The order of the results in <samp>CMD_PROP_VALUES_ARE</samp> match the order of properties given in <samp>CMD_PROP_VALUE_GET</samp>.  </p>
+<p id="rfc.section.4.13.p.3">Errors fetching individual properties are reflected as indicating a change to <samp>PROP_LAST_STATUS</samp> for that property's place.  </p>
+<p id="rfc.section.4.13.p.4">Not all properties can be fetched using this method. As a general rule of thumb, any property that blocks when getting will fail for that individual property with <samp>STATUS_INVALID_COMMAND_FOR_PROP</samp>.  </p>
+<h1 id="rfc.section.4.14"><a href="#rfc.section.4.14">4.14.</a> <a href="#cmd-prop-value-multi-set" id="cmd-prop-value-multi-set">CMD 22: (Host-&gt;NCP) CMD_PROP_VALUE_MULTI_SET</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>A(iD)</samp></li>
+  <li>Required Capability: <samp>CAP_CMD_MULTI</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUE_MULTI_SET</td>
+      <td class="center">Property/Value Pairs</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.14.p.2">With each property/value pair being: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">LENGTH</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">PROP_VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.14.p.3">This command sets the value of several properties at once in the given order. The setting of properties stops at the first error, ignoring any later properties.  </p>
+<p id="rfc.section.4.14.p.4">The result of this command is generally <samp>CMD_PROP_VALUES_ARE</samp> unless (for example) a parsing error has occured (in which case <samp>CMD_PROP_VALUE_IS</samp> for <samp>PROP_LAST_STATUS</samp> would be the result). The order of the results in <samp>CMD_PROP_VALUES_ARE</samp> match the order of properties given in <samp>CMD_PROP_VALUE_MULTI_SET</samp>.  </p>
+<p id="rfc.section.4.14.p.5">Since the processing of properties to set stops at the first error, the resulting <samp>CMD_PROP_VALUES_ARE</samp> can contain fewer items than the requested number of properties to set.  </p>
+<p id="rfc.section.4.14.p.6">Not all properties can be set using this method. As a general rule of thumb, any property that blocks when setting will fail for that individual property with <samp>STATUS_INVALID_COMMAND_FOR_PROP</samp>.  </p>
+<h1 id="rfc.section.4.15"><a href="#rfc.section.4.15">4.15.</a> <a href="#cmd-prop-values-are" id="cmd-prop-values-are">CMD 23: (NCP-&gt;Host) CMD_PROP_VALUES_ARE</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>A(iD)</samp></li>
+  <li>Required Capability: <samp>CAP_CMD_MULTI</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_PROP_VALUES_ARE</td>
+      <td class="center">Property/Value Pairs</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.15.p.2">With each property/value pair being: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+      <th class="center">1-3</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">LENGTH</td>
+      <td class="center">PROP_ID</td>
+      <td class="center">PROP_VALUE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.4.15.p.3">This command is emitted by the NCP as the response to both the <samp>CMD_PROP_VALUE_MULTI_GET</samp> and <samp>CMD_PROP_VALUE_MULTI_SET</samp> commands. It is roughly analogous to <samp>CMD_PROP_VALUE_IS</samp>, except that it contains more than one property.  </p>
+<p id="rfc.section.4.15.p.4">This command SHOULD NOT be emitted asynchronously, or in response to any command other than <samp>CMD_PROP_VALUE_MULTI_GET</samp> or <samp>CMD_PROP_VALUE_MULTI_SET</samp>.  </p>
+<p id="rfc.section.4.15.p.5">The arguments are a list of structures containing the emitted property and the associated value. These are presented in the same order as given in the associated initiating command. In cases where getting or setting a specific property resulted in an error, the associated slot in this command will describe <samp>PROP_LAST_STATUS</samp>.  </p>
+<h1 id="rfc.section.5"><a href="#rfc.section.5">5.</a> <a href="#properties" id="properties">Properties</a></h1>
+<p id="rfc.section.5.p.1">Spinel is largely a property-based protocol, similar to representational state transfer (REST), with a property defined for every attribute that an OS needs to create, read, update or delete in the function of an IPv6 interface. The inspiration of this approach was memory-mapped hardware registers for peripherals. The goal is to avoid, as much as possible, the use of large complicated structures and/or method argument lists. The reason for avoiding these is because they have a tendency to change, especially early in development. Adding or removing a property from a structure can render the entire protocol incompatible. By using properties, you simply extend the protocol with an additional property.  </p>
+<p id="rfc.section.5.p.2">Almost all features and capabilities are implemented using properties. Most new features that are initially proposed as commands can be adapted to be property-based instead. Notable exceptions include "Host Buffer Offload" (<a href="#feature-host-buffer-offload">Section 9</a>) and "Network Save" (<a href="#feature-network-save">Section 8</a>).  </p>
+<p id="rfc.section.5.p.3">In Spinel, properties are keyed by an unsigned integer between 0 and 2,097,151 (See <a href="#packed-unsigned-integer">Section 3.2</a>).  </p>
+<h1 id="rfc.section.5.1"><a href="#rfc.section.5.1">5.1.</a> <a href="#property-methods" id="property-methods">Property Methods</a></h1>
+<p id="rfc.section.5.1.p.1">Properties may support one or more of the following methods: </p>
+<p/>
+
+<ul>
+  <li><samp>VALUE_GET</samp> (<a href="#cmd-prop-value-get">Section 4.3</a>)</li>
+  <li><samp>VALUE_SET</samp> (<a href="#cmd-prop-value-set">Section 4.4</a>)</li>
+  <li><samp>VALUE_INSERT</samp>  (<a href="#cmd-prop-value-insert">Section 4.5</a>)</li>
+  <li><samp>VALUE_REMOVE</samp>  (<a href="#cmd-prop-value-remove">Section 4.6</a>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.1.p.3">Additionally, the NCP can send updates to the host (either synchronously or asynchronously) that inform the host about changes to specific properties: </p>
+<p/>
+
+<ul>
+  <li><samp>VALUE_IS</samp>  (<a href="#cmd-prop-value-is">Section 4.7</a>)</li>
+  <li><samp>VALUE_INSERTED</samp>  (<a href="#cmd-prop-value-inserted">Section 4.8</a>)</li>
+  <li><samp>VALUE_REMOVED</samp>  (<a href="#cmd-prop-value-removed">Section 4.9</a>)</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.2"><a href="#rfc.section.5.2">5.2.</a> <a href="#property-types" id="property-types">Property Types</a></h1>
+<p id="rfc.section.5.2.p.1">Conceptually, there are three different types of properties: </p>
+<p/>
+
+<ul>
+  <li>Single-value properties</li>
+  <li>Multiple-value (Array) properties</li>
+  <li>Stream properties</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.2.1"><a href="#rfc.section.5.2.1">5.2.1.</a> <a href="#singlevalue-properties" id="singlevalue-properties">Single-Value Properties</a></h1>
+<p id="rfc.section.5.2.1.p.1">Single-value properties are properties that have a simple representation of a single value. Examples would be: </p>
+<p/>
+
+<ul>
+  <li>Current radio channel (Represented as an unsigned 8-bit integer)</li>
+  <li>Network name (Represented as a UTF-8 encoded string)</li>
+  <li>802.15.4 PAN ID (Represented as an unsigned 16-bit integer)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.2.1.p.3">The valid operations on these sorts of properties are <samp>GET</samp> and <samp>SET</samp>.  </p>
+<h1 id="rfc.section.5.2.2"><a href="#rfc.section.5.2.2">5.2.2.</a> <a href="#multiplevalue-properties" id="multiplevalue-properties">Multiple-Value Properties</a></h1>
+<p id="rfc.section.5.2.2.p.1">Multiple-Value Properties have more than one value associated with them. Examples would be: </p>
+<p/>
+
+<ul>
+  <li>List of channels supported by the radio hardware.</li>
+  <li>List of IPv6 addresses assigned to the interface.</li>
+  <li>List of capabilities supported by the NCP.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.2.2.p.3">The valid operations on these sorts of properties are <samp>VALUE_GET</samp>, <samp>VALUE_SET</samp>, <samp>VALUE_INSERT</samp>, and <samp>VALUE_REMOVE</samp>.  </p>
+<p id="rfc.section.5.2.2.p.4">When the value is fetched using <samp>VALUE_GET</samp>, the returned value is the concatenation of all of the individual values in the list. If the length of the value for an individual item in the list is not defined by the type then each item returned in the list is prepended with a length (See <a href="#arrays">Section 3.5</a>). The order of the returned items, unless explicitly defined for that specific property, is undefined.  </p>
+<p><samp>VALUE_SET</samp> provides a way to completely replace all previous values. Calling <samp>VALUE_SET</samp> with an empty value effectively instructs the NCP to clear the value of that property.  </p>
+<p><samp>VALUE_INSERT</samp> and <samp>VALUE_REMOVE</samp> provide mechanisms for the insertion or removal of individual items <em>by value</em>. The payload for these commands is a plain single value.  </p>
+<h1 id="rfc.section.5.2.3"><a href="#rfc.section.5.2.3">5.2.3.</a> <a href="#stream-properties" id="stream-properties">Stream Properties</a></h1>
+<p id="rfc.section.5.2.3.p.1">Stream properties are special properties representing streams of data. Examples would be: </p>
+<p/>
+
+<ul>
+  <li>Network packet stream (<a href="#prop-stream-net">Section 5.6.3</a>)</li>
+  <li>Raw packet stream (<a href="#prop-stream-raw">Section 5.6.2</a>)</li>
+  <li>Debug message stream (<a href="#prop-stream-debug">Section 5.6.1</a>)</li>
+  <li>Network Beacon stream (<a href="#prop-mac-scan-beacon">Section 5.8.4</a>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.2.3.p.3">All such properties emit changes asynchronously using the <samp>VALUE_IS</samp> command, sent from the NCP to the host. For example, as IPv6 traffic is received by the NCP, the IPv6 packets are sent to the host by way of asynchronous <samp>VALUE_IS</samp> notifications.  </p>
+<p id="rfc.section.5.2.3.p.4">Some of these properties also support the host send data back to the NCP. For example, this is how the host sends IPv6 traffic to the NCP.  </p>
+<p id="rfc.section.5.2.3.p.5">These types of properties generally do not support <samp>VALUE_GET</samp>, as it is meaningless.  </p>
+<h1 id="rfc.section.5.3"><a href="#rfc.section.5.3">5.3.</a> <a href="#property-numbering" id="property-numbering">Property Numbering</a></h1>
+<p id="rfc.section.5.3.p.1">While the majority of the properties that allow the configuration of network connectivity are network protocol specific, there are several properties that are required in all implementations.  </p>
+<p id="rfc.section.5.3.p.2">Future property allocations SHALL be made from the following allocation plan: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="left">Property ID Range</th>
+      <th class="left">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="left">0 - 127</td>
+      <td class="left">Reserved for frequently-used properties</td>
+    </tr>
+    <tr>
+      <td class="left">128 - 15,359</td>
+      <td class="left">Unallocated</td>
+    </tr>
+    <tr>
+      <td class="left">15,360 - 16,383</td>
+      <td class="left">Vendor-specific</td>
+    </tr>
+    <tr>
+      <td class="left">16,384 - 1,999,999</td>
+      <td class="left">Unallocated</td>
+    </tr>
+    <tr>
+      <td class="left">2,000,000 - 2,097,151</td>
+      <td class="left">Experimental use only</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.3.p.3">For an explanation of the data format encoding shorthand used throughout this document, see <a href="#data-packing">Section 3</a>.  </p>
+<h1 id="rfc.section.5.4"><a href="#rfc.section.5.4">5.4.</a> <a href="#property-sections" id="property-sections">Property Sections</a></h1>
+<p id="rfc.section.5.4.p.1">The currently assigned properties are broken up into several sections, each with reserved ranges of property identifiers.  These ranges are: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Name</th>
+      <th class="center">Range (Inclusive)</th>
+      <th class="center">Documentation</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Core</td>
+      <td class="center">0x00 - 0x1F, 0x1000 - 0x11FF</td>
+      <td class="center">
+        <a href="#prop-core">Section 5.5</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">PHY</td>
+      <td class="center">0x20 - 0x2F, 0x1200 - 0x12FF</td>
+      <td class="center">
+        <a href="#prop-phy">Section 5.7</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">MAC</td>
+      <td class="center">0x30 - 0x3F, 0x1300 - 0x13FF</td>
+      <td class="center">
+        <a href="#prop-mac">Section 5.8</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">NET</td>
+      <td class="center">0x40 - 0x4F, 0x1400 - 0x14FF</td>
+      <td class="center">
+        <a href="#prop-net">Section 5.9</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">Tech</td>
+      <td class="center">0x50 - 0x5F, 0x1500 - 0x15FF</td>
+      <td class="center">Technology-specific</td>
+    </tr>
+    <tr>
+      <td class="center">IPv6</td>
+      <td class="center">0x60 - 0x6F, 0x1600 - 0x16FF</td>
+      <td class="center">
+        <a href="#prop-ipv6">Section 5.10</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">Stream</td>
+      <td class="center">0x70 - 0x7F, 0x1700 - 0x17FF</td>
+      <td class="center">
+        <a href="#prop-core">Section 5.5</a>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">Debug</td>
+      <td class="center">0x4000 - 0x4400</td>
+      <td class="center">
+        <a href="#prop-debug">Section 5.11</a>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.4.p.2">Note that some of the property sections have two reserved ranges: a primary range (which is encoded as a single byte) and an extended range (which is encoded as two bytes).  properties which are used more frequently are generally allocated from the former range.  </p>
+<h1 id="rfc.section.5.5"><a href="#rfc.section.5.5">5.5.</a> <a href="#prop-core" id="prop-core">Core Properties</a></h1>
+<h1 id="rfc.section.5.5.1"><a href="#rfc.section.5.5.1">5.5.1.</a> <a href="#prop-last-status" id="prop-last-status">PROP 0: PROP_LAST_STATUS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Encoding: <samp>i</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="right">Octets:</th>
+      <th class="center">1-3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="right">Fields:</td>
+      <td class="center">LAST_STATUS</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.1.p.2">Describes the status of the last operation. Encoded as a packed unsigned integer.  </p>
+<p id="rfc.section.5.5.1.p.3">This property is emitted often to indicate the result status of pretty much any Host-to-NCP operation.  </p>
+<p id="rfc.section.5.5.1.p.4">It is emitted automatically at NCP startup with a value indicating the reset reason.  </p>
+<p id="rfc.section.5.5.1.p.5">See <a href="#status-codes">Section 6</a> for the complete list of status codes.  </p>
+<h1 id="rfc.section.5.5.2"><a href="#rfc.section.5.5.2">5.5.2.</a> <a href="#prop-protocol-version" id="prop-protocol-version">PROP 1: PROP_PROTOCOL_VERSION</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Encoding: <samp>ii</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1-3</th>
+      <th class="center">1-3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">MAJOR_VERSION</td>
+      <td class="center">MINOR_VERSION</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.2.p.2">Describes the protocol version information. This property contains four fields, each encoded as a packed unsigned integer: </p>
+<p/>
+
+<ul>
+  <li>Major Version Number</li>
+  <li>Minor Version Number</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.2.p.4">This document describes major version 4, minor version 3 of this protocol.  </p>
+<p id="rfc.section.5.5.2.p.5">The host MUST only use this property from NLI 0. Behavior when used from other NLIs is undefined.  </p>
+<h1 id="rfc.section.5.5.2.1"><a href="#rfc.section.5.5.2.1">5.5.2.1.</a> <a href="#major-version-number" id="major-version-number">Major Version Number</a></h1>
+<p id="rfc.section.5.5.2.1.p.1">The major version number is used to identify large and incompatible differences between protocol versions.  </p>
+<p id="rfc.section.5.5.2.1.p.2">The host MUST enter a FAULT state if it does not explicitly support the given major version number.  </p>
+<h1 id="rfc.section.5.5.2.2"><a href="#rfc.section.5.5.2.2">5.5.2.2.</a> <a href="#minor-version-number" id="minor-version-number">Minor Version Number</a></h1>
+<p id="rfc.section.5.5.2.2.p.1">The minor version number is used to identify small but otherwise compatible differences between protocol versions. A mismatch between the advertised minor version number and the minor version that is supported by the host SHOULD NOT be fatal to the operation of the host.  </p>
+<h1 id="rfc.section.5.5.3"><a href="#rfc.section.5.5.3">5.5.3.</a> <a href="#prop-ncp-version" id="prop-ncp-version">PROP 2: PROP_NCP_VERSION</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>U</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">NCP_VESION_STRING</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.3.p.2">Contains a string which describes the firmware currently running on the NCP. Encoded as a zero-terminated UTF-8 string.  </p>
+<p id="rfc.section.5.5.3.p.3">The format of the string is not strictly defined, but it is intended to present similarly to the "User-Agent" string from HTTP. The RECOMMENDED format of the string is as follows: </p>
+<pre>
+STACK-NAME/STACK-VERSION[BUILD_INFO][; OTHER_INFO]; BUILD_DATE_AND_TIME
+</pre>
+<p id="rfc.section.5.5.3.p.4">Examples: </p>
+<p/>
+
+<ul>
+  <li>
+    <samp>OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04</samp>
+  </li>
+  <li>
+    <samp>ConnectIP/2.0b125 s1 ALPHA; Sept 24 2015 20:49:19</samp>
+  </li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.3.p.6">The host MUST only use this property from NLI 0. Behavior when used from other NLIs is undefined.  </p>
+<h1 id="rfc.section.5.5.4"><a href="#rfc.section.5.5.4">5.5.4.</a> <a href="#prop-interface-type" id="prop-interface-type">PROP 3: PROP_INTERFACE_TYPE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Encoding: <samp>i</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1-3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">INTERFACE_TYPE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.4.p.2">This integer identifies what the network protocol for this NCP.  Currently defined values are: </p>
+<p/>
+
+<ul>
+  <li>0: Bootloader</li>
+  <li>2: ZigBee IP(TM)</li>
+  <li>3: Thread(R)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.4.p.4">The host MUST enter a FAULT state if it does not recognize the protocol given by the NCP.  </p>
+<h1 id="rfc.section.5.5.5"><a href="#rfc.section.5.5.5">5.5.5.</a> <a href="#prop-interface-vendor-id" id="prop-interface-vendor-id">PROP 4: PROP_INTERFACE_VENDOR_ID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Encoding: <samp>i</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1-3</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">VENDOR_ID</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.5.p.2">Vendor identifier.  </p>
+<h1 id="rfc.section.5.5.6"><a href="#rfc.section.5.5.6">5.5.6.</a> <a href="#prop-caps" id="prop-caps">PROP 5: PROP_CAPS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>A(i)</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1-3</th>
+      <th class="center">1-3</th>
+      <th class="center">...</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">CAP_1</td>
+      <td class="center">CAP_2</td>
+      <td class="center">...</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.6.p.2">Describes the supported capabilities of this NCP. Encoded as a list of packed unsigned integers.  </p>
+<p id="rfc.section.5.5.6.p.3">A capability is defined as a 21-bit integer that describes a subset of functionality which is supported by the NCP.  </p>
+<p id="rfc.section.5.5.6.p.4">Currently defined values are: </p>
+<p/>
+
+<ul>
+  <li>1: <samp>CAP_LOCK</samp></li>
+  <li>2: <samp>CAP_NET_SAVE</samp></li>
+  <li>3: <samp>CAP_HBO</samp>: Host Buffer Offload. See <a href="#feature-host-buffer-offload">Section 9</a>.</li>
+  <li>4: <samp>CAP_POWER_SAVE</samp></li>
+  <li>5: <samp>CAP_COUNTERS</samp></li>
+  <li>6: <samp>CAP_JAM_DETECT</samp>: Jamming detection. See <a href="#feature-jam-detect">Section 10</a></li>
+  <li>7: <samp>CAP_PEEK_POKE</samp>: PEEK/POKE debugging commands.</li>
+  <li>8: <samp>CAP_WRITABLE_RAW_STREAM</samp>: <samp>PROP_STREAM_RAW</samp> is writable.</li>
+  <li>9: <samp>CAP_GPIO</samp>: Support for GPIO access. See <a href="#feature-gpio-access">Section 11</a>.</li>
+  <li>10: <samp>CAP_TRNG</samp>: Support for true random number generation. See <a href="#feature-trng">Section 12</a>.</li>
+  <li>11: <samp>CAP_CMD_MULTI</samp>: Support for <samp>CMD_PROP_VALUE_MULTI_GET</samp> (<a href="#cmd-prop-value-multi-get">Section 4.13</a>), <samp>CMD_PROP_VALUE_MULTI_SET</samp> (<a href="#cmd-prop-value-multi-set">Section 4.14</a>, and <samp>CMD_PROP_VALUES_ARE</samp> (<a href="#cmd-prop-values-are">Section 4.15</a>).</li>
+  <li>12: <samp>CAP_UNSOL_UPDATE_FILTER</samp>: Support for <samp>PROP_UNSOL_UPDATE_FILTER</samp> (<a href="#prop-unsol-update-filter">Section 5.5.12</a>) and <samp>PROP_UNSOL_UPDATE_LIST</samp> (<a href="#prop-unsol-update-list">Section 5.5.13</a>).</li>
+  <li>16: <samp>CAP_802_15_4_2003</samp></li>
+  <li>17: <samp>CAP_802_15_4_2006</samp></li>
+  <li>18: <samp>CAP_802_15_4_2011</samp></li>
+  <li>21: <samp>CAP_802_15_4_PIB</samp></li>
+  <li>24: <samp>CAP_802_15_4_2450MHZ_OQPSK</samp></li>
+  <li>25: <samp>CAP_802_15_4_915MHZ_OQPSK</samp></li>
+  <li>26: <samp>CAP_802_15_4_868MHZ_OQPSK</samp></li>
+  <li>27: <samp>CAP_802_15_4_915MHZ_BPSK</samp></li>
+  <li>28: <samp>CAP_802_15_4_868MHZ_BPSK</samp></li>
+  <li>29: <samp>CAP_802_15_4_915MHZ_ASK</samp></li>
+  <li>30: <samp>CAP_802_15_4_868MHZ_ASK</samp></li>
+  <li>48: <samp>CAP_ROLE_ROUTER</samp></li>
+  <li>49: <samp>CAP_ROLE_SLEEPY</samp></li>
+  <li>52: <samp>CAP_NET_THREAD_1_0</samp></li>
+  <li>512: <samp>CAP_MAC_WHITELIST</samp></li>
+  <li>513: <samp>CAP_MAC_RAW</samp></li>
+  <li>514: <samp>CAP_OOB_STEERING_DATA</samp></li>
+  <li>1024: <samp>CAP_THREAD_COMMISSIONER</samp></li>
+  <li>1025: <samp>CAP_THREAD_TMF_PROXY</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.6.p.6">Additionally, future capability allocations SHALL be made from the following allocation plan: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Capability Range</th>
+      <th class="center">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">0 - 127</td>
+      <td class="center">Reserved for core capabilities</td>
+    </tr>
+    <tr>
+      <td class="center">128 - 15,359</td>
+      <td class="center">
+        <em>UNALLOCATED</em>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">15,360 - 16,383</td>
+      <td class="center">Vendor-specific</td>
+    </tr>
+    <tr>
+      <td class="center">16,384 - 1,999,999</td>
+      <td class="center">
+        <em>UNALLOCATED</em>
+      </td>
+    </tr>
+    <tr>
+      <td class="center">2,000,000 - 2,097,151</td>
+      <td class="center">Experimental use only</td>
+    </tr>
+  </tbody>
+</table>
+<h1 id="rfc.section.5.5.7"><a href="#rfc.section.5.5.7">5.5.7.</a> <a href="#prop-interface-count" id="prop-interface-count">PROP 6: PROP_INTERFACE_COUNT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>INTERFACE_COUNT</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.7.p.2">Describes the number of concurrent interfaces supported by this NCP.  Since the concurrent interface mechanism is still TBD, this value MUST always be one.  </p>
+<p id="rfc.section.5.5.7.p.3">This value is encoded as an unsigned 8-bit integer.  </p>
+<p id="rfc.section.5.5.7.p.4">The host MUST only use this property from NLI 0. Behavior when used from other NLIs is undefined.  </p>
+<h1 id="rfc.section.5.5.8"><a href="#rfc.section.5.5.8">5.5.8.</a> <a href="#prop-power-state" id="prop-power-state">PROP 7: PROP_POWER_STATE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">POWER_STATE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.8.p.2">Describes the current power state of the NCP. By writing to this property you can manage the lower state of the NCP. Enumeration is encoded as a single unsigned byte.  </p>
+<p id="rfc.section.5.5.8.p.3">Defined values are: </p>
+<p/>
+
+<ul>
+  <li>0: <samp>POWER_STATE_OFFLINE</samp>: NCP is physically powered off.  (Enumerated for completeness sake, not expected on the wire)</li>
+  <li>1: <samp>POWER_STATE_DEEP_SLEEP</samp>: Almost everything on the NCP is shut down, but can still be resumed via a command or interrupt.</li>
+  <li>2: <samp>POWER_STATE_STANDBY</samp>: NCP is in the lowest power state that can still be awoken by an event from the radio (e.g. waiting for alarm)</li>
+  <li>3: <samp>POWER_STATE_LOW_POWER</samp>: NCP is responsive (and possibly connected), but using less power. (e.g. "Sleepy" child node)</li>
+  <li>4: <samp>POWER_STATE_ONLINE</samp>: NCP is fully powered. (e.g. "Parent" node)</li>
+</ul>
+
+<p> </p>
+<p>
+  <a id="CREF2" class="info">[CREF2]<span class="info">RQ: We should consider reversing the numbering here so that 0 is `POWER_STATE_ONLINE`. We may also want to include some extra values between the defined values for future expansion, so that we can preserve the ordered relationship. --</span></a>
+</p>
+<h1 id="rfc.section.5.5.9"><a href="#rfc.section.5.5.9">5.5.9.</a> <a href="#prop-hwaddr" id="prop-hwaddr">PROP 8: PROP_HWADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only*</li>
+  <li>Packed-Encoding: <samp>E</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">8</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HWADDR</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.9.p.2">The static EUI64 address of the device, used as a serial number.  This value is read-only, but may be writable under certain vendor-defined circumstances.  </p>
+<h1 id="rfc.section.5.5.10"><a href="#rfc.section.5.5.10">5.5.10.</a> <a href="#prop-lock" id="prop-lock">PROP 9: PROP_LOCK</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">LOCK</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.10.p.2">Property lock. Used for grouping changes to several properties to take effect at once, or to temporarily prevent the automatic updating of property values. When this property is set, the execution of the NCP is effectively frozen until it is cleared.  </p>
+<p id="rfc.section.5.5.10.p.3">This property is only supported if the <samp>CAP_LOCK</samp> capability is present.  </p>
+<p id="rfc.section.5.5.10.p.4">Unlike most other properties, setting this property to true when the value of the property is already true MUST fail with a last status of <samp>STATUS_ALREADY</samp>.  </p>
+<h1 id="rfc.section.5.5.11"><a href="#rfc.section.5.5.11">5.5.11.</a> <a href="#prop-host-power-state" id="prop-host-power-state">PROP 10: PROP_HOST_POWER_STATE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+  <li>Default value: 4</li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>HOST_POWER_STATE</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.5.11.p.2">Describes the current power state of the <em>host</em>. This property is used by the host to inform the NCP when it has changed power states. The NCP can then use this state to determine which properties need asynchronous updates. Enumeration is encoded as a single unsigned byte. These states are defined in similar terms to <samp>PROP_POWER_STATE</samp> (<a href="#prop-power-state">Section 5.5.8</a>).  </p>
+<p id="rfc.section.5.5.11.p.3">Defined values are: </p>
+<p/>
+
+<ul>
+  <li>0: <samp>HOST_POWER_STATE_OFFLINE</samp>: Host is physically powered off and cannot be woken by the NCP. All asynchronous commands are squelched.</li>
+  <li>1: <samp>HOST_POWER_STATE_DEEP_SLEEP</samp>: The host is in a low power state where it can be woken by the NCP but will potentially require more than two seconds to become fully responsive. The NCP MUST avoid sending unnecessary property updates, such as child table updates or non-critical messages on the debug stream. If the NCP needs to wake the host for traffic, the NCP MUST first take action to wake the host. Once the NCP signals to the host that it should wake up, the NCP MUST wait for some activity from the host (indicating that it is fully awake) before sending frames.</li>
+  <li>2: <strong>RESERVED</strong>. This value MUST NOT be set by the host. If received by the NCP, the NCP SHOULD consider this as a synonym of <samp>HOST_POWER_STATE_DEEP_SLEEP</samp>.</li>
+  <li>3: <samp>HOST_POWER_STATE_LOW_POWER</samp>: The host is in a low power state where it can be immediately woken by the NCP. The NCP SHOULD avoid sending unnecessary property updates, such as child table updates or non-critical messages on the debug stream.</li>
+  <li>4: <samp>HOST_POWER_STATE_ONLINE</samp>: The host is awake and responsive. No special filtering is performed by the NCP on asynchronous updates.</li>
+  <li>All other values are <strong>RESERVED</strong>. They MUST NOT be set by the host. If received by the NCP, the NCP SHOULD consider the value as a synonym of <samp>HOST_POWER_STATE_LOW_POWER</samp>.</li>
+</ul>
+
+<p> </p>
+<p>
+  <a id="CREF3" class="info">[CREF3]<span class="info">RQ: We should consider reversing the numbering here so that 0 is `POWER_STATE_ONLINE`. We may also want to include some extra values between the defined values for future expansion, so that we can preserve the ordered relationship. --</span></a>
+</p>
+<p id="rfc.section.5.5.11.p.6">After setting this power state, any further commands from the host to the NCP will cause <samp>HOST_POWER_STATE</samp> to automatically revert to <samp>HOST_POWER_STATE_ONLINE</samp>.  </p>
+<p id="rfc.section.5.5.11.p.7">When the host is entering a low-power state, it should wait for the response from the NCP acknowledging the command (with <samp>CMD_VALUE_IS</samp>).  Once that acknowledgement is received the host may enter the low-power state.  </p>
+<p id="rfc.section.5.5.11.p.8">If the NCP has the <samp>CAP_UNSOL_UPDATE_FILTER</samp> capability, any unsolicited property updates masked by <samp>PROP_UNSOL_UPDATE_FILTER</samp> should be honored while the host indicates it is in a low-power state. After resuming to the <samp>HOST_POWER_STATE_ONLINE</samp> state, the value of <samp>PROP_UNSOL_UPDATE_FILTER</samp> MUST be unchanged from the value assigned prior to the host indicating it was entering a low-power state.  </p>
+<p id="rfc.section.5.5.11.p.9">The host MUST only use this property from NLI 0. Behavior when used from other NLIs is undefined.  </p>
+<h1 id="rfc.section.5.5.12"><a href="#rfc.section.5.5.12">5.5.12.</a> <a href="#prop-unsol-update-filter" id="prop-unsol-update-filter">PROP 4104: PROP_UNSOL_UPDATE_FILTER</a></h1>
+<p/>
+
+<ul>
+  <li>Required only if <samp>CAP_UNSOL_UPDATE_FILTER</samp> is set.</li>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(I)</samp></li>
+  <li>Default value: Empty.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.12.p.2">Contains a list of properties which are <em>excluded</em> from generating unsolicited value updates. This property MUST be empty after reset.  </p>
+<p id="rfc.section.5.5.12.p.3">In other words, the host may opt-out of unsolicited property updates for a specific property by adding that property id to this list.  </p>
+<p id="rfc.section.5.5.12.p.4">Hosts SHOULD NOT add properties to this list which are not present in <samp>PROP_UNSOL_UPDATE_LIST</samp>. If such properties are added, the NCP MUST ignore the unsupported properties.  </p>
+<p>
+  <a id="CREF4" class="info">[CREF4]<span class="info">RQ: The justification for the above behavior is to attempt to avoid possible future interop problems by explicitly making sure that unknown properties are ignored. Since unknown properties will obviously not be generating unsolicited updates, it seems fairly harmless. An implementation may print out a warning to the debug stream.  Note that the error is still detectable: If you VALUE\_SET unsupported properties, the resulting VALUE\_IS would contain only the supported properties of that set(since the unsupported properties would be ignored). If an implementation cares that much about getting this right then it needs to make sure that it checks PROP\_UNSOL\_UPDATE\_LIST first.  --</span></a>
+</p>
+<p id="rfc.section.5.5.12.p.6">Implementations of this property are only REQUIRED to support and use the following commands: </p>
+<p/>
+
+<ul>
+  <li><samp>CMD_PROP_VALUE_GET</samp> (<a href="#cmd-prop-value-get">Section 4.3</a>)</li>
+  <li><samp>CMD_PROP_VALUE_SET</samp> (<a href="#cmd-prop-value-set">Section 4.4</a>)</li>
+  <li><samp>CMD_PROP_VALUE_IS</samp> (<a href="#cmd-prop-value-is">Section 4.7</a>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.12.p.8">Implementations of this property MAY optionally support and use the following commands: </p>
+<p/>
+
+<ul>
+  <li><samp>CMD_PROP_VALUE_INSERT</samp> (<a href="#cmd-prop-value-insert">Section 4.5</a>)</li>
+  <li><samp>CMD_PROP_VALUE_REMOVE</samp> (<a href="#cmd-prop-value-remove">Section 4.6</a>)</li>
+  <li><samp>CMD_PROP_VALUE_INSERTED</samp> (<a href="#cmd-prop-value-inserted">Section 4.8</a>)</li>
+  <li><samp>CMD_PROP_VALUE_REMOVED</samp> (<a href="#cmd-prop-value-removed">Section 4.9</a>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.12.p.10">Host implementations which are aiming to maximize their compatability across different firmwre implementations SHOULD NOT assume the availability of the optional commands for this property.  </p>
+<p id="rfc.section.5.5.12.p.11">The value of this property SHALL be independent for each NLI.  </p>
+<h1 id="rfc.section.5.5.13"><a href="#rfc.section.5.5.13">5.5.13.</a> <a href="#prop-unsol-update-list" id="prop-unsol-update-list">PROP 4105: PROP_UNSOL_UPDATE_LIST</a></h1>
+<p/>
+
+<ul>
+  <li>Required only if <samp>CAP_UNSOL_UPDATE_FILTER</samp> is set.</li>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>A(I)</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.5.13.p.2">Contains a list of properties which are capable of generating unsolicited value updates. This list can be used when populating <samp>PROP_UNSOL_UPDATE_FILTER</samp> to disable all unsolicited property updates.  </p>
+<p id="rfc.section.5.5.13.p.3">This property is intended to effectively behave as a constant for a given NCP firmware.  </p>
+<p id="rfc.section.5.5.13.p.4">Note that not all properties that support unsolicited updates need to be listed here. Scan results, for example, are only generated due to direct action on the part of the host, so those properties MUST NOT not be included in this list.  </p>
+<p id="rfc.section.5.5.13.p.5">The value of this property MAY be different across available NLIs.  </p>
+<h1 id="rfc.section.5.6"><a href="#rfc.section.5.6">5.6.</a> <a href="#prop-stream" id="prop-stream">Stream Properties</a></h1>
+<h1 id="rfc.section.5.6.1"><a href="#rfc.section.5.6.1">5.6.1.</a> <a href="#prop-stream-debug" id="prop-stream-debug">PROP 112: PROP_STREAM_DEBUG</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only-Stream</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">UTF8_DATA</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.1.p.2">This property is a streaming property, meaning that you cannot explicitly fetch the value of this property. The stream provides human-readable debugging output which may be displayed in the host logs.  </p>
+<p id="rfc.section.5.6.1.p.3">The location of newline characters is not assumed by the host: it is the NCP's responsibility to insert newline characters where needed, just like with any other text stream.  </p>
+<p id="rfc.section.5.6.1.p.4">To receive the debugging stream, you wait for <samp>CMD_PROP_VALUE_IS</samp> commands for this property from the NCP.  </p>
+<h1 id="rfc.section.5.6.2"><a href="#rfc.section.5.6.2">5.6.2.</a> <a href="#prop-stream-raw" id="prop-stream-raw">PROP 113: PROP_STREAM_RAW</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write-Stream</li>
+  <li>Packed-Encoding: <samp>dD</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">FRAME_DATA_LEN</td>
+      <td class="center">FRAME_DATA</td>
+      <td class="center">FRAME_METADATA</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.2.p.2">This stream provides the capability of sending and receiving raw packets to and from the radio. The exact format of the frame metadata and data is dependent on the MAC and PHY being used.  </p>
+<p id="rfc.section.5.6.2.p.3">This property is a streaming property, meaning that you cannot explicitly fetch the value of this property. To receive traffic, you wait for <samp>CMD_PROP_VALUE_IS</samp> commands with this property id from the NCP.  </p>
+<p id="rfc.section.5.6.2.p.4">Implementations may OPTIONALLY support the ability to transmit arbitrary raw packets. Support for this feature is indicated by the presence of the <samp>CAP_WRITABLE_RAW_STREAM</samp> capability.  </p>
+<p id="rfc.section.5.6.2.p.5">If the capability <samp>CAP_WRITABLE_RAW_STREAM</samp> is set, then packets written to this stream with <samp>CMD_PROP_VALUE_SET</samp> will be sent out over the radio.  This allows the caller to use the radio directly, with the stack being implemented on the host instead of the NCP.  </p>
+<h1 id="rfc.section.5.6.2.1"><a href="#rfc.section.5.6.2.1">5.6.2.1.</a> <a href="#frame-metadata-format" id="frame-metadata-format">Frame Metadata Format</a></h1>
+<p id="rfc.section.5.6.2.1.p.1">Any data past the end of <samp>FRAME_DATA_LEN</samp> is considered metadata and is OPTIONAL. Frame metadata MAY be empty or partially specified. Partially specified metadata MUST be accepted. Default values are used for all unspecified fields.  </p>
+<p id="rfc.section.5.6.2.1.p.2">The same general format is used for <samp>PROP_STREAM_RAW</samp>, <samp>PROP_STREAM_NET</samp>, and <samp>PROP_STREAM_NET_INSECURE</samp>. It can be used for frames sent from the NCP to the host as well as frames sent from the host to the NCP.  </p>
+<p id="rfc.section.5.6.2.1.p.3">The frame metadata field consists of the following fields: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="left">Field</th>
+      <th class="left">Description</th>
+      <th class="left">Type</th>
+      <th class="center">Len</th>
+      <th class="center">Default</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="left">MD_POWER</td>
+      <td class="left">(dBm) RSSI/TX-Power</td>
+      <td class="left"><samp>c</samp> int8</td>
+      <td class="center">1</td>
+      <td class="center">-128</td>
+    </tr>
+    <tr>
+      <td class="left">MD_NOISE</td>
+      <td class="left">(dBm) Noise floor</td>
+      <td class="left"><samp>c</samp> int8</td>
+      <td class="center">1</td>
+      <td class="center">-128</td>
+    </tr>
+    <tr>
+      <td class="left">MD_FLAG</td>
+      <td class="left">Flags (defined below)</td>
+      <td class="left"><samp>S</samp> uint16</td>
+      <td class="center">2</td>
+      <td class="center"/>
+    </tr>
+    <tr>
+      <td class="left">MD_PHY</td>
+      <td class="left">PHY-specific data</td>
+      <td class="left"><samp>d</samp> data</td>
+      <td class="center">&gt;=2</td>
+      <td class="center"/>
+    </tr>
+    <tr>
+      <td class="left">MD_VEND</td>
+      <td class="left">Vendor-specific data</td>
+      <td class="left"><samp>d</samp> data</td>
+      <td class="center">&gt;=2</td>
+      <td class="center"/>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.2.1.p.4">The following fields are ignored by the NCP for packets sent to it from the host: </p>
+<p/>
+
+<ul>
+  <li>MD_NOISE</li>
+  <li>MD_FLAG</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.6.2.1.p.6">When specifying <samp>MD_POWER</samp> for a packet to be transmitted, the actual transmit power is never larger than the current value of <samp>PROP_PHY_TX_POWER</samp> (<a href="#prop-phy-tx-power">Section 5.7.6</a>). When left unspecified (or set to the value -128), an appropriate transmit power will be chosen by the NCP.  </p>
+<p id="rfc.section.5.6.2.1.p.7">The bit values in <samp>MD_FLAG</samp> are defined as follows: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Bit</th>
+      <th class="center">Mask</th>
+      <th class="left">Name</th>
+      <th class="left">Description if set</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">15</td>
+      <td class="center">0x0001</td>
+      <td class="left">MD_FLAG_TX</td>
+      <td class="left">Packet was transmitted, not received.</td>
+    </tr>
+    <tr>
+      <td class="center">13</td>
+      <td class="center">0x0004</td>
+      <td class="left">MD_FLAG_BAD_FCS</td>
+      <td class="left">Packet was received with bad FCS</td>
+    </tr>
+    <tr>
+      <td class="center">12</td>
+      <td class="center">0x0008</td>
+      <td class="left">MD_FLAG_DUPE</td>
+      <td class="left">Packet seems to be a duplicate</td>
+    </tr>
+    <tr>
+      <td class="center">0-11, 14</td>
+      <td class="center">0xFFF2</td>
+      <td class="left">MD_FLAG_RESERVED</td>
+      <td class="left">Flags reserved for future use.</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.2.1.p.8">The format of <samp>MD_PHY</samp> is specified by the PHY layer currently in use, and may contain information such as the channel, LQI, antenna, or other pertainent information.  </p>
+<h1 id="rfc.section.5.6.3"><a href="#rfc.section.5.6.3">5.6.3.</a> <a href="#prop-stream-net" id="prop-stream-net">PROP 114: PROP_STREAM_NET</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write-Stream</li>
+  <li>Packed-Encoding: <samp>dD</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">FRAME_DATA_LEN</td>
+      <td class="center">FRAME_DATA</td>
+      <td class="center">FRAME_METADATA</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.3.p.2">This stream provides the capability of sending and receiving data packets to and from the currently attached network. The exact format of the frame metadata and data is dependent on the network protocol being used.  </p>
+<p id="rfc.section.5.6.3.p.3">This property is a streaming property, meaning that you cannot explicitly fetch the value of this property. To receive traffic, you wait for <samp>CMD_PROP_VALUE_IS</samp> commands with this property id from the NCP.  </p>
+<p id="rfc.section.5.6.3.p.4">To send network packets, you call <samp>CMD_PROP_VALUE_SET</samp> on this property with the value of the packet.  </p>
+<p id="rfc.section.5.6.3.p.5">Any data past the end of <samp>FRAME_DATA_LEN</samp> is considered metadata, the format of which is described in <a href="#frame-metadata-format">Section 5.6.2.1</a>.  </p>
+<h1 id="rfc.section.5.6.4"><a href="#rfc.section.5.6.4">5.6.4.</a> <a href="#prop-stream-net-insecure" id="prop-stream-net-insecure">PROP 115: PROP_STREAM_NET_INSECURE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write-Stream</li>
+  <li>Packed-Encoding: <samp>dD</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">FRAME_DATA_LEN</td>
+      <td class="center">FRAME_DATA</td>
+      <td class="center">FRAME_METADATA</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.6.4.p.2">This stream provides the capability of sending and receiving unencrypted and unauthenticated data packets to and from nearby devices for the purposes of device commissioning. The exact format of the frame metadata and data is dependent on the network protocol being used.  </p>
+<p id="rfc.section.5.6.4.p.3">This property is a streaming property, meaning that you cannot explicitly fetch the value of this property. To receive traffic, you wait for <samp>CMD_PROP_VALUE_IS</samp> commands with this property id from the NCP.  </p>
+<p id="rfc.section.5.6.4.p.4">To send network packets, you call <samp>CMD_PROP_VALUE_SET</samp> on this property with the value of the packet.  </p>
+<p id="rfc.section.5.6.4.p.5">Any data past the end of <samp>FRAME_DATA_LEN</samp> is considered metadata, the format of which is described in <a href="#frame-metadata-format">Section 5.6.2.1</a>.  </p>
+<h1 id="rfc.section.5.7"><a href="#rfc.section.5.7">5.7.</a> <a href="#prop-phy" id="prop-phy">PHY Properties</a></h1>
+<h1 id="rfc.section.5.7.1"><a href="#rfc.section.5.7.1">5.7.1.</a> <a href="#prop-phy-enabled" id="prop-phy-enabled">PROP 32: PROP_PHY_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp> (bool8)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.1.p.2">Set to 1 if the PHY is enabled, set to 0 otherwise.  May be directly enabled to bypass higher-level packet processing in order to implement things like packet sniffers. This property can only be written if the <samp>SPINEL_CAP_MAC_RAW</samp> capability is present.  </p>
+<h1 id="rfc.section.5.7.2"><a href="#rfc.section.5.7.2">5.7.2.</a> <a href="#prop-phy-chan" id="prop-phy-chan">PROP 33: PROP_PHY_CHAN</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp> (uint8)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.2.p.2">Value is the current channel. Must be set to one of the values contained in <samp>PROP_PHY_CHAN_SUPPORTED</samp>.  </p>
+<h1 id="rfc.section.5.7.3"><a href="#rfc.section.5.7.3">5.7.3.</a> <a href="#prop-phy-chan-supported" id="prop-phy-chan-supported">PROP 34: PROP_PHY_CHAN_SUPPORTED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>A(C)</samp> (array of uint8)</li>
+  <li>Unit: List of channels</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.3.p.2">Value is a list of channel values that are supported by the hardware.  </p>
+<h1 id="rfc.section.5.7.4"><a href="#rfc.section.5.7.4">5.7.4.</a> <a href="#prop-phy-freq" id="prop-phy-freq">PROP 35: PROP_PHY_FREQ</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>L</samp> (uint32)</li>
+  <li>Unit: Kilohertz</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.4.p.2">Value is the radio frequency (in kilohertz) of the current channel.  </p>
+<h1 id="rfc.section.5.7.5"><a href="#rfc.section.5.7.5">5.7.5.</a> <a href="#prop-phy-cca-threshold" id="prop-phy-cca-threshold">PROP 36: PROP_PHY_CCA_THRESHOLD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>c</samp> (int8)</li>
+  <li>Unit: dBm</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.5.p.2">Value is the CCA (clear-channel assessment) threshold. Set to -128 to disable.  </p>
+<p id="rfc.section.5.7.5.p.3">When setting, the value will be rounded down to a value that is supported by the underlying radio hardware.  </p>
+<h1 id="rfc.section.5.7.6"><a href="#rfc.section.5.7.6">5.7.6.</a> <a href="#prop-phy-tx-power" id="prop-phy-tx-power">PROP 37: PROP_PHY_TX_POWER</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>c</samp> (int8)</li>
+  <li>Unit: dBm</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.6.p.2">Value is the transmit power of the radio.  </p>
+<p id="rfc.section.5.7.6.p.3">When setting, the value will be rounded down to a value that is supported by the underlying radio hardware.  </p>
+<h1 id="rfc.section.5.7.7"><a href="#rfc.section.5.7.7">5.7.7.</a> <a href="#prop-phy-rssi" id="prop-phy-rssi">PROP 38: PROP_PHY_RSSI</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>c</samp> (int8)</li>
+  <li>Unit: dBm</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.7.p.2">Value is the current RSSI (Received signal strength indication) from the radio. This value can be used in energy scans and for determining the ambient noise floor for the operating environment.  </p>
+<h1 id="rfc.section.5.7.8"><a href="#rfc.section.5.7.8">5.7.8.</a> <a href="#prop-phy-rx-sensitivity" id="prop-phy-rx-sensitivity">PROP 39: PROP_PHY_RX_SENSITIVITY</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>c</samp> (int8)</li>
+  <li>Unit: dBm</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.7.8.p.2">Value is the radio receive sensitivity. This value can be used as lower bound noise floor for link metrics computation.  </p>
+<h1 id="rfc.section.5.8"><a href="#rfc.section.5.8">5.8.</a> <a href="#prop-mac" id="prop-mac">MAC Properties</a></h1>
+<h1 id="rfc.section.5.8.1"><a href="#rfc.section.5.8.1">5.8.1.</a> <a href="#prop-mac-scan-state" id="prop-mac-scan-state">PROP 48: PROP_MAC_SCAN_STATE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+  <li>Unit: Enumeration</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.1.p.2">Possible Values: </p>
+<p/>
+
+<ul>
+  <li>0: <samp>SCAN_STATE_IDLE</samp></li>
+  <li>1: <samp>SCAN_STATE_BEACON</samp></li>
+  <li>2: <samp>SCAN_STATE_ENERGY</samp></li>
+  <li>3: <samp>SCAN_STATE_DISCOVER</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.1.p.4">Set to <samp>SCAN_STATE_BEACON</samp> to start an active scan.  Beacons will be emitted from <samp>PROP_MAC_SCAN_BEACON</samp>.  </p>
+<p id="rfc.section.5.8.1.p.5">Set to <samp>SCAN_STATE_ENERGY</samp> to start an energy scan.  Channel energy result will be reported by emissions of <samp>PROP_MAC_ENERGY_SCAN_RESULT</samp> (per channel).  </p>
+<p id="rfc.section.5.8.1.p.6">Set to <samp>SCAN_STATE_DISOVER</samp> to start a Thread MLE discovery scan operation. Discovery scan result will be emitted from <samp>PROP_MAC_SCAN_BEACON</samp>.  </p>
+<p id="rfc.section.5.8.1.p.7">Value switches to <samp>SCAN_STATE_IDLE</samp> when scan is complete.  </p>
+<h1 id="rfc.section.5.8.2"><a href="#rfc.section.5.8.2">5.8.2.</a> <a href="#prop-mac-scan-mask" id="prop-mac-scan-mask">PROP 49: PROP_MAC_SCAN_MASK</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(C)</samp></li>
+  <li>Unit: List of channels to scan</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.3"><a href="#rfc.section.5.8.3">5.8.3.</a> <a href="#prop-mac-scan-period" id="prop-mac-scan-period">PROP 50: PROP_MAC_SCAN_PERIOD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>S</samp> (uint16)</li>
+  <li>Unit: milliseconds per channel</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.4"><a href="#rfc.section.5.8.4">5.8.4.</a> <a href="#prop-mac-scan-beacon" id="prop-mac-scan-beacon">PROP 51: PROP_MAC_SCAN_BEACON</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only-Stream</li>
+  <li>Packed-Encoding: <samp>Ccdd</samp> (or <samp>Cct(ESSc)t(iCUdd)</samp>)</li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">CH</td>
+      <td class="center">RSSI</td>
+      <td class="center">MAC_LEN</td>
+      <td class="center">MAC_DATA</td>
+      <td class="center">NET_LEN</td>
+      <td class="center">NET_DATA</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.8.4.p.2">Scan beacons have two embedded structures which contain information about the MAC layer and the NET layer. Their format depends on the MAC and NET layer currently in use.  The format below is for an 802.15.4 MAC with Thread: </p>
+<p/>
+
+<ul>
+  <li><samp>C</samp>: Channel</li>
+  <li><samp>c</samp>: RSSI of the beacon</li>
+  <li><samp>t</samp>: MAC layer properties (802.15.4 layer shown below for convenience) <ul><li><samp>E</samp>: Long address</li><li><samp>S</samp>: Short address</li><li><samp>S</samp>: PAN-ID</li><li><samp>c</samp>: LQI</li></ul></li>
+  <li>NET layer properties (Standard net layer shown below for convenience) <ul><li><samp>i</samp>: Protocol Number</li><li><samp>C</samp>: Flags</li><li><samp>U</samp>: Network Name</li><li><samp>d</samp>: XPANID</li><li><samp>d</samp>: Steering data</li></ul></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.4.p.4">Extra parameters may be added to each of the structures in the future, so care should be taken to read the length that prepends each structure.  </p>
+<h1 id="rfc.section.5.8.5"><a href="#rfc.section.5.8.5">5.8.5.</a> <a href="#prop-mac-15-4-laddr" id="prop-mac-15-4-laddr">PROP 52: PROP_MAC_15_4_LADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>E</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.5.p.2">The 802.15.4 long address of this node.  </p>
+<p id="rfc.section.5.8.5.p.3">This property is only present on NCPs which implement 802.15.4 </p>
+<h1 id="rfc.section.5.8.6"><a href="#rfc.section.5.8.6">5.8.6.</a> <a href="#prop-mac-15-4-saddr" id="prop-mac-15-4-saddr">PROP 53: PROP_MAC_15_4_SADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.6.p.2">The 802.15.4 short address of this node.  </p>
+<p id="rfc.section.5.8.6.p.3">This property is only present on NCPs which implement 802.15.4 </p>
+<h1 id="rfc.section.5.8.7"><a href="#rfc.section.5.8.7">5.8.7.</a> <a href="#prop-mac-15-4-panid" id="prop-mac-15-4-panid">PROP 54: PROP_MAC_15_4_PANID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.7.p.2">The 802.15.4 PANID this node is associated with.  </p>
+<p id="rfc.section.5.8.7.p.3">This property is only present on NCPs which implement 802.15.4 </p>
+<h1 id="rfc.section.5.8.8"><a href="#rfc.section.5.8.8">5.8.8.</a> <a href="#prop-mac-raw-stream-enabled" id="prop-mac-raw-stream-enabled">PROP 55: PROP_MAC_RAW_STREAM_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.8.p.2">Set to true to enable raw MAC frames to be emitted from <samp>PROP_STREAM_RAW</samp>.  See <a href="#prop-stream-raw">Section 5.6.2</a>.  </p>
+<h1 id="rfc.section.5.8.9"><a href="#rfc.section.5.8.9">5.8.9.</a> <a href="#prop-mac-promiscuous-mode" id="prop-mac-promiscuous-mode">PROP 56: PROP_MAC_PROMISCUOUS_MODE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.9.p.2">Possible Values: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Id</th>
+      <th class="center">Name</th>
+      <th class="center">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">0</td>
+      <td class="center">
+        <samp>MAC_PROMISCUOUS_MODE_OFF</samp>
+      </td>
+      <td class="center">Normal MAC filtering is in place.</td>
+    </tr>
+    <tr>
+      <td class="center">1</td>
+      <td class="center">
+        <samp>MAC_PROMISCUOUS_MODE_NETWORK</samp>
+      </td>
+      <td class="center">All MAC packets matching network are passed up the stack.</td>
+    </tr>
+    <tr>
+      <td class="center">2</td>
+      <td class="center">
+        <samp>MAC_PROMISCUOUS_MODE_FULL</samp>
+      </td>
+      <td class="center">All decoded MAC packets are passed up the stack.</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.5.8.9.p.3">See <a href="#prop-stream-raw">Section 5.6.2</a>.  </p>
+<h1 id="rfc.section.5.8.10"><a href="#rfc.section.5.8.10">5.8.10.</a> <a href="#prop-mac-escan-result" id="prop-mac-escan-result">PROP 57: PROP_MAC_ENERGY_SCAN_RESULT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only-Stream</li>
+  <li>Packed-Encoding: <samp>Cc</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.10.p.2">This property is emitted during energy scan operation per scanned channel with following format: </p>
+<p/>
+
+<ul>
+  <li><samp>C</samp>: Channel</li>
+  <li><samp>c</samp>: RSSI (in dBm)</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.11"><a href="#rfc.section.5.8.11">5.8.11.</a> <a href="#prop-mac-whitelist" id="prop-mac-whitelist">PROP 4864: PROP_MAC_WHITELIST</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(T(Ec))</samp></li>
+  <li>Required capability: <samp>CAP_MAC_WHITELIST</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.11.p.2">Structure Parameters: </p>
+<p/>
+
+<ul>
+  <li><samp>E</samp>: EUI64 address of node</li>
+  <li><samp>c</samp>: Optional RSSI-override value. The value 127 indicates that the RSSI-override feature is not enabled for this address. If this value is omitted when setting or inserting, it is assumed to be 127. This parameter is ignored when removing.</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.12"><a href="#rfc.section.5.8.12">5.8.12.</a> <a href="#prop-mac-whitelist-enabled" id="prop-mac-whitelist-enabled">PROP 4865: PROP_MAC_WHITELIST_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Required capability: <samp>CAP_MAC_WHITELIST</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.13"><a href="#rfc.section.5.8.13">5.8.13.</a> <a href="#prop-mac-src-match-enabled" id="prop-mac-src-match-enabled">PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.13.p.2">Set to true to enable radio source matching or false to disable it. This property is only available if the <samp>SPINEL_CAP_MAC_RAW</samp> capability is present. The source match functionality is used by radios when generating ACKs. The short and extended address lists are used for settings the Frame Pending bit in the ACKs.  </p>
+<h1 id="rfc.section.5.8.14"><a href="#rfc.section.5.8.14">5.8.14.</a> <a href="#prop-mac-src-match-short-addresses" id="prop-mac-src-match-short-addresses">PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write</li>
+  <li>Packed-Encoding: <samp>A(S)</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.14.p.2">Configures the list of short addresses used for source matching. This property is only available if the <samp>SPINEL_CAP_MAC_RAW</samp> capability is present.  </p>
+<p id="rfc.section.5.8.14.p.3">Structure Parameters: </p>
+<p/>
+
+<ul>
+  <li><samp>S</samp>: Short address for hardware generated ACKs</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.15"><a href="#rfc.section.5.8.15">5.8.15.</a> <a href="#prop-mac-src-match-extended-addresses" id="prop-mac-src-match-extended-addresses">PROP 4869: SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write</li>
+  <li>Packed-Encoding: <samp>A(E)</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.15.p.2">Configures the list of extended addresses used for source matching. This property is only available if the <samp>SPINEL_CAP_MAC_RAW</samp> capability is present.  </p>
+<p id="rfc.section.5.8.15.p.3">Structure Parameters: </p>
+<p/>
+
+<ul>
+  <li><samp>E</samp>: EUI64 address for hardware generated ACKs</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.16"><a href="#rfc.section.5.8.16">5.8.16.</a> <a href="#prop-mac-blacklist" id="prop-mac-blacklist">PROP 4870: PROP_MAC_BLACKLIST</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(T(E))</samp></li>
+  <li>Required capability: <samp>CAP_MAC_WHITELIST</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.8.16.p.2">Structure Parameters: </p>
+<p/>
+
+<ul>
+  <li><samp>E</samp>: EUI64 address of node</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.8.17"><a href="#rfc.section.5.8.17">5.8.17.</a> <a href="#prop-mac-blacklist-enabled" id="prop-mac-blacklist-enabled">PROP 4871: PROP_MAC_BLACKLIST_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Required capability: <samp>CAP_MAC_WHITELIST</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9"><a href="#rfc.section.5.9">5.9.</a> <a href="#prop-net" id="prop-net">NET Properties</a></h1>
+<h1 id="rfc.section.5.9.1"><a href="#rfc.section.5.9.1">5.9.1.</a> <a href="#prop-net-saved" id="prop-net-saved">PROP 64: PROP_NET_SAVED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.9.1.p.2">Returns true if there is a network state stored/saved.  </p>
+<h1 id="rfc.section.5.9.2"><a href="#rfc.section.5.9.2">5.9.2.</a> <a href="#prop-net-if-up" id="prop-net-if-up">PROP 65: PROP_NET_IF_UP</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.9.2.p.2">Network interface up/down status. Non-zero (set to 1) indicates up, zero indicates down.  </p>
+<h1 id="rfc.section.5.9.3"><a href="#rfc.section.5.9.3">5.9.3.</a> <a href="#prop-net-stack-up" id="prop-net-stack-up">PROP 66: PROP_NET_STACK_UP</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Unit: Enumeration</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.9.3.p.2">Thread stack operational status. Non-zero (set to 1) indicates up, zero indicates down.  </p>
+<h1 id="rfc.section.5.9.4"><a href="#rfc.section.5.9.4">5.9.4.</a> <a href="#prop-net-role" id="prop-net-role">PROP 67: PROP_NET_ROLE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+  <li>Unit: Enumeration</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.9.4.p.2">Values: </p>
+<p/>
+
+<ul>
+  <li>0: <samp>NET_ROLE_DETACHED</samp></li>
+  <li>1: <samp>NET_ROLE_CHILD</samp></li>
+  <li>2: <samp>NET_ROLE_ROUTER</samp></li>
+  <li>3: <samp>NET_ROLE_LEADER</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.5"><a href="#rfc.section.5.9.5">5.9.5.</a> <a href="#prop-net-network-name" id="prop-net-network-name">PROP 68: PROP_NET_NETWORK_NAME</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>U</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.6"><a href="#rfc.section.5.9.6">5.9.6.</a> <a href="#prop-net-xpanid" id="prop-net-xpanid">PROP 69: PROP_NET_XPANID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.7"><a href="#rfc.section.5.9.7">5.9.7.</a> <a href="#prop-net-master-key" id="prop-net-master-key">PROP 70: PROP_NET_MASTER_KEY</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.8"><a href="#rfc.section.5.9.8">5.9.8.</a> <a href="#prop-net-key-sequence-counter" id="prop-net-key-sequence-counter">PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.9"><a href="#rfc.section.5.9.9">5.9.9.</a> <a href="#prop-net-partition-id" id="prop-net-partition-id">PROP 72: PROP_NET_PARTITION_ID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.9.9.p.2">The partition ID of the partition that this node is a member of.  </p>
+<h1 id="rfc.section.5.9.10"><a href="#rfc.section.5.9.10">5.9.10.</a> <a href="#prop-net-require-join-existing" id="prop-net-require-join-existing">PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.11"><a href="#rfc.section.5.9.11">5.9.11.</a> <a href="#prop-net-key-swtich-guardtime" id="prop-net-key-swtich-guardtime">PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.9.12"><a href="#rfc.section.5.9.12">5.9.12.</a> <a href="#prop-net-pskc" id="prop-net-pskc">PROP 75: PROP_NET_PSKC</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.10"><a href="#rfc.section.5.10">5.10.</a> <a href="#prop-ipv6" id="prop-ipv6">IPv6 Properties</a></h1>
+<h1 id="rfc.section.5.10.1"><a href="#rfc.section.5.10.1">5.10.1.</a> <a href="#prop-ipv6-ll-addr" id="prop-ipv6-ll-addr">PROP 96: PROP_IPV6_LL_ADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>6</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.10.1.p.2">IPv6 Address </p>
+<h1 id="rfc.section.5.10.2"><a href="#rfc.section.5.10.2">5.10.2.</a> <a href="#prop-ipv6-ml-addr" id="prop-ipv6-ml-addr">PROP 97: PROP_IPV6_ML_ADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>6</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.10.2.p.2">IPv6 Address + Prefix Length </p>
+<h1 id="rfc.section.5.10.3"><a href="#rfc.section.5.10.3">5.10.3.</a> <a href="#prop-ipv6-ml-prefix" id="prop-ipv6-ml-prefix">PROP 98: PROP_IPV6_ML_PREFIX</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>6C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.10.3.p.2">IPv6 Prefix + Prefix Length </p>
+<h1 id="rfc.section.5.10.4"><a href="#rfc.section.5.10.4">5.10.4.</a> <a href="#prop-ipv6-address-table" id="prop-ipv6-address-table">PROP 99: PROP_IPV6_ADDRESS_TABLE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(t(6CLLC))</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.10.4.p.2">Array of structures containing: </p>
+<p/>
+
+<ul>
+  <li><samp>6</samp>: IPv6 Address</li>
+  <li><samp>C</samp>: Network Prefix Length</li>
+  <li><samp>L</samp>: Valid Lifetime</li>
+  <li><samp>L</samp>: Preferred Lifetime</li>
+  <li><samp>C</samp>: Flags</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.5.10.5"><a href="#rfc.section.5.10.5">5.10.5.</a> <a href="#prop-101-propipv6icmppingoffload" id="prop-101-propipv6icmppingoffload">PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.10.5.p.2">Allow the NCP to directly respond to ICMP ping requests. If this is turned on, ping request ICMP packets will not be passed to the host.  </p>
+<p id="rfc.section.5.10.5.p.3">Default value is <samp>false</samp>.  </p>
+<h1 id="rfc.section.5.11"><a href="#rfc.section.5.11">5.11.</a> <a href="#prop-debug" id="prop-debug">Debug Properties</a></h1>
+<h1 id="rfc.section.5.11.1"><a href="#rfc.section.5.11.1">5.11.1.</a> <a href="#prop-debug-test-assert" id="prop-debug-test-assert">PROP 16384: PROP_DEBUG_TEST_ASSERT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.11.1.p.2">Reading this property will cause an assert on the NCP. This is intended for testing the assert functionality of underlying platform/NCP. Assert should ideally cause the NCP to reset, but if <samp>assert</samp> is not supported or disabled boolean value of <samp>false</samp> is returned in response.  </p>
+<h1 id="rfc.section.5.11.2"><a href="#rfc.section.5.11.2">5.11.2.</a> <a href="#prop-debug-ncp-log-level" id="prop-debug-ncp-log-level">PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.11.2.p.2">Provides access to the NCP log level. Currently defined values are (which follows the RFC 5424): </p>
+<p/>
+
+<ul>
+  <li>0: Emergency (emerg).</li>
+  <li>1: Alert (alert).</li>
+  <li>2: Critical (crit).</li>
+  <li>3: Error (err).</li>
+  <li>4: Warning (warn).</li>
+  <li>5: Notice (notice).</li>
+  <li>6: Information (info).</li>
+  <li>7: Debug (debug).</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.5.11.2.p.4">If the NCP supports dynamic log level control, setting this property changes the log level accordingly. Getting the value returns the current log level.  If the dynamic log level control is not supported, setting this property returns a <samp>PROP_LAST_STATUS</samp> with <samp>STATUS_INVALID_COMMAND_FOR_PROP</samp>.  </p>
+<h1 id="rfc.section.6"><a href="#rfc.section.6">6.</a> <a href="#status-codes" id="status-codes">Status Codes</a></h1>
+<p id="rfc.section.6.p.1">Status codes are sent from the NCP to the host via <samp>PROP_LAST_STATUS</samp> using the <samp>CMD_VALUE_IS</samp> command to indicate the return status of a previous command. As with any response, the TID field of the FLAG byte is used to correlate the response with the request.  </p>
+<p id="rfc.section.6.p.2">Note that most successfully executed commands do not indicate a last status of <samp>STATUS_OK</samp>. The usual way the NCP indicates a successful command is to mirror the property change back to the host. For example, if you do a <samp>CMD_VALUE_SET</samp> on <samp>PROP_PHY_ENABLED</samp>, the NCP would indicate success by responding with a <samp>CMD_VALUE_IS</samp> for <samp>PROP_PHY_ENABLED</samp>. If the command failed, <samp>PROP_LAST_STATUS</samp> would be emitted instead.  </p>
+<p id="rfc.section.6.p.3">See <a href="#prop-last-status">Section 5.5.1</a> for more information on <samp>PROP_LAST_STATUS</samp>.  </p>
+<p/>
+
+<ul>
+  <li>0: <samp>STATUS_OK</samp>: Operation has completed successfully.</li>
+  <li>1: <samp>STATUS_FAILURE</samp>: Operation has failed for some undefined reason.</li>
+  <li>2: <samp>STATUS_UNIMPLEMENTED</samp>: The given operation has not been implemented.</li>
+  <li>3: <samp>STATUS_INVALID_ARGUMENT</samp>: An argument to the given operation is invalid.</li>
+  <li>4: <samp>STATUS_INVALID_STATE</samp> : The given operation is invalid for the current state of the device.</li>
+  <li>5: <samp>STATUS_INVALID_COMMAND</samp>: The given command is not recognized.</li>
+  <li>6: <samp>STATUS_INVALID_INTERFACE</samp>: The given Spinel interface is not supported.</li>
+  <li>7: <samp>STATUS_INTERNAL_ERROR</samp>: An internal runtime error has occurred.</li>
+  <li>8: <samp>STATUS_SECURITY_ERROR</samp>: A security or authentication error has occurred.</li>
+  <li>9: <samp>STATUS_PARSE_ERROR</samp>: An error has occurred while parsing the command.</li>
+  <li>10: <samp>STATUS_IN_PROGRESS</samp>: The operation is in progress and will be completed asynchronously.</li>
+  <li>11: <samp>STATUS_NOMEM</samp>: The operation has been prevented due to memory pressure.</li>
+  <li>12: <samp>STATUS_BUSY</samp>: The device is currently performing a mutually exclusive operation.</li>
+  <li>13: <samp>STATUS_PROP_NOT_FOUND</samp>: The given property is not recognized.</li>
+  <li>14: <samp>STATUS_PACKET_DROPPED</samp>: The packet was dropped.</li>
+  <li>15: <samp>STATUS_EMPTY</samp>: The result of the operation is empty.</li>
+  <li>16: <samp>STATUS_CMD_TOO_BIG</samp>: The command was too large to fit in the internal buffer.</li>
+  <li>17: <samp>STATUS_NO_ACK</samp>: The packet was not acknowledged.</li>
+  <li>18: <samp>STATUS_CCA_FAILURE</samp>: The packet was not sent due to a CCA failure.</li>
+  <li>19: <samp>STATUS_ALREADY</samp>: The operation is already in progress or the property was already set to the given value.</li>
+  <li>20: <samp>STATUS_ITEM_NOT_FOUND</samp>: The given item could not be found in the property.</li>
+  <li>21: <samp>STATUS_INVALID_COMMAND_FOR_PROP</samp>: The given command cannot be performed on this property.</li>
+  <li>22-111: RESERVED</li>
+  <li>112-127: Reset Causes <ul><li>112: <samp>STATUS_RESET_POWER_ON</samp></li><li>113: <samp>STATUS_RESET_EXTERNAL</samp></li><li>114: <samp>STATUS_RESET_SOFTWARE</samp></li><li>115: <samp>STATUS_RESET_FAULT</samp></li><li>116: <samp>STATUS_RESET_CRASH</samp></li><li>117: <samp>STATUS_RESET_ASSERT</samp></li><li>118: <samp>STATUS_RESET_OTHER</samp></li><li>119: <samp>STATUS_RESET_UNKNOWN</samp></li><li>120: <samp>STATUS_RESET_WATCHDOG</samp></li><li>121-127: RESERVED-RESET-CODES</li></ul></li>
+  <li>128 - 15,359: UNALLOCATED</li>
+  <li>15,360 - 16,383: Vendor-specific</li>
+  <li>16,384 - 1,999,999: UNALLOCATED</li>
+  <li>2,000,000 - 2,097,151: Experimental Use Only (MUST NEVER be used in production!)</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7"><a href="#rfc.section.7">7.</a> <a href="#tech-thread" id="tech-thread">Technology: Thread(R)</a></h1>
+<p id="rfc.section.7.p.1">This section describes all of the properties and semantics required for managing a Thread(R) NCP.  </p>
+<p id="rfc.section.7.p.2">Thread(R) NCPs have the following requirements: </p>
+<p/>
+
+<ul>
+  <li>The property <samp>PROP_INTERFACE_TYPE</samp> must be 3.</li>
+  <li>The non-optional properties in the following sections MUST be implemented: CORE, PHY, MAC, NET, and IPV6.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.p.4">All serious implementations of an NCP SHOULD also support the network save feature (See <a href="#feature-network-save">Section 8</a>).  </p>
+<h1 id="rfc.section.7.1"><a href="#rfc.section.7.1">7.1.</a> <a href="#thread-caps" id="thread-caps">Capabilities</a></h1>
+<p id="rfc.section.7.1.p.1">The Thread(R) technology defines the following capabilities: </p>
+<p/>
+
+<ul>
+  <li><samp>CAP_NET_THREAD_1_0</samp> - Indicates that the NCP implements v1.0 of the Thread(R) standard.</li>
+  <li><samp>CAP_NET_THREAD_1_1</samp> - Indicates that the NCP implements v1.1 of the Thread(R) standard.</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2"><a href="#rfc.section.7.2">7.2.</a> <a href="#thread-properties" id="thread-properties">Properties</a></h1>
+<p id="rfc.section.7.2.p.1">Properties for Thread(R) are allocated out of the <samp>Tech</samp> property section (see <a href="#property-sections">Section 5.4</a>).  </p>
+<h1 id="rfc.section.7.2.1"><a href="#rfc.section.7.2.1">7.2.1.</a> <a href="#prop-80-propthreadleaderaddr" id="prop-80-propthreadleaderaddr">PROP 80: PROP_THREAD_LEADER_ADDR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>6</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.1.p.2">The IPv6 address of the leader. (Note: May change to long and short address of leader) </p>
+<h1 id="rfc.section.7.2.2"><a href="#rfc.section.7.2.2">7.2.2.</a> <a href="#prop-81-propthreadparent" id="prop-81-propthreadparent">PROP 81: PROP_THREAD_PARENT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>ES</samp></li>
+  <li>LADDR, SADDR</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.2.p.2">The long address and short address of the parent of this node.  </p>
+<h1 id="rfc.section.7.2.3"><a href="#rfc.section.7.2.3">7.2.3.</a> <a href="#prop-82-propthreadchildtable" id="prop-82-propthreadchildtable">PROP 82: PROP_THREAD_CHILD_TABLE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>A(t(ES))</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.3.p.2">Table containing the long and short addresses of all the children of this node.  </p>
+<h1 id="rfc.section.7.2.4"><a href="#rfc.section.7.2.4">7.2.4.</a> <a href="#prop-83-propthreadleaderrid" id="prop-83-propthreadleaderrid">PROP 83: PROP_THREAD_LEADER_RID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.4.p.2">The router-id of the current leader.  </p>
+<h1 id="rfc.section.7.2.5"><a href="#rfc.section.7.2.5">7.2.5.</a> <a href="#prop-84-propthreadleaderweight" id="prop-84-propthreadleaderweight">PROP 84: PROP_THREAD_LEADER_WEIGHT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.5.p.2">The leader weight of the current leader.  </p>
+<h1 id="rfc.section.7.2.6"><a href="#rfc.section.7.2.6">7.2.6.</a> <a href="#prop-85-propthreadlocalleaderweight" id="prop-85-propthreadlocalleaderweight">PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.6.p.2">The leader weight for this node.  </p>
+<h1 id="rfc.section.7.2.7"><a href="#rfc.section.7.2.7">7.2.7.</a> <a href="#prop-86-propthreadnetworkdata" id="prop-86-propthreadnetworkdata">PROP 86: PROP_THREAD_NETWORK_DATA</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.7.p.2">The local network data.  </p>
+<h1 id="rfc.section.7.2.8"><a href="#rfc.section.7.2.8">7.2.8.</a> <a href="#prop-87-propthreadnetworkdataversion" id="prop-87-propthreadnetworkdataversion">PROP 87: PROP_THREAD_NETWORK_DATA_VERSION</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.9"><a href="#rfc.section.7.2.9">7.2.9.</a> <a href="#prop-88-propthreadstablenetworkdata" id="prop-88-propthreadstablenetworkdata">PROP 88: PROP_THREAD_STABLE_NETWORK_DATA</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.9.p.2">The local stable network data.  </p>
+<h1 id="rfc.section.7.2.10"><a href="#rfc.section.7.2.10">7.2.10.</a> <a href="#prop-89-propthreadstablenetworkdataversion" id="prop-89-propthreadstablenetworkdataversion">PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.11"><a href="#rfc.section.7.2.11">7.2.11.</a> <a href="#prop-90-propthreadonmeshnets" id="prop-90-propthreadonmeshnets">PROP 90: PROP_THREAD_ON_MESH_NETS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(t(6CbCb))</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.11.p.2">Data per item is: </p>
+<p/>
+
+<ul>
+  <li><samp>6</samp>: IPv6 Prefix</li>
+  <li><samp>C</samp>: Prefix length in bits</li>
+  <li><samp>b</samp>: Stable flag</li>
+  <li><samp>C</samp>: TLV flags</li>
+  <li><samp>b</samp>: "Is defined locally" flag. Set if this network was locally defined. Assumed to be true for set, insert and replace. Clear if the on mesh network was defined by another node.</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.12"><a href="#rfc.section.7.2.12">7.2.12.</a> <a href="#prop-91-propthreadoffmeshroutes" id="prop-91-propthreadoffmeshroutes">PROP 91: PROP_THREAD_OFF_MESH_ROUTES</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(t(6CbCbb))</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.12.p.2">Data per item is: </p>
+<p/>
+
+<ul>
+  <li><samp>6</samp>: Route Prefix</li>
+  <li><samp>C</samp>: Prefix length in bits</li>
+  <li><samp>b</samp>: Stable flag</li>
+  <li><samp>C</samp>: Route preference flags</li>
+  <li><samp>b</samp>: "Is defined locally" flag. Set if this route info was locally defined as part of local network data. Assumed to be true for set, insert and replace. Clear if the route is part of partition's network data.</li>
+  <li><samp>b</samp>: "Next hop is this device" flag. Set if the next hop for the route is this device itself (i.e., route was added by this device) This value is ignored when adding an external route. For any added route the next hop is this device.</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.13"><a href="#rfc.section.7.2.13">7.2.13.</a> <a href="#prop-92-propthreadassistingports" id="prop-92-propthreadassistingports">PROP 92: PROP_THREAD_ASSISTING_PORTS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>A(S)</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.14"><a href="#rfc.section.7.2.14">7.2.14.</a> <a href="#prop-93-propthreadallowlocalnetdatachange" id="prop-93-propthreadallowlocalnetdatachange">PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.14.p.2">Set to true before changing local net data. Set to false when finished.  This allows changes to be aggregated into single events.  </p>
+<h1 id="rfc.section.7.2.15"><a href="#rfc.section.7.2.15">7.2.15.</a> <a href="#prop-94-propthreadmode" id="prop-94-propthreadmode">PROP 94: PROP_THREAD_MODE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.15.p.2">This property contains the value of the mode TLV for this node. The meaning of the bits in this bitfield are defined by section 4.5.2 of the Thread(R) specification.  </p>
+<h1 id="rfc.section.7.2.16"><a href="#rfc.section.7.2.16">7.2.16.</a> <a href="#prop-5376-propthreadchildtimeout" id="prop-5376-propthreadchildtimeout">PROP 5376: PROP_THREAD_CHILD_TIMEOUT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.16.p.2">Used when operating in the Child role.  </p>
+<h1 id="rfc.section.7.2.17"><a href="#rfc.section.7.2.17">7.2.17.</a> <a href="#prop-5377-propthreadrloc16" id="prop-5377-propthreadrloc16">PROP 5377: PROP_THREAD_RLOC16</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.18"><a href="#rfc.section.7.2.18">7.2.18.</a> <a href="#prop-5378-propthreadrouterupgradethreshold" id="prop-5378-propthreadrouterupgradethreshold">PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.19"><a href="#rfc.section.7.2.19">7.2.19.</a> <a href="#prop-5379-propthreadcontextreusedelay" id="prop-5379-propthreadcontextreusedelay">PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.20"><a href="#rfc.section.7.2.20">7.2.20.</a> <a href="#prop-5380-propthreadnetworkidtimeout" id="prop-5380-propthreadnetworkidtimeout">PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.20.p.2">Allows you to get or set the Thread(R) <samp>NETWORK_ID_TIMEOUT</samp> constant, as defined by the Thread(R) specification.  </p>
+<h1 id="rfc.section.7.2.21"><a href="#rfc.section.7.2.21">7.2.21.</a> <a href="#prop-5381-propthreadactiverouterids" id="prop-5381-propthreadactiverouterids">PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write/Write-Only</li>
+  <li>Packed-Encoding: <samp>A(C)</samp> (List of active thread router ids)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.21.p.2">Note that some implementations may not support <samp>CMD_GET_VALUE</samp> router ids, but may support <samp>CMD_REMOVE_VALUE</samp> when the node is a leader.  </p>
+<h1 id="rfc.section.7.2.22"><a href="#rfc.section.7.2.22">7.2.22.</a> <a href="#prop-5382-propthreadrloc16debugpassthru" id="prop-5382-propthreadrloc16debugpassthru">PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.22.p.2">Allow the HOST to directly observe all IPv6 packets received by the NCP, including ones sent to the RLOC16 address.  </p>
+<p id="rfc.section.7.2.22.p.3">Default value is <samp>false</samp>.  </p>
+<h1 id="rfc.section.7.2.23"><a href="#rfc.section.7.2.23">7.2.23.</a> <a href="#prop-5383-propthreadrouterroleenabled" id="prop-5383-propthreadrouterroleenabled">PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.23.p.2">Allow the HOST to indicate whether or not the router role is enabled.  If current role is a router, setting this property to <samp>false</samp> starts a re-attach process as an end-device.  </p>
+<h1 id="rfc.section.7.2.24"><a href="#rfc.section.7.2.24">7.2.24.</a> <a href="#prop-5384-propthreadrouterdowngradethreshold" id="prop-5384-propthreadrouterdowngradethreshold">PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.25"><a href="#rfc.section.7.2.25">7.2.25.</a> <a href="#prop-5385-propthreadrouterselectionjitter" id="prop-5385-propthreadrouterselectionjitter">PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.25.p.2">Specifies the self imposed random delay in seconds a REED waits before registering to become an Active Router.  </p>
+<h1 id="rfc.section.7.2.26"><a href="#rfc.section.7.2.26">7.2.26.</a> <a href="#prop-5386-propthreadpreferredrouterid" id="prop-5386-propthreadpreferredrouterid">PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write-Only</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.26.p.2">Specifies the preferred Router Id. Upon becoming a router/leader the node attempts to use this Router Id. If the preferred Router Id is not set or if it can not be used, a randomly generated router id is picked. This property can be set only when the device role is either detached or disabled.  </p>
+<h1 id="rfc.section.7.2.27"><a href="#rfc.section.7.2.27">7.2.27.</a> <a href="#prop-5387-propthreadneighbortable" id="prop-5387-propthreadneighbortable">PROP 5387: PROP_THREAD_NEIGHBOR_TABLE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>A(t(ESLCcCbLL))</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.27.p.2">Data per item is: </p>
+<p/>
+
+<ul>
+  <li><samp>E</samp>: Extended/long address</li>
+  <li><samp>S</samp>: RLOC16</li>
+  <li><samp>L</samp>: Age</li>
+  <li><samp>C</samp>: Link Quality In</li>
+  <li><samp>c</samp>: Average RSS</li>
+  <li><samp>C</samp>: Mode (bit-flags)</li>
+  <li><samp>b</samp>: <samp>true</samp> if neighbor is a child, <samp>false</samp> otherwise.</li>
+  <li><samp>L</samp>: Link Frame Counter</li>
+  <li><samp>L</samp>: MLE Frame Counter</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.7.2.28"><a href="#rfc.section.7.2.28">7.2.28.</a> <a href="#prop-5388-propthreadchildcountmax" id="prop-5388-propthreadchildcountmax">PROP 5388: PROP_THREAD_CHILD_COUNT_MAX</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>C</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.28.p.2">Specifies the maximum number of children currently allowed.  This parameter can only be set when Thread(R) protocol operation has been stopped.  </p>
+<h1 id="rfc.section.7.2.29"><a href="#rfc.section.7.2.29">7.2.29.</a> <a href="#prop-5389-propthreadleadernetworkdata" id="prop-5389-propthreadleadernetworkdata">PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.29.p.2">The leader network data.  </p>
+<h1 id="rfc.section.7.2.30"><a href="#rfc.section.7.2.30">7.2.30.</a> <a href="#prop-5390-propthreadstableleadernetworkdata" id="prop-5390-propthreadstableleadernetworkdata">PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>D</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.30.p.2">The stable leader network data.  </p>
+<h1 id="rfc.section.7.2.31"><a href="#rfc.section.7.2.31">7.2.31.</a> <a href="#prop-thread-joiners" id="prop-thread-joiners">PROP 5391: PROP_THREAD_JOINERS</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Insert/Remove Only (optionally Read-Write)</li>
+  <li>Packed-Encoding: <samp>A(t(ULE))</samp></li>
+  <li>Required capability: <samp>CAP_THREAD_COMMISSIONER</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.31.p.2">Data per item is: </p>
+<p/>
+
+<ul>
+  <li><samp>U</samp>: PSKd</li>
+  <li><samp>L</samp>: Timeout in seconds</li>
+  <li><samp>E</samp>: Extended/long address (optional)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.31.p.4">Passess Pre-Shared Key for the Device to the NCP in the commissioning process.  When the Extended address is ommited all Devices which provided a valid PSKd are allowed to join the Thread(R) Network.  </p>
+<h1 id="rfc.section.7.2.32"><a href="#rfc.section.7.2.32">7.2.32.</a> <a href="#prop-thread-commissioner-enabled" id="prop-thread-commissioner-enabled">PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write only (optionally Read-Write)</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Required capability: <samp>CAP_THREAD_COMMISSIONER</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.32.p.2">Set to true to enable the native commissioner. It is mandatory before adding the joiner to the network.  </p>
+<h1 id="rfc.section.7.2.33"><a href="#rfc.section.7.2.33">7.2.33.</a> <a href="#prop-thread-tmf-proxy-enabled" id="prop-thread-tmf-proxy-enabled">PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Required capability: <samp>CAP_THREAD_TMF_PROXY</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.33.p.2">Set to true to enable the TMF proxy.  </p>
+<h1 id="rfc.section.7.2.34"><a href="#rfc.section.7.2.34">7.2.34.</a> <a href="#prop-thread-tmf-proxy-stream" id="prop-thread-tmf-proxy-stream">PROP 5394: PROP_THREAD_TMF_PROXY_STREAM</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write-Stream</li>
+  <li>Packed-Encoding: <samp>dSS</samp></li>
+  <li>Required capability: <samp>CAP_THREAD_TMF_PROXY</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.34.p.2">Data per item is: </p>
+<p/>
+
+<ul>
+  <li><samp>d</samp>: CoAP frame</li>
+  <li><samp>S</samp>: source/destination RLOC/ALOC</li>
+  <li><samp>S</samp>: source/destination port</li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octects:</th>
+      <th class="center">2</th>
+      <th class="center">n</th>
+      <th class="center">2</th>
+      <th class="center">2</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">Length</td>
+      <td class="center">CoAP</td>
+      <td class="center">locator</td>
+      <td class="center">port</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.7.2.34.p.4">This property allows the host to send and receive TMF messages from the NCP's RLOC address and support Thread-specific border router functions.  </p>
+<h1 id="rfc.section.7.2.35"><a href="#rfc.section.7.2.35">7.2.35.</a> <a href="#prop-thread-discovery-scan-joiner-flag" id="prop-thread-discovery-scan-joiner-flag">PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding:: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.35.p.2">This property specifies the value used in Thread(R) MLE Discovery Request TLV during discovery scan operation. Default value is <samp>false</samp>.  </p>
+<h1 id="rfc.section.7.2.36"><a href="#rfc.section.7.2.36">7.2.36.</a> <a href="#prop-thread-discovery-scan-enable-filtering" id="prop-thread-discovery-scan-enable-filtering">PROP 5396: PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding:: <samp>b</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.36.p.2">This property is used to enable/disable EUI64 filtering during discovery scan operation. Default value is <samp>false</samp>.  </p>
+<h1 id="rfc.section.7.2.37"><a href="#rfc.section.7.2.37">7.2.37.</a> <a href="#prop-thread-discovery-scan-panid" id="prop-thread-discovery-scan-panid">PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-write</li>
+  <li>Packed-Encoding:: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.37.p.2">This property specifies the PANID used for filtering during discovery scan operation. Default value is <samp>0xffff</samp> (broadcast PANID) which disables PANID filtering.  </p>
+<h1 id="rfc.section.7.2.38"><a href="#rfc.section.7.2.38">7.2.38.</a> <a href="#prop-thread-steering-data" id="prop-thread-steering-data">PROP 5398: PROP_THREAD_STEERING_DATA</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write-Only</li>
+  <li>Packed-Encoding: <samp>E</samp></li>
+  <li>Required capability: <samp>CAP_OOB_STEERING_DATA</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.7.2.38.p.2">This property can be used to set the steering data for MLE Discovery Response messages.  </p>
+<p/>
+
+<ul>
+  <li>All zeros to clear the steering data (indicating no steering data).</li>
+  <li>All 0xFFs to set the steering data (bloom filter) to accept/allow all.</li>
+  <li>A specific EUI64 which is then added to steering data/bloom filter.</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.8"><a href="#rfc.section.8">8.</a> <a href="#feature-network-save" id="feature-network-save">Feature: Network Save</a></h1>
+<p id="rfc.section.8.p.1">The network save/recall feature is an optional NCP capability that, when present, allows the host to save and recall network credentials and state to and from nonvolatile storage.  </p>
+<p id="rfc.section.8.p.2">The presence of the save/recall feature can be detected by checking for the presence of the <samp>CAP_NET_SAVE</samp> capability in <samp>PROP_CAPS</samp>.  </p>
+<p id="rfc.section.8.p.3">Network clear feature allows host to erase all network credentials and state from non-volatile memory.  </p>
+<h1 id="rfc.section.8.1"><a href="#rfc.section.8.1">8.1.</a> <a href="#commands-1" id="commands-1">Commands</a></h1>
+<h1 id="rfc.section.8.1.1"><a href="#rfc.section.8.1.1">8.1.1.</a> <a href="#cmd-9-hostncp-cmdnetsave" id="cmd-9-hostncp-cmdnetsave">CMD 9: (Host-&gt;NCP) CMD_NET_SAVE</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_NET_SAVE</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.8.1.1.p.1">Save network state command. Saves any current network credentials and state necessary to reconnect to the current network to non-volatile memory.  </p>
+<p id="rfc.section.8.1.1.p.2">This operation affects non-volatile memory only. The current network information stored in volatile memory is unaffected.  </p>
+<p id="rfc.section.8.1.1.p.3">The response to this command is always a <samp>CMD_PROP_VALUE_IS</samp> for <samp>PROP_LAST_STATUS</samp>, indicating the result of the operation.  </p>
+<p id="rfc.section.8.1.1.p.4">This command is only available if the <samp>CAP_NET_SAVE</samp> capability is set.  </p>
+<h1 id="rfc.section.8.1.2"><a href="#rfc.section.8.1.2">8.1.2.</a> <a href="#cmd-10-hostncp-cmdnetclear" id="cmd-10-hostncp-cmdnetclear">CMD 10: (Host-&gt;NCP) CMD_NET_CLEAR</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_NET_CLEAR</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.8.1.2.p.1">Clear saved network settings command. Erases all network credentials and state from non-volatile memory. The erased settings include any data saved automatically by the network stack firmware and/or data saved by <samp>CMD_NET_SAVE</samp> operation.  </p>
+<p id="rfc.section.8.1.2.p.2">This operation affects non-volatile memory only. The current network information stored in volatile memory is unaffected.  </p>
+<p id="rfc.section.8.1.2.p.3">The response to this command is always a <samp>CMD_PROP_VALUE_IS</samp> for <samp>PROP_LAST_STATUS</samp>, indicating the result of the operation.  </p>
+<p id="rfc.section.8.1.2.p.4">This command is always available independent of the value of <samp>CAP_NET_SAVE</samp> capability.  </p>
+<h1 id="rfc.section.8.1.3"><a href="#rfc.section.8.1.3">8.1.3.</a> <a href="#cmd-11-hostncp-cmdnetrecall" id="cmd-11-hostncp-cmdnetrecall">CMD 11: (Host-&gt;NCP) CMD_NET_RECALL</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HEADER</td>
+      <td class="center">CMD_NET_RECALL</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.8.1.3.p.1">Recall saved network state command. Recalls any previously saved network credentials and state previously stored by <samp>CMD_NET_SAVE</samp> from non-volatile memory.  </p>
+<p id="rfc.section.8.1.3.p.2">This command will typically generated several unsolicited property updates as the network state is loaded. At the conclusion of loading, the authoritative response to this command is always a <samp>CMD_PROP_VALUE_IS</samp> for <samp>PROP_LAST_STATUS</samp>, indicating the result of the operation.  </p>
+<p id="rfc.section.8.1.3.p.3">This command is only available if the <samp>CAP_NET_SAVE</samp> capability is set.  </p>
+<h1 id="rfc.section.9"><a href="#rfc.section.9">9.</a> <a href="#feature-host-buffer-offload" id="feature-host-buffer-offload">Feature: Host Buffer Offload</a></h1>
+<p id="rfc.section.9.p.1">The memory on an NCP may be much more limited than the memory on the host processor. In such situations, it is sometimes useful for the NCP to offload buffers to the host processor temporarily so that it can perform other operations.  </p>
+<p id="rfc.section.9.p.2">Host buffer offload is an optional NCP capability that, when present, allows the NCP to store data buffers on the host processor that can be recalled at a later time.  </p>
+<p id="rfc.section.9.p.3">The presence of this feature can be detected by the host by checking for the presence of the <samp>CAP_HBO</samp> capability in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.9.1"><a href="#rfc.section.9.1">9.1.</a> <a href="#commands-2" id="commands-2">Commands</a></h1>
+<h1 id="rfc.section.9.1.1"><a href="#rfc.section.9.1.1">9.1.1.</a> <a href="#cmd-12-ncphost-cmdhbooffload" id="cmd-12-ncphost-cmdhbooffload">CMD 12: (NCP-&gt;Host) CMD_HBO_OFFLOAD</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>LscD</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li><li><samp>Expiration</samp>: In seconds-from-now</li><li><samp>Priority</samp>: Critical, High, Medium, Low</li><li><samp>Data</samp>: Data to offload</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.1.2"><a href="#rfc.section.9.1.2">9.1.2.</a> <a href="#cmd-13-ncphost-cmdhboreclaim" id="cmd-13-ncphost-cmdhboreclaim">CMD 13: (NCP-&gt;Host) CMD_HBO_RECLAIM</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>Lb</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li><li><samp>KeepAfterReclaim</samp>: If not set to true, the block will be dropped by the host after it is sent to the NCP.</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.1.3"><a href="#rfc.section.9.1.3">9.1.3.</a> <a href="#cmd-14-ncphost-cmdhbodrop" id="cmd-14-ncphost-cmdhbodrop">CMD 14: (NCP-&gt;Host) CMD_HBO_DROP</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>L</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.1.4"><a href="#rfc.section.9.1.4">9.1.4.</a> <a href="#cmd-15-hostncp-cmdhbooffloaded" id="cmd-15-hostncp-cmdhbooffloaded">CMD 15: (Host-&gt;NCP) CMD_HBO_OFFLOADED</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>Li</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li><li><samp>Status</samp>: Status code for the result of the operation.</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.1.5"><a href="#rfc.section.9.1.5">9.1.5.</a> <a href="#cmd-16-hostncp-cmdhboreclaimed" id="cmd-16-hostncp-cmdhboreclaimed">CMD 16: (Host-&gt;NCP) CMD_HBO_RECLAIMED</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>LiD</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li><li><samp>Status</samp>: Status code for the result of the operation.</li><li><samp>Data</samp>: Data that was previously offloaded (if any)</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.1.6"><a href="#rfc.section.9.1.6">9.1.6.</a> <a href="#cmd-17-hostncp-cmdhbodropped" id="cmd-17-hostncp-cmdhbodropped">CMD 17: (Host-&gt;NCP) CMD_HBO_DROPPED</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>Li</samp> <ul><li><samp>OffloadId</samp>: 32-bit unique block identifier</li><li><samp>Status</samp>: Status code for the result of the operation.</li></ul></li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.section.9.2"><a href="#rfc.section.9.2">9.2.</a> <a href="#properties-1" id="properties-1">Properties</a></h1>
+<h1 id="rfc.section.9.2.1"><a href="#rfc.section.9.2.1">9.2.1.</a> <a href="#prop-hbo-mem-max" id="prop-hbo-mem-max">PROP 10: PROP_HBO_MEM_MAX</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>L</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">4</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>PROP_HBO_MEM_MAX</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.9.2.1.p.2">Describes the number of bytes that may be offloaded from the NCP to the host. Default value is zero, so this property must be set by the host to a non-zero value before the NCP will begin offloading blocks.  </p>
+<p id="rfc.section.9.2.1.p.3">This value is encoded as an unsigned 32-bit integer.  </p>
+<p id="rfc.section.9.2.1.p.4">This property is only available if the <samp>CAP_HBO</samp> capability is present in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.9.2.2"><a href="#rfc.section.9.2.2">9.2.2.</a> <a href="#prop-hbo-block-max" id="prop-hbo-block-max">PROP 11: PROP_HBO_BLOCK_MAX</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>S</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">2</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>PROP_HBO_BLOCK_MAX</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.9.2.2.p.2">Describes the number of blocks that may be offloaded from the NCP to the host. Default value is 32. Setting this value to zero will cause host block offload to be effectively disabled.  </p>
+<p id="rfc.section.9.2.2.p.3">This value is encoded as an unsigned 16-bit integer.  </p>
+<p id="rfc.section.9.2.2.p.4">This property is only available if the <samp>CAP_HBO</samp> capability is present in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.10"><a href="#rfc.section.10">10.</a> <a href="#feature-jam-detect" id="feature-jam-detect">Feature: Jam Detection</a></h1>
+<p id="rfc.section.10.p.1">Jamming detection is a feature that allows the NCP to report when it detects high levels of interference that are characteristic of intentional signal jamming.  </p>
+<p id="rfc.section.10.p.2">The presence of this feature can be detected by checking for the presence of the <samp>CAP_JAM_DETECT</samp> (value 6) capability in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.10.1"><a href="#rfc.section.10.1">10.1.</a> <a href="#properties-2" id="properties-2">Properties</a></h1>
+<h1 id="rfc.section.10.1.1"><a href="#rfc.section.10.1.1">10.1.1.</a> <a href="#prop-jam-detect-enable" id="prop-jam-detect-enable">PROP 4608: PROP_JAM_DETECT_ENABLE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>Default Value: false</li>
+  <li>REQUIRED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>PROP_JAM_DETECT_ENABLE</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.10.1.1.p.2">Indicates if jamming detection is enabled or disabled. Set to true to enable jamming detection.  </p>
+<p id="rfc.section.10.1.1.p.3">This property is only available if the <samp>CAP_JAM_DETECT</samp> capability is present in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.10.1.2"><a href="#rfc.section.10.1.2">10.1.2.</a> <a href="#prop-jam-detected" id="prop-jam-detected">PROP 4609: PROP_JAM_DETECTED</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>b</samp></li>
+  <li>REQUIRED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">
+        <samp>PROP_JAM_DETECTED</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.10.1.2.p.2">Set to true if radio jamming is detected. Set to false otherwise.  </p>
+<p id="rfc.section.10.1.2.p.3">When jamming detection is enabled, changes to the value of this property are emitted asynchronously via <samp>CMD_PROP_VALUE_IS</samp>.  </p>
+<p id="rfc.section.10.1.2.p.4">This property is only available if the <samp>CAP_JAM_DETECT</samp> capability is present in <samp>PROP_CAPS</samp>.  </p>
+<h1 id="rfc.section.10.1.3"><a href="#rfc.section.10.1.3">10.1.3.</a> <a href="#prop-4610-propjamdetectrssithreshold" id="prop-4610-propjamdetectrssithreshold">PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>c</samp></li>
+  <li>Units: dBm</li>
+  <li>Default Value: Implementation-specific</li>
+  <li>RECOMMENDED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.10.1.3.p.2">This parameter describes the threshold RSSI level (measured in dBm) above which the jamming detection will consider the channel blocked.  </p>
+<h1 id="rfc.section.10.1.4"><a href="#rfc.section.10.1.4">10.1.4.</a> <a href="#prop-4611-propjamdetectwindow" id="prop-4611-propjamdetectwindow">PROP 4611: PROP_JAM_DETECT_WINDOW</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>c</samp></li>
+  <li>Units: Seconds (1-64)</li>
+  <li>Default Value: Implementation-specific</li>
+  <li>RECOMMENDED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.10.1.4.p.2">This parameter describes the window period for signal jamming detection.  </p>
+<h1 id="rfc.section.10.1.5"><a href="#rfc.section.10.1.5">10.1.5.</a> <a href="#prop-4612-propjamdetectbusy" id="prop-4612-propjamdetectbusy">PROP 4612: PROP_JAM_DETECT_BUSY</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+  <li>Packed-Encoding: <samp>i</samp></li>
+  <li>Units: Seconds (1-64)</li>
+  <li>Default Value: Implementation-specific</li>
+  <li>RECOMMENDED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.10.1.5.p.2">This parameter describes the number of aggregate seconds within the detection window where the RSSI must be above <samp>PROP_JAM_DETECT_RSSI_THRESHOLD</samp> to trigger detection.  </p>
+<p id="rfc.section.10.1.5.p.3">The behavior of the jamming detection feature when <samp>PROP_JAM_DETECT_BUSY</samp> is larger than <samp>PROP_JAM_DETECT_WINDOW</samp> is undefined.  </p>
+<h1 id="rfc.section.10.1.6"><a href="#rfc.section.10.1.6">10.1.6.</a> <a href="#prop-4613-propjamdetecthistorybitmap" id="prop-4613-propjamdetecthistorybitmap">PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Only</li>
+  <li>Packed-Encoding: <samp>LL</samp></li>
+  <li>Default Value: Implementation-specific</li>
+  <li>RECOMMENDED for <samp>CAP_JAM_DETECT</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.10.1.6.p.2">This value provides information about current state of jamming detection module for monitoring/debugging purpose. It returns a 64-bit value where each bit corresponds to one second interval starting with bit 0 for the most recent interval and bit 63 for the oldest intervals (63 sec earlier).  The bit is set to 1 if the jamming detection module observed/detected high signal level during the corresponding one second interval.  The value is read-only and is encoded as two <samp>L</samp> (uint32) values in little-endian format (first <samp>L</samp> (uint32) value gives the lower bits corresponding to more recent history).  </p>
+<h1 id="rfc.section.11"><a href="#rfc.section.11">11.</a> <a href="#feature-gpio-access" id="feature-gpio-access">Feature: GPIO Access</a></h1>
+<p id="rfc.section.11.p.1">This feature allows the host to have control over some or all of the GPIO pins on the NCP. The host can determine which GPIOs are available by examining <samp>PROP_GPIO_CONFIG</samp>, described below. This API supports a maximum of 256 individual GPIO pins.  </p>
+<p id="rfc.section.11.p.2">Support for this feature can be determined by the presence of <samp>CAP_GPIO</samp>.  </p>
+<h1 id="rfc.section.11.1"><a href="#rfc.section.11.1">11.1.</a> <a href="#properties-3" id="properties-3">Properties</a></h1>
+<h1 id="rfc.section.11.1.1"><a href="#rfc.section.11.1.1">11.1.1.</a> <a href="#prop-4096-propgpioconfig" id="prop-4096-propgpioconfig">PROP 4096: PROP_GPIO_CONFIG</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>A(t(CCU))</samp></li>
+  <li>Type: Read-write (Writable only using <samp>CMD_PROP_VALUE_INSERT</samp>, <a href="#cmd-prop-value-insert">Section 4.5</a>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.1.p.2">An array of structures which contain the following fields: </p>
+<p/>
+
+<ul>
+  <li><samp>C</samp>: GPIO Number</li>
+  <li><samp>C</samp>: GPIO Configuration Flags</li>
+  <li><samp>U</samp>: Human-readable GPIO name</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.1.p.4">GPIOs which do not have a corresponding entry are not supported.  </p>
+<p id="rfc.section.11.1.1.p.5">The configuration parameter contains the configuration flags for the GPIO: </p>
+<pre>
+  0   1   2   3   4   5   6   7
++---+---+---+---+---+---+---+---+
+|DIR|PUP|PDN|TRIGGER|  RESERVED |
++---+---+---+---+---+---+---+---+
+        |O/D|
+        +---+
+</pre>
+<p/>
+
+<ul>
+  <li><samp>DIR</samp>: Pin direction. Clear (0) for input, set (1) for output.</li>
+  <li><samp>PUP</samp>: Pull-up enabled flag.</li>
+  <li><samp>PDN</samp>/<samp>O/D</samp>: Flag meaning depends on pin direction: <ul><li>Input: Pull-down enabled.</li><li>Output: Output is an open-drain.</li></ul></li>
+  <li><samp>TRIGGER</samp>: Enumeration describing how pin changes generate asynchronous notification commands (TBD) from the NCP to the host.  <ul><li>0: Feature disabled for this pin</li><li>1: Trigger on falling edge</li><li>2: Trigger on rising edge</li><li>3: Trigger on level change</li></ul></li>
+  <li><samp>RESERVED</samp>: Bits reserved for future use. Always cleared to zero and ignored when read.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.1.p.7">As an optional feature, the configuration of individual pins may be modified using the <samp>CMD_PROP_VALUE_INSERT</samp> command. Only the GPIO number and flags fields MUST be present, the GPIO name (if present) would be ignored. This command can only be used to modify the configuration of GPIOs which are already exposed---it cannot be used by the host to add addional GPIOs.  </p>
+<h1 id="rfc.section.11.1.2"><a href="#rfc.section.11.1.2">11.1.2.</a> <a href="#prop-4098-propgpiostate" id="prop-4098-propgpiostate">PROP 4098: PROP_GPIO_STATE</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Read-Write</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.2.p.2">Contains a bit field identifying the state of the GPIOs. The length of the data associated with these properties depends on the number of GPIOs. If you have 10 GPIOs, you'd have two bytes. GPIOs are numbered from most significant bit to least significant bit, so 0x80 is GPIO 0, 0x40 is GPIO 1, etc.  </p>
+<p id="rfc.section.11.1.2.p.3">For GPIOs configured as inputs: </p>
+<p/>
+
+<ul>
+  <li><samp>CMD_PROP_VAUE_GET</samp>: The value of the associated bit describes the logic level read from the pin.</li>
+  <li><samp>CMD_PROP_VALUE_SET</samp>: The value of the associated bit is ignored for these pins.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.2.p.5">For GPIOs configured as outputs: </p>
+<p/>
+
+<ul>
+  <li><samp>CMD_PROP_VAUE_GET</samp>: The value of the associated bit is implementation specific.</li>
+  <li><samp>CMD_PROP_VALUE_SET</samp>: The value of the associated bit determines the new logic level of the output. If this pin is configured as an open-drain, setting the associated bit to 1 will cause the pin to enter a Hi-Z state.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.2.p.7">For GPIOs which are not specified in <samp>PROP_GPIO_CONFIG</samp>: </p>
+<p/>
+
+<ul>
+  <li><samp>CMD_PROP_VAUE_GET</samp>: The value of the associated bit is implementation specific.</li>
+  <li><samp>CMD_PROP_VALUE_SET</samp>: The value of the associated bit MUST be ignored by the NCP.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.2.p.9">When writing, unspecified bits are assumed to be zero.  </p>
+<h1 id="rfc.section.11.1.3"><a href="#rfc.section.11.1.3">11.1.3.</a> <a href="#prop-4099-propgpiostateset" id="prop-4099-propgpiostateset">PROP 4099: PROP_GPIO_STATE_SET</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write-only</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.3.p.2">Allows for the state of various output GPIOs to be set without affecting other GPIO states. Contains a bit field identifying the output GPIOs that should have their state set to 1.  </p>
+<p id="rfc.section.11.1.3.p.3">When writing, unspecified bits are assumed to be zero. The value of any bits for GPIOs which are not specified in <samp>PROP_GPIO_CONFIG</samp> MUST be ignored.  </p>
+<h1 id="rfc.section.11.1.4"><a href="#rfc.section.11.1.4">11.1.4.</a> <a href="#prop-4100-propgpiostateclear" id="prop-4100-propgpiostateclear">PROP 4100: PROP_GPIO_STATE_CLEAR</a></h1>
+<p/>
+
+<ul>
+  <li>Type: Write-only</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.11.1.4.p.2">Allows for the state of various output GPIOs to be cleared without affecting other GPIO states. Contains a bit field identifying the output GPIOs that should have their state cleared to 0.  </p>
+<p id="rfc.section.11.1.4.p.3">When writing, unspecified bits are assumed to be zero. The value of any bits for GPIOs which are not specified in <samp>PROP_GPIO_CONFIG</samp> MUST be ignored.  </p>
+<h1 id="rfc.section.12"><a href="#rfc.section.12">12.</a> <a href="#feature-trng" id="feature-trng">Feature: True Random Number Generation</a></h1>
+<p id="rfc.section.12.p.1">This feature allows the host to have access to any strong hardware random number generator that might be present on the NCP, for things like key generation or seeding PRNGs.  </p>
+<p id="rfc.section.12.p.2">Support for this feature can be determined by the presence of <samp>CAP_TRNG</samp>.  </p>
+<p id="rfc.section.12.p.3">Note well that implementing a cryptographically-strong software-based true random number generator (that is impervious to things like temperature changes, manufacturing differences across devices, or unexpected output correlations) is non-trivial without a well-designed, dedicated hardware random number generator. Implementors who have little or no experience in this area are encouraged to not advertise this capability.  </p>
+<h1 id="rfc.section.12.1"><a href="#rfc.section.12.1">12.1.</a> <a href="#properties-4" id="properties-4">Properties</a></h1>
+<h1 id="rfc.section.12.1.1"><a href="#rfc.section.12.1.1">12.1.1.</a> <a href="#prop-4101-proptrng32" id="prop-4101-proptrng32">PROP 4101: PROP_TRNG_32</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>L</samp></li>
+  <li>Type: Read-Only</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.12.1.1.p.2">Fetching this property returns a strong random 32-bit integer that is suitable for use as a PRNG seed or for cryptographic use.  </p>
+<p id="rfc.section.12.1.1.p.3">While the exact mechanism behind the calculation of this value is implementation-specific, the implementation must satisfy the following requirements: </p>
+<p/>
+
+<ul>
+  <li>Data representing at least 32 bits of fresh entropy (extracted from the primary entropy source) MUST be consumed by the calculation of each query.</li>
+  <li>Each of the 32 bits returned MUST be free of bias and have no statistical correlation to any part of the raw data used for the calculation of any query.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.12.1.1.p.5">Support for this property is REQUIRED if <samp>CAP_TRNG</samp> is included in the device capabilities.  </p>
+<h1 id="rfc.section.12.1.2"><a href="#rfc.section.12.1.2">12.1.2.</a> <a href="#prop-4102-proptrng128" id="prop-4102-proptrng128">PROP 4102: PROP_TRNG_128</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>D</samp></li>
+  <li>Type: Read-Only</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.12.1.2.p.2">Fetching this property returns 16 bytes of strong random data suitable for direct cryptographic use without further processing(For example, as an AES key).  </p>
+<p id="rfc.section.12.1.2.p.3">While the exact mechanism behind the calculation of this value is implementation-specific, the implementation must satisfy the following requirements: </p>
+<p/>
+
+<ul>
+  <li>Data representing at least 128 bits of fresh entropy (extracted from the primary entropy source) MUST be consumed by the calculation of each query.</li>
+  <li>Each of the 128 bits returned MUST be free of bias and have no statistical correlation to any part of the raw data used for the calculation of any query.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.12.1.2.p.5">Support for this property is REQUIRED if <samp>CAP_TRNG</samp> is included in the device capabilities.  </p>
+<h1 id="rfc.section.12.1.3"><a href="#rfc.section.12.1.3">12.1.3.</a> <a href="#prop-4103-proptrngraw32" id="prop-4103-proptrngraw32">PROP 4103: PROP_TRNG_RAW_32</a></h1>
+<p/>
+
+<ul>
+  <li>Argument-Encoding: <samp>D</samp></li>
+  <li>Type: Read-Only</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.12.1.3.p.2">This property is primarily used to diagnose and debug the behavior of the entropy source used for strong random number generation.  </p>
+<p id="rfc.section.12.1.3.p.3">When queried, returns the raw output from the entropy source used to generate <samp>PROP_TRNG_32</samp>, prior to any reduction/whitening and/or mixing with prior state.  </p>
+<p id="rfc.section.12.1.3.p.4">The length of the returned buffer is implementation specific and should be expected to be non-deterministic.  </p>
+<p id="rfc.section.12.1.3.p.5">Support for this property is RECOMMENDED if <samp>CAP_TRNG</samp> is included in the device capabilities.  </p>
+<h1 id="rfc.section.13"><a href="#rfc.section.13">13.</a> <a href="#security-considerations" id="security-considerations">Security Considerations</a></h1>
+<h1 id="rfc.section.13.1"><a href="#rfc.section.13.1">13.1.</a> <a href="#raw-application-access" id="raw-application-access">Raw Application Access</a></h1>
+<p id="rfc.section.13.1.p.1">Spinel MAY be used as an API boundary for allowing processes to configure the NCP. However, such a system MUST NOT give unprivileged processess the ability to send or receive arbitrary command frames to the NCP. Only the specific commands and properties that are required should be allowed to be passed, and then only after being checked for proper format.  </p>
+<h1 id="rfc.appendix.A"><a href="#rfc.appendix.A">Appendix A.</a> <a href="#framing-protocol" id="framing-protocol">Framing Protocol</a></h1>
+<p id="rfc.section.A.p.1">Since this NCP protocol is defined independently of the physical transport or framing, any number of transports and framing protocols could be used successfully. However, in the interests of compatibility, this document provides some recommendations.  </p>
+<h1 id="rfc.appendix.A.1"><a href="#rfc.appendix.A.1">A.1.</a> <a href="#uart-recommendations" id="uart-recommendations">UART Recommendations</a></h1>
+<p id="rfc.section.A.1.p.1">The recommended default UART settings are: </p>
+<p/>
+
+<ul>
+  <li>Bit rate:     115200</li>
+  <li>Start bits:   1</li>
+  <li>Data bits:    8</li>
+  <li>Stop bits:    1</li>
+  <li>Parity:       None</li>
+  <li>Flow Control: Hardware</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.1.p.3">These values may be adjusted depending on the individual needs of the application or product, but some sort of flow control MUST be used.  Hardware flow control is preferred over software flow control. In the absence of hardware flow control, software flow control (XON/XOFF) MUST be used instead.  </p>
+<p id="rfc.section.A.1.p.4">We also <strong>RECOMMEND</strong> an Arduino-style hardware reset, where the DTR signal is coupled to the <samp>R&#773;E&#773;S&#773;</samp> pin through a 0.01&#181;F capacitor. This causes the NCP to automatically reset whenever the serial port is opened. At the very least we <strong>RECOMMEND</strong> dedicating one of your host pins to controlling the <samp>R&#773;E&#773;S&#773;</samp> pin on the NCP, so that you can easily perform a hardware reset if necessary.  </p>
+<h1 id="rfc.appendix.A.1.1"><a href="#rfc.appendix.A.1.1">A.1.1.</a> <a href="#uart-bit-rate-detection" id="uart-bit-rate-detection">UART Bit Rate Detection</a></h1>
+<p id="rfc.section.A.1.1.p.1">When using a UART, the issue of an appropriate bit rate must be considered. A bitrate of 115200 bits per second has become a defacto standard baud rate for many serial peripherals. This rate, however, is slower than the theoretical maximum bitrate of the 802.15.4 2.4GHz PHY (250kbit). In most circumstances this mismatch is not significant because the overall bitrate will be much lower than either of these rates, but there are circumstances where a faster UART bitrate is desirable. Thus, this document proposes a simple bitrate detection scheme that can be employed by the host to detect when the attached NCP is initially running at a higher bitrate.  </p>
+<p id="rfc.section.A.1.1.p.2">The algorithm is to send successive NOOP commands to the NCP at increasing bitrates. When a valid <samp>CMD_LAST_STATUS</samp> response has been received, we have identified the correct bitrate.  </p>
+<p id="rfc.section.A.1.1.p.3">In order to limit the time spent hunting for the appropriate bitrate, we RECOMMEND that only the following bitrates be checked: </p>
+<p/>
+
+<ul>
+  <li>115200</li>
+  <li>230400</li>
+  <li>1000000 (1Mbit)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.1.1.p.5">The bitrate MAY also be changed programmatically by adjusting <samp>PROP_UART_BITRATE</samp>, if implemented.  </p>
+<h1 id="rfc.appendix.A.1.2"><a href="#rfc.appendix.A.1.2">A.1.2.</a> <a href="#hdlc-lite" id="hdlc-lite">HDLC-Lite</a></h1>
+<p><em>HDLC-Lite</em> is the recommended framing protocol for transmitting Spinel frames over a UART. HDLC-Lite consists of only the framing, escaping, and CRC parts of the larger HDLC protocol---all other parts of HDLC are omitted. This protocol was chosen because it works well with software flow control and is widely implemented.  </p>
+<p id="rfc.section.A.1.2.p.2">To transmit a frame with HDLC-lite, the 16-bit CRC must first be appended to the frame. The CRC function is defined to be CRC-16/CCITT, otherwise known as the <a href="http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit">KERMIT CRC</a>.  </p>
+<p id="rfc.section.A.1.2.p.3">Individual frames are terminated with a frame delimiter octet called the 'flag' octet (<samp>0x7E</samp>).  </p>
+<p id="rfc.section.A.1.2.p.4">The following octets values are considered <em>special</em> and should be escaped when present in data frames: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octet Value</th>
+      <th class="center">Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">0x7E</td>
+      <td class="center">Frame Delimiter (Flag)</td>
+    </tr>
+    <tr>
+      <td class="center">0x7D</td>
+      <td class="center">Escape Byte</td>
+    </tr>
+    <tr>
+      <td class="center">0x11</td>
+      <td class="center">XON</td>
+    </tr>
+    <tr>
+      <td class="center">0x13</td>
+      <td class="center">XOFF</td>
+    </tr>
+    <tr>
+      <td class="center">0xF8</td>
+      <td class="center">Vendor-Specific</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.A.1.2.p.5">When present in a data frame, these octet values are escaped by prepending the escape octet (<samp>0x7D</samp>) and XORing the value with <samp>0x20</samp>.  </p>
+<p id="rfc.section.A.1.2.p.6">When receiving a frame, the CRC must be verified after the frame is unescaped. If the CRC value does not match what is calculated for the frame data, the frame MUST be discarded. The implementation MAY indicate the failure to higher levels to handle as they see fit, but MUST NOT attempt to process the deceived frame.  </p>
+<p id="rfc.section.A.1.2.p.7">Consecutive flag octets are entirely legal and MUST NOT be treated as a framing error. Consecutive flag octets MAY be used as a way to wake up a sleeping NCP.  </p>
+<p id="rfc.section.A.1.2.p.8">When first establishing a connection to the NCP, it is customary to send one or more flag octets to ensure that any previously received data is discarded.  </p>
+<h1 id="rfc.appendix.A.2"><a href="#rfc.appendix.A.2">A.2.</a> <a href="#spi-recommendations" id="spi-recommendations">SPI Recommendations</a></h1>
+<p id="rfc.section.A.2.p.1">We RECOMMEND the use of the following standard SPI signals: </p>
+<p/>
+
+<ul>
+  <li><samp>C&#773;S&#773;</samp>:   (Host-to-NCP) Chip Select</li>
+  <li><samp>CLK</samp>:  (Host-to-NCP) Clock</li>
+  <li><samp>MOSI</samp>: Master-Output/Slave-Input</li>
+  <li><samp>MISO</samp>: Master-Input/Slave-Output</li>
+  <li><samp>I&#773;N&#773;T&#773;</samp>:  (NCP-to-Host) Host Interrupt</li>
+  <li><samp>R&#773;E&#773;S&#773;</samp>:  (Host-to-NCP) NCP Hardware Reset</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.2.p.3">The <samp>I&#773;N&#773;T&#773;</samp> signal is used by the NCP to indicate to the host that the NCP has frames pending to send to it. When asserted, the host SHOULD initiate a SPI transaction in a timely manner.  </p>
+<p id="rfc.section.A.2.p.4">We RECOMMEND the following SPI properties: </p>
+<p/>
+
+<ul>
+  <li><samp>C&#773;S&#773;</samp> is active low.</li>
+  <li><samp>CLK</samp> is active high.</li>
+  <li><samp>CLK</samp> speed is larger than 500 kHz.</li>
+  <li>Data is valid on leading edge of <samp>CLK</samp>.</li>
+  <li>Data is sent in multiples of 8-bits (octets).</li>
+  <li>Octets are sent most-significant bit first.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.2.p.6">This recommended configuration may be adjusted depending on the individual needs of the application or product.  </p>
+<h1 id="rfc.appendix.A.2.1"><a href="#rfc.appendix.A.2.1">A.2.1.</a> <a href="#spi-framing-protocol" id="spi-framing-protocol">SPI Framing Protocol</a></h1>
+<p id="rfc.section.A.2.1.p.1">Each SPI frame starts with a 5-byte frame header: </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">Octets:</th>
+      <th class="center">1</th>
+      <th class="center">2</th>
+      <th class="center">2</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">Fields:</td>
+      <td class="center">HDR</td>
+      <td class="center">RECV_LEN</td>
+      <td class="center">DATA_LEN</td>
+    </tr>
+  </tbody>
+</table>
+<p/>
+
+<ul>
+  <li><samp>HDR</samp>: The first byte is the header byte (defined below)</li>
+  <li><samp>RECV_LEN</samp>: The second and third bytes indicate the largest frame size that that device is ready to receive. If zero, then the other device must not send any data. (Little endian)</li>
+  <li><samp>DATA_LEN</samp>: The fourth and fifth bytes indicate the size of the pending data frame to be sent to the other device. If this value is equal-to or less-than the number of bytes that the other device is willing to receive, then the data of the frame is immediately after the header. (Little Endian)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.2.1.p.3">The <samp>HDR</samp> byte is defined as: </p>
+<pre>
+  0   1   2   3   4   5   6   7
++---+---+---+---+---+---+---+---+
+|RST|CRC|CCF|  RESERVED |PATTERN|
++---+---+---+---+---+---+---+---+
+</pre>
+<p/>
+
+<ul>
+  <li><samp>RST</samp>: This bit is set when that device has been reset since the last time <samp>C&#773;S&#773;</samp> was asserted.</li>
+  <li><samp>CRC</samp>: This bit is set when that device supports writing a 16-bit CRC at the end of the data. The CRC length is NOT included in DATA_LEN.</li>
+  <li><samp>CCF</samp>: "CRC Check Failure". Set if the CRC check on the last received frame failed, cleared to zero otherwise. This bit is only used if both sides support CRC.</li>
+  <li><samp>RESERVED</samp>: These bits are all reserved for future used. They MUST be cleared to zero and MUST be ignored if set.</li>
+  <li><samp>PATTERN</samp>: These bits are set to a fixed value to help distinguish valid SPI frames from garbage (by explicitly making <samp>0xFF</samp> and <samp>0x00</samp> invalid values). Bit 6 MUST be set to be one and bit 7 MUST be cleared (0). A frame received that has any other values for these bits MUST be dropped.</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.A.2.1.p.5">Prior to a sending or receiving a frame, the master MAY send a 5-octet frame with zeros for both the max receive frame size and the the contained frame length. This will induce the slave device to indicate the length of the frame it wants to send (if any) and indicate the largest frame it is capable of receiving at the moment.  This allows the master to calculate the size of the next transaction.  Alternatively, if the master has a frame to send it can just go ahead and send a frame of that length and determine if the frame was accepted by checking that the <samp>RECV_LEN</samp> from the slave frame is larger than the frame the master just tried to send. If the <samp>RECV_LEN</samp> is smaller then the frame wasn't accepted and will need to be transmitted again.  </p>
+<p id="rfc.section.A.2.1.p.6">This protocol can be used either unidirectionally or bidirectionally, determined by the behavior of the master and the slave.  </p>
+<p id="rfc.section.A.2.1.p.7">If the the master notices <samp>PATTERN</samp> is not set correctly, the master should consider the transaction to have failed and try again after 10 milliseconds, retrying up to 200 times. After unsuccessfully trying 200 times in a row, the master MAY take appropriate remedial action (like a NCP hardware reset, or indicating a communication failure to a user interface).  </p>
+<p id="rfc.section.A.2.1.p.8">At the end of the data of a frame is an optional 16-bit CRC, support for which is indicated by the <samp>CRC</samp> bit of the <samp>HDR</samp> byte being set. If these bits are set for both the master and slave frames, then CRC checking is enabled on both sides, effectively requiring that frame sizes be two bytes longer than would be otherwise required. The CRC is calculated using the same mechanism used for the CRC calculation in HDLC-Lite (See <a href="#hdlc-lite">Appendix A.1.2</a>).  When both of the <samp>CRC</samp> bits are set, both sides must verify that the <samp>CRC</samp> is valid before accepting the frame. If not enough bytes were clocked out for the CRC to be read, then the frame must be ignored. If enough bytes were clocked out to perform a CRC check, but the CRC check fails, then the frame must be rejected and the <samp>CRC_FAIL</samp> bit on the next frame (and ONLY the next frame) MUST be set.  </p>
+<h1 id="rfc.appendix.A.3"><a href="#rfc.appendix.A.3">A.3.</a> <a href="#i2c-recommendations" id="i2c-recommendations">I&#178;C Recommendations</a></h1>
+<p id="rfc.section.A.3.p.1">TBD </p>
+<p>
+  <a id="CREF5" class="info">[CREF5]<span class="info">RQ: It may make sense to have a look at what Bluetooth HCI is doing for native I&#178;C framing and go with that.</span></a>
+</p>
+<h1 id="rfc.appendix.A.4"><a href="#rfc.appendix.A.4">A.4.</a> <a href="#native-usb-recommendations" id="native-usb-recommendations">Native USB Recommendations</a></h1>
+<p id="rfc.section.A.4.p.1">TBD </p>
+<p>
+  <a id="CREF6" class="info">[CREF6]<span class="info">RQ: It may make sense to have a look at what Bluetooth HCI is doing for native USB framing and go with that.</span></a>
+</p>
+<h1 id="rfc.appendix.B"><a href="#rfc.appendix.B">Appendix B.</a> <a href="#test-vectors" id="test-vectors">Test Vectors</a></h1>
+<h1 id="rfc.appendix.B.1"><a href="#rfc.appendix.B.1">B.1.</a> <a href="#test-vector-packed-unsigned-integer" id="test-vector-packed-unsigned-integer">Test Vector: Packed Unsigned Integer</a></h1>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="right">Decimal Value</th>
+      <th class="left">Packet Octet Encoding</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="right">0</td>
+      <td class="left">
+        <samp>00</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">1</td>
+      <td class="left">
+        <samp>01</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">127</td>
+      <td class="left">
+        <samp>7F</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">128</td>
+      <td class="left">
+        <samp>80 01</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">129</td>
+      <td class="left">
+        <samp>81 01</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">1,337</td>
+      <td class="left">
+        <samp>B9 0A</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">16,383</td>
+      <td class="left">
+        <samp>FF 7F</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">16,384</td>
+      <td class="left">
+        <samp>80 80 01</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">16,385</td>
+      <td class="left">
+        <samp>81 80 01</samp>
+      </td>
+    </tr>
+    <tr>
+      <td class="right">2,097,151</td>
+      <td class="left">
+        <samp>FF FF 7F</samp>
+      </td>
+    </tr>
+  </tbody>
+</table>
+<p>
+  <a id="CREF7" class="info">[CREF7]<span class="info">RQ: The PUI test-vector encodings need to be verified.</span></a>
+</p>
+<h1 id="rfc.appendix.B.2"><a href="#rfc.appendix.B.2">B.2.</a> <a href="#test-vector-reset-command" id="test-vector-reset-command">Test Vector: Reset Command</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 0</li>
+  <li>CMD: 1 (<samp>CMD_RESET</samp>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.2.p.2">Frame: </p>
+<pre>
+80 01
+</pre>
+<h1 id="rfc.appendix.B.3"><a href="#rfc.appendix.B.3">B.3.</a> <a href="#test-vector-reset-notification" id="test-vector-reset-notification">Test Vector: Reset Notification</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 0</li>
+  <li>CMD: 6 (<samp>CMD_VALUE_IS</samp>)</li>
+  <li>PROP: 0 (<samp>PROP_LAST_STATUS</samp>)</li>
+  <li>VALUE: 114 (<samp>STATUS_RESET_SOFTWARE</samp>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.3.p.2">Frame: </p>
+<pre>
+80 06 00 72
+</pre>
+<h1 id="rfc.appendix.B.4"><a href="#rfc.appendix.B.4">B.4.</a> <a href="#test-vector-scan-beacon" id="test-vector-scan-beacon">Test Vector: Scan Beacon</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 0</li>
+  <li>CMD: 7 (<samp>CMD_VALUE_INSERTED</samp>)</li>
+  <li>PROP: 51 (<samp>PROP_MAC_SCAN_BEACON</samp>)</li>
+  <li>VALUE: Structure, encoded as <samp>Cct(ESSc)t(iCUd)</samp> <ul><li>CHAN: 15</li><li>RSSI: -60dBm</li><li>MAC_DATA: (0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00) <ul><li>Long address: B6:40:D4:8C:E9:38:F9:52</li><li>Short address: 0xFFFF</li><li>PAN-ID: 0x04D2</li><li>LQI: 0</li></ul></li><li>NET_DATA: (13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE EF 00 CA FE) <ul><li>Protocol Number: 3</li><li>Flags: 0x20</li><li>Network Name: <samp>spinel</samp></li><li>XPANID: <samp>DE AD 00 BE EF 00 CA FE</samp></li></ul></li></ul></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.4.p.2">Frame: </p>
+<pre>
+80 07 33 0F C4 0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00
+13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE EF 00 CA
+FE
+</pre>
+<h1 id="rfc.appendix.B.5"><a href="#rfc.appendix.B.5">B.5.</a> <a href="#test-vector-inbound-ipv6-packet" id="test-vector-inbound-ipv6-packet">Test Vector: Inbound IPv6 Packet</a></h1>
+<p id="rfc.section.B.5.p.1">CMD_VALUE_IS(PROP_STREAM_NET) </p>
+<p>
+  <a id="CREF8" class="info">[CREF8]<span class="info">RQ: FIXME: This test vector is incomplete.</span></a>
+</p>
+<h1 id="rfc.appendix.B.6"><a href="#rfc.appendix.B.6">B.6.</a> <a href="#test-vector-outbound-ipv6-packet" id="test-vector-outbound-ipv6-packet">Test Vector: Outbound IPv6 Packet</a></h1>
+<p id="rfc.section.B.6.p.1">CMD_VALUE_SET(PROP_STREAM_NET) </p>
+<p>
+  <a id="CREF9" class="info">[CREF9]<span class="info">RQ: FIXME: This test vector is incomplete.</span></a>
+</p>
+<h1 id="rfc.appendix.B.7"><a href="#rfc.appendix.B.7">B.7.</a> <a href="#test-vector-fetch-list-of-onmesh-networks" id="test-vector-fetch-list-of-onmesh-networks">Test Vector: Fetch list of on-mesh networks</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 4</li>
+  <li>CMD: 2 (<samp>CMD_VALUE_GET</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.7.p.2">Frame: </p>
+<pre>
+84 02 5A
+</pre>
+<h1 id="rfc.appendix.B.8"><a href="#rfc.appendix.B.8">B.8.</a> <a href="#test-vector-returned-list-of-onmesh-networks" id="test-vector-returned-list-of-onmesh-networks">Test Vector: Returned list of on-mesh networks</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 4</li>
+  <li>CMD: 6 (<samp>CMD_VALUE_IS</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+  <li>VALUE: Array of structures, encoded as <samp>A(t(6CbC))</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">IPv6 Prefix</th>
+      <th class="center">Prefix Length</th>
+      <th class="center">Stable Flag</th>
+      <th class="center">Other Flags</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">2001:DB8:1::</td>
+      <td class="center">64</td>
+      <td class="center">True</td>
+      <td class="center">??</td>
+    </tr>
+    <tr>
+      <td class="center">2001:DB8:2::</td>
+      <td class="center">64</td>
+      <td class="center">False</td>
+      <td class="center">??</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.B.8.p.2">Frame: </p>
+<pre>
+84 06 5A 13 00 20 01 0D B8 00 01 00 00 00 00 00 00 00 00 00
+00 40 01 ?? 13 00 20 01 0D B8 00 02 00 00 00 00 00 00 00 00
+00 00 40 00 ??
+</pre>
+<h1 id="rfc.appendix.B.9"><a href="#rfc.appendix.B.9">B.9.</a> <a href="#test-vector-adding-an-onmesh-network" id="test-vector-adding-an-onmesh-network">Test Vector: Adding an on-mesh network</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 5</li>
+  <li>CMD: 4 (<samp>CMD_VALUE_INSERT</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+  <li>VALUE: Structure, encoded as <samp>6CbCb</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">IPv6 Prefix</th>
+      <th class="center">Prefix Length</th>
+      <th class="center">Stable Flag</th>
+      <th class="center">Other Flags</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">2001:DB8:3::</td>
+      <td class="center">64</td>
+      <td class="center">True</td>
+      <td class="center">??</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.B.9.p.2">Frame: </p>
+<pre>
+85 03 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+01 ?? 01
+</pre>
+<p>
+  <a id="CREF10" class="info">[CREF10]<span class="info">RQ: FIXME: This test vector is incomplete.</span></a>
+</p>
+<h1 id="rfc.appendix.B.10"><a href="#rfc.appendix.B.10">B.10.</a> <a href="#test-vector-insertion-notification-of-an-onmesh-network" id="test-vector-insertion-notification-of-an-onmesh-network">Test Vector: Insertion notification of an on-mesh network</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 5</li>
+  <li>CMD: 7 (<samp>CMD_VALUE_INSERTED</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+  <li>VALUE: Structure, encoded as <samp>6CbCb</samp></li>
+</ul>
+
+<p> </p>
+<table cellpadding="3" cellspacing="0" class="tt full center">
+  <thead>
+    <tr>
+      <th class="center">IPv6 Prefix</th>
+      <th class="center">Prefix Length</th>
+      <th class="center">Stable Flag</th>
+      <th class="center">Other Flags</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td class="center">2001:DB8:3::</td>
+      <td class="center">64</td>
+      <td class="center">True</td>
+      <td class="center">??</td>
+    </tr>
+  </tbody>
+</table>
+<p id="rfc.section.B.10.p.2">Frame: </p>
+<pre>
+85 07 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+01 ?? 01
+</pre>
+<p>
+  <a id="CREF11" class="info">[CREF11]<span class="info">RQ: FIXME: This test vector is incomplete.</span></a>
+</p>
+<h1 id="rfc.appendix.B.11"><a href="#rfc.appendix.B.11">B.11.</a> <a href="#test-vector-removing-a-local-onmesh-network" id="test-vector-removing-a-local-onmesh-network">Test Vector: Removing a local on-mesh network</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 6</li>
+  <li>CMD: 5 (<samp>CMD_VALUE_REMOVE</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+  <li>VALUE: IPv6 Prefix <samp>2001:DB8:3::</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.11.p.2">Frame: </p>
+<pre>
+86 05 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+</pre>
+<h1 id="rfc.appendix.B.12"><a href="#rfc.appendix.B.12">B.12.</a> <a href="#test-vector-removal-notification-of-an-onmesh-network" id="test-vector-removal-notification-of-an-onmesh-network">Test Vector: Removal notification of an on-mesh network</a></h1>
+<p/>
+
+<ul>
+  <li>NLI: 0</li>
+  <li>TID: 6</li>
+  <li>CMD: 8 (<samp>CMD_VALUE_REMOVED</samp>)</li>
+  <li>PROP: 90 (<samp>PROP_THREAD_ON_MESH_NETS</samp>)</li>
+  <li>VALUE: IPv6 Prefix <samp>2001:DB8:3::</samp></li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.B.12.p.2">Frame: </p>
+<pre>
+86 08 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+</pre>
+<h1 id="rfc.appendix.C"><a href="#rfc.appendix.C">Appendix C.</a> <a href="#example-sessions" id="example-sessions">Example Sessions</a></h1>
+<h1 id="rfc.appendix.C.1"><a href="#rfc.appendix.C.1">C.1.</a> <a href="#ncp-initialization" id="ncp-initialization">NCP Initialization</a></h1>
+<p>
+  <a id="CREF12" class="info">[CREF12]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p id="rfc.section.C.1.p.2">Check the protocol version to see if it is supported: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_GET:PROP_PROTOCOL_VERSION</li>
+  <li>CMD_VALUE_IS:PROP_PROTOCOL_VERSION</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.1.p.4">Check the NCP version to see if a firmware update may be necessary: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_GET:PROP_NCP_VERSION</li>
+  <li>CMD_VALUE_IS:PROP_NCP_VERSION</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.1.p.6">Check interface type to make sure that it is what we expect: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_GET:PROP_INTERFACE_TYPE</li>
+  <li>CMD_VALUE_IS:PROP_INTERFACE_TYPE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.1.p.8">If the host supports using vendor-specific commands, the vendor should be verified before using them: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_GET:PROP_VENDOR_ID</li>
+  <li>CMD_VALUE_IS:PROP_VENDOR_ID</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.1.p.10">Fetch the capability list so that we know what features this NCP supports: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_GET:PROP_CAPS</li>
+  <li>CMD_VALUE_IS:PROP_CAPS</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.1.p.12">If the NCP supports CAP_NET_SAVE, then we go ahead and recall the network: </p>
+<p/>
+
+<ul>
+  <li>CMD_NET_RECALL</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.appendix.C.2"><a href="#rfc.appendix.C.2">C.2.</a> <a href="#attaching-to-a-network" id="attaching-to-a-network">Attaching to a network</a></h1>
+<p>
+  <a id="CREF13" class="info">[CREF13]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p id="rfc.section.C.2.p.2">We make the assumption that the NCP is not currently associated with a network.  </p>
+<p id="rfc.section.C.2.p.3">Set the network properties, if they were not already set: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_PHY_CHAN</li>
+  <li>CMD_VALUE_IS:PROP_PHY_CHAN</li>
+  <li>CMD_VALUE_SET:PROP_NET_XPANID</li>
+  <li>CMD_VALUE_IS:PROP_NET_XPANID</li>
+  <li>CMD_VALUE_SET:PROP_MAC_15_4_PANID</li>
+  <li>CMD_VALUE_IS:PROP_MAC_15_4_PANID</li>
+  <li>CMD_VALUE_SET:PROP_NET_NETWORK_NAME</li>
+  <li>CMD_VALUE_IS:PROP_NET_NETWORK_NAME</li>
+  <li>CMD_VALUE_SET:PROP_NET_MASTER_KEY</li>
+  <li>CMD_VALUE_IS:PROP_NET_MASTER_KEY</li>
+  <li>CMD_VALUE_SET:PROP_NET_KEY_SEQUENCE_COUNTER</li>
+  <li>CMD_VALUE_IS:PROP_NET_KEY_SEQUENCE_COUNTER</li>
+  <li>CMD_VALUE_SET:PROP_NET_KEY_SWITCH_GUARDTIME</li>
+  <li>CMD_VALUE_IS:PROP_NET_KEY_SWITCH_GUARDTIME</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.2.p.5">Bring the network interface up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_IF_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_IF_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.2.p.7">Bring the routing stack up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.2.p.9">Some asynchronous events from the NCP: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_IS:PROP_NET_ROLE</li>
+  <li>CMD_VALUE_IS:PROP_NET_PARTITION_ID</li>
+  <li>CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.appendix.C.3"><a href="#rfc.appendix.C.3">C.3.</a> <a href="#successfully-joining-a-preexisting-network" id="successfully-joining-a-preexisting-network">Successfully joining a pre-existing network</a></h1>
+<p>
+  <a id="CREF14" class="info">[CREF14]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p id="rfc.section.C.3.p.2">This example session is identical to the above session up to the point where we set PROP_NET_IF_UP to true. From there, the behavior changes.  </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.3.p.4">Bring the routing stack up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.3.p.6">Some asynchronous events from the NCP: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_IS:PROP_NET_ROLE</li>
+  <li>CMD_VALUE_IS:PROP_NET_PARTITION_ID</li>
+  <li>CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.3.p.8">Now let's save the network settings to NVRAM: </p>
+<p/>
+
+<ul>
+  <li>CMD_NET_SAVE</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.appendix.C.4"><a href="#rfc.appendix.C.4">C.4.</a> <a href="#unsuccessfully-joining-a-preexisting-network" id="unsuccessfully-joining-a-preexisting-network">Unsuccessfully joining a pre-existing network</a></h1>
+<p id="rfc.section.C.4.p.1">This example session is identical to the above session up to the point where we set PROP_NET_IF_UP to true. From there, the behavior changes.  </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.4.p.3">Bring the routing stack up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.4.p.5">Some asynchronous events from the NCP: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_JOIN_NO_PEERS</li>
+  <li>CMD_VALUE_IS:PROP_NET_STACK_UP:FALSE</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.appendix.C.5"><a href="#rfc.appendix.C.5">C.5.</a> <a href="#detaching-from-a-network" id="detaching-from-a-network">Detaching from a network</a></h1>
+<p id="rfc.section.C.5.p.1">TBD </p>
+<h1 id="rfc.appendix.C.6"><a href="#rfc.appendix.C.6">C.6.</a> <a href="#attaching-to-a-saved-network" id="attaching-to-a-saved-network">Attaching to a saved network</a></h1>
+<p>
+  <a id="CREF15" class="info">[CREF15]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p id="rfc.section.C.6.p.2">Recall the saved network if you haven't already done so: </p>
+<p/>
+
+<ul>
+  <li>CMD_NET_RECALL</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.6.p.4">Bring the network interface up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_IF_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_IF_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.6.p.6">Bring the routing stack up: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.6.p.8">Some asynchronous events from the NCP: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_IS:PROP_NET_ROLE</li>
+  <li>CMD_VALUE_IS:PROP_NET_PARTITION_ID</li>
+  <li>CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS</li>
+</ul>
+
+<p> </p>
+<h1 id="rfc.appendix.C.7"><a href="#rfc.appendix.C.7">C.7.</a> <a href="#ncp-software-reset" id="ncp-software-reset">NCP Software Reset</a></h1>
+<p>
+  <a id="CREF16" class="info">[CREF16]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p/>
+
+<ul>
+  <li>CMD_RESET</li>
+  <li>CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_RESET_SOFTWARE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.7.p.3">Then jump to <a href="#ncp-initialization">Appendix C.1</a>.  </p>
+<h1 id="rfc.appendix.C.8"><a href="#rfc.appendix.C.8">C.8.</a> <a href="#adding-an-onmesh-prefix" id="adding-an-onmesh-prefix">Adding an on-mesh prefix</a></h1>
+<p id="rfc.section.C.8.p.1">TBD </p>
+<h1 id="rfc.appendix.C.9"><a href="#rfc.appendix.C.9">C.9.</a> <a href="#entering-lowpower-modes" id="entering-lowpower-modes">Entering low-power modes</a></h1>
+<p id="rfc.section.C.9.p.1">TBD </p>
+<h1 id="rfc.appendix.C.10"><a href="#rfc.appendix.C.10">C.10.</a> <a href="#sniffing-raw-packets" id="sniffing-raw-packets">Sniffing raw packets</a></h1>
+<p>
+  <a id="CREF17" class="info">[CREF17]<span class="info">RQ: FIXME: This example session is incomplete.</span></a>
+</p>
+<p id="rfc.section.C.10.p.2">This assumes that the NCP has been initialized.  </p>
+<p id="rfc.section.C.10.p.3">Optionally set the channel: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_PHY_CHAN:x</li>
+  <li>CMD_VALUE_IS:PROP_PHY_CHAN</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.10.p.5">Set the filter mode: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONITOR</li>
+  <li>CMD_VALUE_IS:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONITOR</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.10.p.7">Enable the raw stream: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_MAC_RAW_STREAM_ENABLED:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_MAC_RAW_STREAM_ENABLED:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.10.p.9">Enable the PHY directly: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_SET:PROP_PHY_ENABLED:TRUE</li>
+  <li>CMD_VALUE_IS:PROP_PHY_ENABLED:TRUE</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.10.p.11">Now we will get raw 802.15.4 packets asynchronously on PROP_STREAM_RAW: </p>
+<p/>
+
+<ul>
+  <li>CMD_VALUE_IS:PROP_STREAM_RAW:...</li>
+  <li>CMD_VALUE_IS:PROP_STREAM_RAW:...</li>
+  <li>CMD_VALUE_IS:PROP_STREAM_RAW:...</li>
+</ul>
+
+<p> </p>
+<p id="rfc.section.C.10.p.13">This mode may be entered even when associated with a network.  In that case, you should set <samp>PROP_MAC_PROMISCUOUS_MODE</samp> to <samp>MAC_PROMISCUOUS_MODE_PROMISCUOUS</samp> or <samp>MAC_PROMISCUOUS_MODE_NORMAL</samp>, so that you can avoid receiving packets from other networks or that are destined for other nodes.  </p>
+<h1 id="rfc.appendix.D"><a href="#rfc.appendix.D">Appendix D.</a> <a href="#glossary" id="glossary">Glossary</a></h1>
+<p>
+  <a id="CREF18" class="info">[CREF18]<span class="info">RQ: Alphabetize before finalization.</span></a>
+</p>
+<p/>
+
+<dl>
+  <dt>FCS</dt>
+  <dd style="margin-left: 8"><br/> Final Checksum. Bytes added to the end of a packet to help determine if the packet was received without corruption.</dd>
+  <dt>NCP</dt>
+  <dd style="margin-left: 8"><br/> Network Control Processor.</dd>
+  <dt>NLI</dt>
+  <dd style="margin-left: 8"><br/> Network Link Identifier. May be a value between zero and three. See <a href="#nli-network-link-identifier">Section 2.1.2</a> for more information.</dd>
+  <dt>OS</dt>
+  <dd style="margin-left: 8"><br/> Operating System, i.e. the IPv6 node using Spinel to control and manage one or more of its IPv6 network interfaces.</dd>
+  <dt>PHY</dt>
+  <dd style="margin-left: 8"><br/> Physical layer. Refers to characteristics and parameters related to the physical implementation and operation of a networking medium.</dd>
+  <dt>PUI</dt>
+  <dd style="margin-left: 8"><br/> Packed Unsigned Integer. A way to serialize an unsigned integer using one, two, or three bytes. Used throughout the Spinel protocol. See <a href="#packed-unsigned-integer">Section 3.2</a> for more information.</dd>
+  <dt>TID</dt>
+  <dd style="margin-left: 8"><br/> Transaction Identifier. May be a value between zero and fifteen. See <a href="#tid-transaction-identifier">Section 2.1.3</a> for more information.</dd>
+</dl>
+
+<p> </p>
+<h1 id="rfc.appendix.E"><a href="#rfc.appendix.E">Appendix E.</a> <a href="#acknowledgments" id="acknowledgments">Acknowledgments</a></h1>
+<p id="rfc.section.E.p.1">Thread is a registered trademark of The Thread Group, Inc.  </p>
+<p id="rfc.section.E.p.2">Special thanks to Nick Banks, Jonathan Hui, Abtin Keshavarzian, Yakun Xu, Piotr Szkotak, Arjuna Sivasithambaresan and Martin Turon for their substantial contributions and feedback related to this document.  </p>
+<p id="rfc.section.E.p.3">This document was prepared using <a href="https://github.com/miekg/mmark">mmark</a> by (Miek Gieben) and <a href="http://xml2rfc.ietf.org/">xml2rfc (version 2)</a>.  </p>
+<h1 id="rfc.authors">
+  <a href="#rfc.authors">Authors' Addresses</a>
+</h1>
+<div class="avoidbreak">
+  <address class="vcard">
+	<span class="vcardline">
+	  <span class="fn">Robert S. Quattlebaum</span> 
+	  <span class="n hidden">
+		<span class="family-name">Quattlebaum</span>
+	  </span>
+	</span>
+	<span class="org vcardline">Nest Labs, Inc.</span>
+	<span class="adr">
+	  <span class="vcardline">3400 Hillview Ave.</span>
+
+	  <span class="vcardline">
+		<span class="locality">Palo Alto</span>,  
+		<span class="region">California</span> 
+		<span class="code">94304</span>
+	  </span>
+	  <span class="country-name vcardline">USA</span>
+	</span>
+	<span class="vcardline">EMail: <a href="mailto:rquattle@nestlabs.com">rquattle@nestlabs.com</a></span>
+
+  </address>
+</div><div class="avoidbreak">
+  <address class="vcard">
+	<span class="vcardline">
+	  <span class="fn">James Woodyatt</span> (editor)
+	  <span class="n hidden">
+		<span class="family-name">Woodyatt</span>
+	  </span>
+	</span>
+	<span class="org vcardline">Nest Labs, Inc.</span>
+	<span class="adr">
+	  <span class="vcardline">3400 Hillview Ave.</span>
+
+	  <span class="vcardline">
+		<span class="locality">Palo Alto</span>,  
+		<span class="region">California</span> 
+		<span class="code">94304</span>
+	  </span>
+	  <span class="country-name vcardline">USA</span>
+	</span>
+	<span class="vcardline">EMail: <a href="mailto:jhw@nestlabs.com">jhw@nestlabs.com</a></span>
+
+  </address>
+</div>
+
+</body>
+</html>
diff --git a/doc/draft-rquattle-spinel-unified.txt b/doc/draft-rquattle-spinel-unified.txt
new file mode 100644
index 0000000..1db873e
--- /dev/null
+++ b/doc/draft-rquattle-spinel-unified.txt
@@ -0,0 +1,4648 @@
+
+
+
+
+Network Working Group                                     R. Quattlebaum
+Internet-Draft                                          J. Woodyatt, Ed.
+Intended status: Informational                           Nest Labs, Inc.
+Expires: December 24, 2017                                 June 22, 2017
+
+
+                    Spinel Host-Controller Protocol
+                 draft-rquattle-spinel-unified-ab5628a5
+
+Abstract
+
+   This document describes the Spinel protocol, which facilitates the
+   control and management of IPv6 network interfaces on devices where
+   general purpose application processors offload network functions at
+   their interfaces to network co-processors (NCP) connected by simple
+   communication links like serial data channels.  While initially
+   developed to support Thread(R), Spinel's layered design allows it to
+   be easily adapted to other similar network technologies.
+
+   This document also describes various Spinel specializations,
+   including support for the Thread(R) low-power mesh network
+   technology.
+
+Status of This Memo
+
+   This Internet-Draft is submitted in full conformance with the
+   provisions of BCP 78 and BCP 79.
+
+   Internet-Drafts are working documents of the Internet Engineering
+   Task Force (IETF).  Note that other groups may also distribute
+   working documents as Internet-Drafts.  The list of current Internet-
+   Drafts is at http://datatracker.ietf.org/drafts/current/.
+
+   Internet-Drafts are draft documents valid for a maximum of six months
+   and may be updated, replaced, or obsoleted by other documents at any
+   time.  It is inappropriate to use Internet-Drafts as reference
+   material or to cite them other than as "work in progress."
+
+   This Internet-Draft will expire on December 24, 2017.
+
+Copyright Notice
+
+   Copyright (c) 2017 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 1]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+   This document may not be modified, and derivative works of it may not
+   be created, and it may not be published except as an Internet-Draft.
+
+Table of Contents
+
+   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   7
+     1.1.  About this Draft  . . . . . . . . . . . . . . . . . . . .   7
+       1.1.1.  Scope . . . . . . . . . . . . . . . . . . . . . . . .   7
+       1.1.2.  Renumbering . . . . . . . . . . . . . . . . . . . . .   7
+   2.  Frame Format  . . . . . . . . . . . . . . . . . . . . . . . .   8
+     2.1.  Header Format . . . . . . . . . . . . . . . . . . . . . .   8
+       2.1.1.  FLG: Flag . . . . . . . . . . . . . . . . . . . . . .   9
+       2.1.2.  NLI: Network Link Identifier  . . . . . . . . . . . .   9
+       2.1.3.  TID: Transaction Identifier . . . . . . . . . . . . .   9
+       2.1.4.  Command Identifier (CMD)  . . . . . . . . . . . . . .   9
+       2.1.5.  Command Payload (Optional)  . . . . . . . . . . . . .  10
+   3.  Data Packing  . . . . . . . . . . . . . . . . . . . . . . . .  10
+     3.1.  Primitive Types . . . . . . . . . . . . . . . . . . . . .  11
+     3.2.  Packed Unsigned Integer . . . . . . . . . . . . . . . . .  11
+     3.3.  Data Blobs  . . . . . . . . . . . . . . . . . . . . . . .  12
+     3.4.  Structured Data . . . . . . . . . . . . . . . . . . . . .  13
+     3.5.  Arrays  . . . . . . . . . . . . . . . . . . . . . . . . .  13
+   4.  Commands  . . . . . . . . . . . . . . . . . . . . . . . . . .  14
+     4.1.  CMD 0: (Host->NCP) CMD_NOOP . . . . . . . . . . . . . . .  14
+     4.2.  CMD 1: (Host->NCP) CMD_RESET  . . . . . . . . . . . . . .  14
+     4.3.  CMD 2: (Host->NCP) CMD_PROP_VALUE_GET . . . . . . . . . .  14
+     4.4.  CMD 3: (Host->NCP) CMD_PROP_VALUE_SET . . . . . . . . . .  15
+     4.5.  CMD 4: (Host->NCP) CMD_PROP_VALUE_INSERT  . . . . . . . .  15
+     4.6.  CMD 5: (Host->NCP) CMD_PROP_VALUE_REMOVE  . . . . . . . .  16
+     4.7.  CMD 6: (NCP->Host) CMD_PROP_VALUE_IS  . . . . . . . . . .  17
+     4.8.  CMD 7: (NCP->Host) CMD_PROP_VALUE_INSERTED  . . . . . . .  17
+     4.9.  CMD 8: (NCP->Host) CMD_PROP_VALUE_REMOVED . . . . . . . .  18
+     4.10. CMD 18: (Host->NCP) CMD_PEEK  . . . . . . . . . . . . . .  18
+     4.11. CMD 19: (NCP->Host) CMD_PEEK_RET  . . . . . . . . . . . .  19
+     4.12. CMD 20: (Host->NCP) CMD_POKE  . . . . . . . . . . . . . .  19
+     4.13. CMD 21: (Host->NCP) CMD_PROP_VALUE_MULTI_GET  . . . . . .  19
+     4.14. CMD 22: (Host->NCP) CMD_PROP_VALUE_MULTI_SET  . . . . . .  20
+     4.15. CMD 23: (NCP->Host) CMD_PROP_VALUES_ARE . . . . . . . . .  21
+   5.  Properties  . . . . . . . . . . . . . . . . . . . . . . . . .  21
+     5.1.  Property Methods  . . . . . . . . . . . . . . . . . . . .  22
+     5.2.  Property Types  . . . . . . . . . . . . . . . . . . . . .  22
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 2]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+       5.2.1.  Single-Value Properties . . . . . . . . . . . . . . .  22
+       5.2.2.  Multiple-Value Properties . . . . . . . . . . . . . .  23
+       5.2.3.  Stream Properties . . . . . . . . . . . . . . . . . .  23
+     5.3.  Property Numbering  . . . . . . . . . . . . . . . . . . .  24
+     5.4.  Property Sections . . . . . . . . . . . . . . . . . . . .  24
+     5.5.  Core Properties . . . . . . . . . . . . . . . . . . . . .  25
+       5.5.1.  PROP 0: PROP_LAST_STATUS  . . . . . . . . . . . . . .  25
+       5.5.2.  PROP 1: PROP_PROTOCOL_VERSION . . . . . . . . . . . .  25
+       5.5.3.  PROP 2: PROP_NCP_VERSION  . . . . . . . . . . . . . .  26
+       5.5.4.  PROP 3: PROP_INTERFACE_TYPE . . . . . . . . . . . . .  27
+       5.5.5.  PROP 4: PROP_INTERFACE_VENDOR_ID  . . . . . . . . . .  27
+       5.5.6.  PROP 5: PROP_CAPS . . . . . . . . . . . . . . . . . .  27
+       5.5.7.  PROP 6: PROP_INTERFACE_COUNT  . . . . . . . . . . . .  29
+       5.5.8.  PROP 7: PROP_POWER_STATE  . . . . . . . . . . . . . .  29
+       5.5.9.  PROP 8: PROP_HWADDR . . . . . . . . . . . . . . . . .  30
+       5.5.10. PROP 9: PROP_LOCK . . . . . . . . . . . . . . . . . .  30
+       5.5.11. PROP 10: PROP_HOST_POWER_STATE  . . . . . . . . . . .  31
+       5.5.12. PROP 4104: PROP_UNSOL_UPDATE_FILTER . . . . . . . . .  32
+       5.5.13. PROP 4105: PROP_UNSOL_UPDATE_LIST . . . . . . . . . .  33
+     5.6.  Stream Properties . . . . . . . . . . . . . . . . . . . .  33
+       5.6.1.  PROP 112: PROP_STREAM_DEBUG . . . . . . . . . . . . .  33
+       5.6.2.  PROP 113: PROP_STREAM_RAW . . . . . . . . . . . . . .  34
+       5.6.3.  PROP 114: PROP_STREAM_NET . . . . . . . . . . . . . .  36
+       5.6.4.  PROP 115: PROP_STREAM_NET_INSECURE  . . . . . . . . .  37
+     5.7.  PHY Properties  . . . . . . . . . . . . . . . . . . . . .  37
+       5.7.1.  PROP 32: PROP_PHY_ENABLED . . . . . . . . . . . . . .  37
+       5.7.2.  PROP 33: PROP_PHY_CHAN  . . . . . . . . . . . . . . .  37
+       5.7.3.  PROP 34: PROP_PHY_CHAN_SUPPORTED  . . . . . . . . . .  38
+       5.7.4.  PROP 35: PROP_PHY_FREQ  . . . . . . . . . . . . . . .  38
+       5.7.5.  PROP 36: PROP_PHY_CCA_THRESHOLD . . . . . . . . . . .  38
+       5.7.6.  PROP 37: PROP_PHY_TX_POWER  . . . . . . . . . . . . .  38
+       5.7.7.  PROP 38: PROP_PHY_RSSI  . . . . . . . . . . . . . . .  38
+       5.7.8.  PROP 39: PROP_PHY_RX_SENSITIVITY  . . . . . . . . . .  39
+     5.8.  MAC Properties  . . . . . . . . . . . . . . . . . . . . .  39
+       5.8.1.  PROP 48: PROP_MAC_SCAN_STATE  . . . . . . . . . . . .  39
+       5.8.2.  PROP 49: PROP_MAC_SCAN_MASK . . . . . . . . . . . . .  39
+       5.8.3.  PROP 50: PROP_MAC_SCAN_PERIOD . . . . . . . . . . . .  39
+       5.8.4.  PROP 51: PROP_MAC_SCAN_BEACON . . . . . . . . . . . .  40
+       5.8.5.  PROP 52: PROP_MAC_15_4_LADDR  . . . . . . . . . . . .  40
+       5.8.6.  PROP 53: PROP_MAC_15_4_SADDR  . . . . . . . . . . . .  41
+       5.8.7.  PROP 54: PROP_MAC_15_4_PANID  . . . . . . . . . . . .  41
+       5.8.8.  PROP 55: PROP_MAC_RAW_STREAM_ENABLED  . . . . . . . .  41
+       5.8.9.  PROP 56: PROP_MAC_PROMISCUOUS_MODE  . . . . . . . . .  41
+       5.8.10. PROP 57: PROP_MAC_ENERGY_SCAN_RESULT  . . . . . . . .  42
+       5.8.11. PROP 4864: PROP_MAC_WHITELIST . . . . . . . . . . . .  42
+       5.8.12. PROP 4865: PROP_MAC_WHITELIST_ENABLED . . . . . . . .  42
+       5.8.13. PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED  . . . .  42
+       5.8.14. PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES   42
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 3]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+       5.8.15. PROP 4869:
+               SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES  . . . .  43
+       5.8.16. PROP 4870: PROP_MAC_BLACKLIST . . . . . . . . . . . .  43
+       5.8.17. PROP 4871: PROP_MAC_BLACKLIST_ENABLED . . . . . . . .  43
+     5.9.  NET Properties  . . . . . . . . . . . . . . . . . . . . .  43
+       5.9.1.  PROP 64: PROP_NET_SAVED . . . . . . . . . . . . . . .  43
+       5.9.2.  PROP 65: PROP_NET_IF_UP . . . . . . . . . . . . . . .  44
+       5.9.3.  PROP 66: PROP_NET_STACK_UP  . . . . . . . . . . . . .  44
+       5.9.4.  PROP 67: PROP_NET_ROLE  . . . . . . . . . . . . . . .  44
+       5.9.5.  PROP 68: PROP_NET_NETWORK_NAME  . . . . . . . . . . .  44
+       5.9.6.  PROP 69: PROP_NET_XPANID  . . . . . . . . . . . . . .  44
+       5.9.7.  PROP 70: PROP_NET_MASTER_KEY  . . . . . . . . . . . .  44
+       5.9.8.  PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER  . . . . . . .  45
+       5.9.9.  PROP 72: PROP_NET_PARTITION_ID  . . . . . . . . . . .  45
+       5.9.10. PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING . . . . . . .  45
+       5.9.11. PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME  . . . . . . .  45
+       5.9.12. PROP 75: PROP_NET_PSKC  . . . . . . . . . . . . . . .  45
+     5.10. IPv6 Properties . . . . . . . . . . . . . . . . . . . . .  45
+       5.10.1.  PROP 96: PROP_IPV6_LL_ADDR . . . . . . . . . . . . .  45
+       5.10.2.  PROP 97: PROP_IPV6_ML_ADDR . . . . . . . . . . . . .  45
+       5.10.3.  PROP 98: PROP_IPV6_ML_PREFIX . . . . . . . . . . . .  45
+       5.10.4.  PROP 99: PROP_IPV6_ADDRESS_TABLE . . . . . . . . . .  46
+       5.10.5.  PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD  . . . . . . .  46
+     5.11. Debug Properties  . . . . . . . . . . . . . . . . . . . .  46
+       5.11.1.  PROP 16384: PROP_DEBUG_TEST_ASSERT . . . . . . . . .  46
+       5.11.2.  PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL . . . . . . . .  46
+   6.  Status Codes  . . . . . . . . . . . . . . . . . . . . . . . .  47
+   7.  Technology: Thread(R) . . . . . . . . . . . . . . . . . . . .  48
+     7.1.  Capabilities  . . . . . . . . . . . . . . . . . . . . . .  49
+     7.2.  Properties  . . . . . . . . . . . . . . . . . . . . . . .  49
+       7.2.1.  PROP 80: PROP_THREAD_LEADER_ADDR  . . . . . . . . . .  49
+       7.2.2.  PROP 81: PROP_THREAD_PARENT . . . . . . . . . . . . .  49
+       7.2.3.  PROP 82: PROP_THREAD_CHILD_TABLE  . . . . . . . . . .  49
+       7.2.4.  PROP 83: PROP_THREAD_LEADER_RID . . . . . . . . . . .  50
+       7.2.5.  PROP 84: PROP_THREAD_LEADER_WEIGHT  . . . . . . . . .  50
+       7.2.6.  PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT  . . . . . .  50
+       7.2.7.  PROP 86: PROP_THREAD_NETWORK_DATA . . . . . . . . . .  50
+       7.2.8.  PROP 87: PROP_THREAD_NETWORK_DATA_VERSION . . . . . .  50
+       7.2.9.  PROP 88: PROP_THREAD_STABLE_NETWORK_DATA  . . . . . .  50
+       7.2.10. PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION  . .  50
+       7.2.11. PROP 90: PROP_THREAD_ON_MESH_NETS . . . . . . . . . .  51
+       7.2.12. PROP 91: PROP_THREAD_OFF_MESH_ROUTES  . . . . . . . .  51
+       7.2.13. PROP 92: PROP_THREAD_ASSISTING_PORTS  . . . . . . . .  51
+       7.2.14. PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE  . .  51
+       7.2.15. PROP 94: PROP_THREAD_MODE . . . . . . . . . . . . . .  52
+       7.2.16. PROP 5376: PROP_THREAD_CHILD_TIMEOUT  . . . . . . . .  52
+       7.2.17. PROP 5377: PROP_THREAD_RLOC16 . . . . . . . . . . . .  52
+       7.2.18. PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD . . .  52
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 4]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+       7.2.19. PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY  . . . . .  52
+       7.2.20. PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT . . . . . .  52
+       7.2.21. PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS  . . . . . .  52
+       7.2.22. PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU  . . . .  53
+       7.2.23. PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED  . . . . .  53
+       7.2.24. PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD . .  53
+       7.2.25. PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER  . . .  53
+       7.2.26. PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID  . . . . .  53
+       7.2.27. PROP 5387: PROP_THREAD_NEIGHBOR_TABLE . . . . . . . .  53
+       7.2.28. PROP 5388: PROP_THREAD_CHILD_COUNT_MAX  . . . . . . .  54
+       7.2.29. PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA  . . . . .  54
+       7.2.30. PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA . .  54
+       7.2.31. PROP 5391: PROP_THREAD_JOINERS  . . . . . . . . . . .  54
+       7.2.32. PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED . . . . .  55
+       7.2.33. PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED  . . . . . .  55
+       7.2.34. PROP 5394: PROP_THREAD_TMF_PROXY_STREAM . . . . . . .  55
+       7.2.35. PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG  . .  55
+       7.2.36. PROP 5396:
+               PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING . . . . .  56
+       7.2.37. PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID . . . . .  56
+       7.2.38. PROP 5398: PROP_THREAD_STEERING_DATA  . . . . . . . .  56
+   8.  Feature: Network Save . . . . . . . . . . . . . . . . . . . .  56
+     8.1.  Commands  . . . . . . . . . . . . . . . . . . . . . . . .  57
+       8.1.1.  CMD 9: (Host->NCP) CMD_NET_SAVE . . . . . . . . . . .  57
+       8.1.2.  CMD 10: (Host->NCP) CMD_NET_CLEAR . . . . . . . . . .  57
+       8.1.3.  CMD 11: (Host->NCP) CMD_NET_RECALL  . . . . . . . . .  58
+   9.  Feature: Host Buffer Offload  . . . . . . . . . . . . . . . .  58
+     9.1.  Commands  . . . . . . . . . . . . . . . . . . . . . . . .  58
+       9.1.1.  CMD 12: (NCP->Host) CMD_HBO_OFFLOAD . . . . . . . . .  58
+       9.1.2.  CMD 13: (NCP->Host) CMD_HBO_RECLAIM . . . . . . . . .  59
+       9.1.3.  CMD 14: (NCP->Host) CMD_HBO_DROP  . . . . . . . . . .  59
+       9.1.4.  CMD 15: (Host->NCP) CMD_HBO_OFFLOADED . . . . . . . .  59
+       9.1.5.  CMD 16: (Host->NCP) CMD_HBO_RECLAIMED . . . . . . . .  59
+       9.1.6.  CMD 17: (Host->NCP) CMD_HBO_DROPPED . . . . . . . . .  59
+     9.2.  Properties  . . . . . . . . . . . . . . . . . . . . . . .  59
+       9.2.1.  PROP 10: PROP_HBO_MEM_MAX . . . . . . . . . . . . . .  59
+       9.2.2.  PROP 11: PROP_HBO_BLOCK_MAX . . . . . . . . . . . . .  60
+   10. Feature: Jam Detection  . . . . . . . . . . . . . . . . . . .  60
+     10.1.  Properties . . . . . . . . . . . . . . . . . . . . . . .  60
+       10.1.1.  PROP 4608: PROP_JAM_DETECT_ENABLE  . . . . . . . . .  60
+       10.1.2.  PROP 4609: PROP_JAM_DETECTED . . . . . . . . . . . .  61
+       10.1.3.  PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD  . . . . .  61
+       10.1.4.  PROP 4611: PROP_JAM_DETECT_WINDOW  . . . . . . . . .  61
+       10.1.5.  PROP 4612: PROP_JAM_DETECT_BUSY  . . . . . . . . . .  62
+       10.1.6.  PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP  . . . . .  62
+   11. Feature: GPIO Access  . . . . . . . . . . . . . . . . . . . .  62
+     11.1.  Properties . . . . . . . . . . . . . . . . . . . . . . .  63
+       11.1.1.  PROP 4096: PROP_GPIO_CONFIG  . . . . . . . . . . . .  63
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 5]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+       11.1.2.  PROP 4098: PROP_GPIO_STATE . . . . . . . . . . . . .  64
+       11.1.3.  PROP 4099: PROP_GPIO_STATE_SET . . . . . . . . . . .  64
+       11.1.4.  PROP 4100: PROP_GPIO_STATE_CLEAR . . . . . . . . . .  65
+   12. Feature: True Random Number Generation  . . . . . . . . . . .  65
+     12.1.  Properties . . . . . . . . . . . . . . . . . . . . . . .  65
+       12.1.1.  PROP 4101: PROP_TRNG_32  . . . . . . . . . . . . . .  65
+       12.1.2.  PROP 4102: PROP_TRNG_128 . . . . . . . . . . . . . .  66
+       12.1.3.  PROP 4103: PROP_TRNG_RAW_32  . . . . . . . . . . . .  66
+   13. Security Considerations . . . . . . . . . . . . . . . . . . .  67
+     13.1.  Raw Application Access . . . . . . . . . . . . . . . . .  67
+     14.1.  URIs . . . . . . . . . . . . . . . . . . . . . . . . . .  67
+   Appendix A.  Framing Protocol . . . . . . . . . . . . . . . . . .  67
+     A.1.  UART Recommendations  . . . . . . . . . . . . . . . . . .  67
+       A.1.1.  UART Bit Rate Detection . . . . . . . . . . . . . . .  68
+       A.1.2.  HDLC-Lite . . . . . . . . . . . . . . . . . . . . . .  68
+     A.2.  SPI Recommendations . . . . . . . . . . . . . . . . . . .  69
+       A.2.1.  SPI Framing Protocol  . . . . . . . . . . . . . . . .  70
+     A.3.  I^2C Recommendations  . . . . . . . . . . . . . . . . . .  72
+     A.4.  Native USB Recommendations  . . . . . . . . . . . . . . .  72
+   Appendix B.  Test Vectors . . . . . . . . . . . . . . . . . . . .  72
+     B.1.  Test Vector: Packed Unsigned Integer  . . . . . . . . . .  72
+     B.2.  Test Vector: Reset Command  . . . . . . . . . . . . . . .  72
+     B.3.  Test Vector: Reset Notification . . . . . . . . . . . . .  73
+     B.4.  Test Vector: Scan Beacon  . . . . . . . . . . . . . . . .  73
+     B.5.  Test Vector: Inbound IPv6 Packet  . . . . . . . . . . . .  73
+     B.6.  Test Vector: Outbound IPv6 Packet . . . . . . . . . . . .  74
+     B.7.  Test Vector: Fetch list of on-mesh networks . . . . . . .  74
+     B.8.  Test Vector: Returned list of on-mesh networks  . . . . .  74
+     B.9.  Test Vector: Adding an on-mesh network  . . . . . . . . .  74
+     B.10. Test Vector: Insertion notification of an on-mesh network  75
+     B.11. Test Vector: Removing a local on-mesh network . . . . . .  75
+     B.12. Test Vector: Removal notification of an on-mesh network .  76
+   Appendix C.  Example Sessions . . . . . . . . . . . . . . . . . .  76
+     C.1.  NCP Initialization  . . . . . . . . . . . . . . . . . . .  76
+     C.2.  Attaching to a network  . . . . . . . . . . . . . . . . .  77
+     C.3.  Successfully joining a pre-existing network . . . . . . .  77
+     C.4.  Unsuccessfully joining a pre-existing network . . . . . .  78
+     C.5.  Detaching from a network  . . . . . . . . . . . . . . . .  78
+     C.6.  Attaching to a saved network  . . . . . . . . . . . . . .  79
+     C.7.  NCP Software Reset  . . . . . . . . . . . . . . . . . . .  79
+     C.8.  Adding an on-mesh prefix  . . . . . . . . . . . . . . . .  79
+     C.9.  Entering low-power modes  . . . . . . . . . . . . . . . .  79
+     C.10. Sniffing raw packets  . . . . . . . . . . . . . . . . . .  79
+   Appendix D.  Glossary . . . . . . . . . . . . . . . . . . . . . .  80
+   Appendix E.  Acknowledgments  . . . . . . . . . . . . . . . . . .  81
+   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  82
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 6]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+1.  Introduction
+
+   Spinel is a host-controller protocol designed to enable
+   interoperation over simple serial connections between general purpose
+   device operating systems (OS) and network co-processors (NCP) for the
+   purpose of controlling and managing their IPv6 network interfaces,
+   achieving the following goals:
+
+   o  Adopt a layered approach to the protocol design, allowing future
+      support for other network protocols.
+   o  Minimize the number of required commands/methods by providing a
+      rich, property-based API.
+   o  Support NCPs capable of being connected to more than one network
+      at a time.
+   o  Gracefully handle the addition of new features and capabilities
+      without necessarily breaking backward compatibility.
+   o  Be as minimal and light-weight as possible without unnecessarily
+      sacrificing flexibility.
+
+   On top of this core framework, we define the properties and commands
+   to enable various features and network protocols.
+
+1.1.  About this Draft
+
+   This document is currently in a draft status and is changing often.
+   This section discusses some ideas for changes to the protocol that
+   haven't yet been fully specified, as well as some of the impetus for
+   the current design.
+
+1.1.1.  Scope
+
+   The eventual intent is to have two documents: A Spinel basis document
+   which discusses the network-technology-agnostic mechanisms and a
+   Thread(R) specialization document which describes all of the
+   Thread(R)-specific implementation details.  Currently, this document
+   covers both.
+
+1.1.2.  Renumbering
+
+   Efforts are currently maintained to try to prevent overtly backward-
+   incompatible changes to the existing protocol, but if you are
+   implementing Spinel in your own products you should expect there to
+   be at least one large renumbering event and major version number
+   change before the standard is considered "baked".  All changes will
+   be clearly marked and documented to make such a transition as easy as
+   possible.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 7]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   To allow conclusive detection of protocol (in)compatibility between
+   the host and the NCP, the following commands and properties are
+   already considered to be "baked" and will not change:
+
+   o  Command IDs zero through eight.  (Reset, No-op, and Property-Value
+      Commands)
+   o  Property IDs zero through two.  (Last status, Protocol Version,
+      and NCP Version)
+
+   Renumbering would be undertaken in order to better organize the
+   allocation of property IDs and capability IDs.  One of the initial
+   goals of this protocol was for it to be possible for a host or NCP to
+   only implement properties with values less than 127 and for the NCP
+   to still be usable---relegating all larger property values for extra
+   features or other capabilities that aren't strictly necessary.  This
+   would allow simple implementations to avoid the need to implement
+   support for PUIs (Section 3.2).
+
+   As time has gone by and the protocol has become more fleshed out, it
+   has become clear that some of the initial allocations were inadequate
+   and should be revisited if we want to try to achieve the original
+   goal.
+
+2.  Frame Format
+
+   A frame is defined simply as the concatenation of
+
+   o  A header byte
+   o  A command (up to three bytes, see Section 3.2 for format)
+   o  An optional command payload
+
+                 +---------+--------+-----+-------------+
+                 | Octets: |   1    | 1-3 |      n      |
+                 +---------+--------+-----+-------------+
+                 | Fields: | HEADER | CMD | CMD_PAYLOAD |
+                 +---------+--------+-----+-------------+
+
+2.1.  Header Format
+
+   The header byte is broken down as follows:
+
+                       0   1   2   3   4   5   6   7
+                     +---+---+---+---+---+---+---+---+
+                     |  FLG  |  NLI  |      TID      |
+                     +---+---+---+---+---+---+---+---+
+
+   [CREF1]
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 8]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+2.1.1.  FLG: Flag
+
+   The flag field of the header byte ("FLG") is always set to the value
+   two (or "10" in binary).  Any frame received with these bits set to
+   any other value else MUST NOT be considered a Spinel frame.
+
+   This convention allows Spinel to be line compatible with BTLE HCI.
+   By defining the first two bit in this way we can disambiguate between
+   Spinel frames and HCI frames (which always start with either "0x01"
+   or "0x04") without any additional framing overhead.
+
+2.1.2.  NLI: Network Link Identifier
+
+   The Network Link Identifier (NLI) is a number between 0 and 3, which
+   is associated by the OS with one of up to four IPv6 zone indices
+   corresponding to conceptual IPv6 interfaces on the NCP.  This allows
+   the protocol to support IPv6 nodes connecting simultaneously to more
+   than one IPv6 network link using a single NCP instance.  The first
+   Network Link Identifier (0) MUST refer to a distinguished conceptual
+   interface provided by the NCP for its IPv6 link type.  The other
+   three Network Link Identifiers (1, 2 and 3) MAY be dissociated from
+   any conceptual interface.
+
+2.1.3.  TID: Transaction Identifier
+
+   The least significant bits of the header represent the Transaction
+   Identifier(TID).  The TID is used for correlating responses to the
+   commands which generated them.
+
+   When a command is sent from the host, any reply to that command sent
+   by the NCP will use the same value for the TID.  When the host
+   receives a frame that matches the TID of the command it sent, it can
+   easily recognize that frame as the actual response to that command.
+
+   The TID value of zero (0) is used for commands to which a correlated
+   response is not expected or needed, such as for unsolicited update
+   commands sent to the host from the NCP.
+
+2.1.4.  Command Identifier (CMD)
+
+   The command identifier is a 21-bit unsigned integer encoded in up to
+   three bytes using the packed unsigned integer format described in
+   Section 3.2.  This encoding allows for up to 2,097,152 individual
+   commands, with the first 127 commands represented as a single byte.
+   Command identifiers larger than 2,097,151 are explicitly forbidden.
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017               [Page 9]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+          +-----------------------+----------------------------+
+          |       CID Range       |        Description         |
+          +-----------------------+----------------------------+
+          |         0 - 63        | Reserved for core commands |
+          |      64 - 15,359      |       _UNALLOCATED_        |
+          |    15,360 - 16,383    |      Vendor-specific       |
+          |   16,384 - 1,999,999  |       _UNALLOCATED_        |
+          | 2,000,000 - 2,097,151 |   Experimental use only    |
+          +-----------------------+----------------------------+
+
+2.1.5.  Command Payload (Optional)
+
+   Depending on the semantics of the command in question, a payload MAY
+   be included in the frame.  The exact composition and length of the
+   payload is defined by the command identifier.
+
+3.  Data Packing
+
+   Data serialization for properties is performed using a light-weight
+   data packing format which was loosely inspired by D-Bus.  The format
+   of a serialization is defined by a specially formatted string.
+
+   This packing format is used for notational convenience.  While this
+   string-based datatype format has been designed so that the strings
+   may be directly used by a structured data parser, such a thing is not
+   required to implement Spinel.  Indeed, higly constrained applications
+   may find such a thing to be too heavyweight.
+
+   Goals:
+
+   o  Be lightweight and favor direct representation of values.
+   o  Use an easily readable and memorable format string.
+   o  Support lists and structures.
+   o  Allow properties to be appended to structures while maintaining
+      backward compatibility.
+
+   Each primitive datatype has an ASCII character associated with it.
+   Structures can be represented as strings of these characters.  For
+   example:
+
+   o  "C": A single unsigned byte.
+   o  "C6U": A single unsigned byte, followed by a 128-bit IPv6 address,
+      followed by a zero-terminated UTF8 string.
+   o  "A(6)": An array of concatenated IPv6 addresses
+
+   In each case, the data is represented exactly as described.  For
+   example, an array of 10 IPv6 address is stored as 160 bytes.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 10]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+3.1.  Primitive Types
+
+   +----------+----------------------+---------------------------------+
+   |   Char   | Name                 | Description                     |
+   +----------+----------------------+---------------------------------+
+   |   "."    | DATATYPE_VOID        | Empty data type. Used           |
+   |          |                      | internally.                     |
+   |   "b"    | DATATYPE_BOOL        | Boolean value. Encoded in       |
+   |          |                      | 8-bits as either 0x00 or 0x01.  |
+   |          |                      | All other values are illegal.   |
+   |   "C"    | DATATYPE_UINT8       | Unsigned 8-bit integer.         |
+   |   "c"    | DATATYPE_INT8        | Signed 8-bit integer.           |
+   |   "S"    | DATATYPE_UINT16      | Unsigned 16-bit integer.        |
+   |   "s"    | DATATYPE_INT16       | Signed 16-bit integer.          |
+   |   "L"    | DATATYPE_UINT32      | Unsigned 32-bit integer.        |
+   |   "l"    | DATATYPE_INT32       | Signed 32-bit integer.          |
+   |   "i"    | DATATYPE_UINT_PACKED | Packed Unsigned Integer. See    |
+   |          |                      | Section 3.2.                    |
+   |   "6"    | DATATYPE_IPv6ADDR    | IPv6 Address. (Big-endian)      |
+   |   "E"    | DATATYPE_EUI64       | EUI-64 Address. (Big-endian)    |
+   |   "e"    | DATATYPE_EUI48       | EUI-48 Address. (Big-endian)    |
+   |   "D"    | DATATYPE_DATA        | Arbitrary data. See Section     |
+   |          |                      | 3.3.                            |
+   |   "d"    | DATATYPE_DATA_WLEN   | Arbitrary data with prepended   |
+   |          |                      | length. See Section 3.3.        |
+   |   "U"    | DATATYPE_UTF8        | Zero-terminated UTF8-encoded    |
+   |          |                      | string.                         |
+   | "t(...)" | DATATYPE_STRUCT      | Structured datatype with        |
+   |          |                      | prepended length. See Section   |
+   |          |                      | 3.4.                            |
+   | "A(...)" | DATATYPE_ARRAY       | Array of datatypes. Compound    |
+   |          |                      | type. See Section 3.5.          |
+   +----------+----------------------+---------------------------------+
+
+   All multi-byte values are little-endian unless explicitly stated
+   otherwise.
+
+3.2.  Packed Unsigned Integer
+
+   For certain types of integers, such command or property identifiers,
+   usually have a value on the wire that is less than 127.  However, in
+   order to not preclude the use of values larger than 255, we would
+   need to add an extra byte.  Doing this would add an extra byte to the
+   majority of instances, which can add up in terms of bandwidth.
+
+   The packed unsigned integer format is based on the unsigned integer
+   format in EXI [1], except that we limit the maximum value to the
+   largest value that can be encoded into three bytes(2,097,151).
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 11]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   For all values less than 127, the packed form of the number is simply
+   a single byte which directly represents the number.  For values
+   larger than 127, the following process is used to encode the value:
+
+   1.  The unsigned integer is broken up into _n_ 7-bit chunks and
+       placed into _n_ octets, leaving the most significant bit of each
+       octet unused.
+   2.  Order the octets from least-significant to most-significant.
+       (Little-endian)
+   3.  Clear the most significant bit of the most significant octet.
+       Set the least significant bit on all other octets.
+
+   Where _n_ is the smallest number of 7-bit chunks you can use to
+   represent the given value.
+
+   Take the value 1337, for example:
+
+                              1337 => 0x0539
+                                   => [39 0A]
+                                   => [B9 0A]
+
+   To decode the value, you collect the 7-bit chunks until you find an
+   octet with the most significant bit clear.
+
+3.3.  Data Blobs
+
+   There are two types for data blobs: "d" and "D".
+
+   o  "d" has the length of the data (in bytes) prepended to the data
+      (with the length encoded as type "S").  The size of the length
+      field is not included in the length.
+   o  "D" does not have a prepended length: the length of the data is
+      implied by the bytes remaining to be parsed.  It is an error for
+      "D" to not be the last type in a type in a type signature.
+
+   This dichotomy allows for more efficient encoding by eliminating
+   redundency.  If the rest of the buffer is a data blob, encoding the
+   length would be redundant because we already know how many bytes are
+   in the rest of the buffer.
+
+   In some cases we use "d" even if it is the last field in a type
+   signature.  We do this to allow for us to be able to append
+   additional fields to the type signature if necessary in the future.
+   This is usually the case with embedded structs, like in the scan
+   results.
+
+   For example, let's say we have a buffer that is encoded with the
+   datatype signature of "CLLD".  In this case, it is pretty easy to
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 12]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   tell where the start and end of the data blob is: the start is 9
+   bytes from the start of the buffer, and its length is the length of
+   the buffer minus 9. (9 is the number of bytes taken up by a byte and
+   two longs)
+
+   The datatype signature "CLLDU" is illegal because we can't determine
+   where the last field (a zero-terminated UTF8 string) starts.  But the
+   datatype "CLLdU" _is_ legal, because the parser can determine the
+   exact length of the data blob-- allowing it to know where the start
+   of the next field would be.
+
+3.4.  Structured Data
+
+   The structure data type ("t(...)") is a way of bundling together
+   several fields into a single structure.  It can be thought of as a
+   "d" type except that instead of being opaque, the fields in the
+   content are known.  This is useful for things like scan results where
+   you have substructures which are defined by different layers.
+
+   For example, consider the type signature "Lt(ES)t(6C)".  In this
+   hypothetical case, the first struct is defined by the MAC layer, and
+   the second struct is defined by the PHY layer.  Because of the use of
+   structures, we know exactly what part comes from that layer.
+   Additionally, we can add fields to each structure without introducing
+   backward compatability problems: Data encoded as "Lt(ESU)t(6C)"
+   (Notice the extra "U") will decode just fine as "Lt(ES)t(6C)".
+   Additionally, if we don't care about the MAC layer and only care
+   about the network layer, we could parse as "Lt()t(6C)".
+
+   Note that data encoded as "Lt(ES)t(6C)" will also parse as "Ldd",
+   with the structures from both layers now being opaque data blobs.
+
+3.5.  Arrays
+
+   An array is simply a concatenated set of _n_ data encodings.  For
+   example, the type "A(6)" is simply a list of IPv6 addresses---one
+   after the other.  The type "A(6E)" likewise a concatenation of IPv6-
+   address/EUI-64 pairs.
+
+   If an array contains many fields, the fields will often be surrounded
+   by a structure ("t(...)").  This effectively prepends each item in
+   the array with its length.  This is useful for improving parsing
+   performance or to allow additional fields to be added in the future
+   in a backward compatible way.  If there is a high certainty that
+   additional fields will never be added, the struct may be omitted
+   (saving two bytes per item).
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 13]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   This specification does not define a way to embed an array as a field
+   alongside other fields.
+
+4.  Commands
+
+4.1.  CMD 0: (Host->NCP) CMD_NOOP
+
+                      +---------+--------+----------+
+                      | Octets: |   1    |    1     |
+                      +---------+--------+----------+
+                      | Fields: | HEADER | CMD_NOOP |
+                      +---------+--------+----------+
+
+   No-Operation command.  Induces the NCP to send a success status back
+   to the host.  This is primarily used for liveliness checks.
+
+   The command payload for this command SHOULD be empty.  The receiver
+   MUST ignore any non-empty command payload.
+
+   There is no error condition for this command.
+
+4.2.  CMD 1: (Host->NCP) CMD_RESET
+
+                     +---------+--------+-----------+
+                     | Octets: |   1    |     1     |
+                     +---------+--------+-----------+
+                     | Fields: | HEADER | CMD_RESET |
+                     +---------+--------+-----------+
+
+   Reset NCP command.  Causes the NCP to perform a software reset.  Due
+   to the nature of this command, the TID is ignored.  The host should
+   instead wait for a "CMD_PROP_VALUE_IS" command from the NCP
+   indicating "PROP_LAST_STATUS" has been set to
+   "STATUS_RESET_SOFTWARE".
+
+   The command payload for this command SHOULD be empty.  The receiver
+   MUST ignore any non-empty command payload.
+
+   If an error occurs, the value of "PROP_LAST_STATUS" will be emitted
+   instead with the value set to the generated status code for the
+   error.
+
+4.3.  CMD 2: (Host->NCP) CMD_PROP_VALUE_GET
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 14]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+            +---------+--------+--------------------+---------+
+            | Octets: |   1    |         1          |   1-3   |
+            +---------+--------+--------------------+---------+
+            | Fields: | HEADER | CMD_PROP_VALUE_GET | PROP_ID |
+            +---------+--------+--------------------+---------+
+
+   Get property value command.  Causes the NCP to emit a
+   "CMD_PROP_VALUE_IS" command for the given property identifier.
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2.
+
+   If an error occurs, the value of "PROP_LAST_STATUS" will be emitted
+   instead with the value set to the generated status code for the
+   error.
+
+4.4.  CMD 3: (Host->NCP) CMD_PROP_VALUE_SET
+
+        +---------+--------+--------------------+---------+-------+
+        | Octets: |   1    |         1          |   1-3   |   n   |
+        +---------+--------+--------------------+---------+-------+
+        | Fields: | HEADER | CMD_PROP_VALUE_SET | PROP_ID | VALUE |
+        +---------+--------+--------------------+---------+-------+
+
+   Set property value command.  Instructs the NCP to set the given
+   property to the specific given value, replacing any previous value.
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the property value.  The exact format of the property value is
+   defined by the property.
+
+   If an error occurs, the value of "PROP_LAST_STATUS" will be emitted
+   with the value set to the generated status code for the error.
+
+4.5.  CMD 4: (Host->NCP) CMD_PROP_VALUE_INSERT
+
+      +---------+--------+-----------------------+---------+-------+
+      | Octets: |   1    |           1           |   1-3   |   n   |
+      +---------+--------+-----------------------+---------+-------+
+      | Fields: | HEADER | CMD_PROP_VALUE_INSERT | PROP_ID | VALUE |
+      +---------+--------+-----------------------+---------+-------+
+
+   Insert value into property command.  Instructs the NCP to insert the
+   given value into a list-oriented property, without removing other
+   items in the list.  The resulting order of items in the list is
+   defined by the individual property being operated on.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 15]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the value to be inserted.  The exact format of the value is
+   defined by the property.
+
+   If the type signature of the property specified by "PROP_ID" consists
+   of a single structure enclosed by an array ("A(t(...))"), then the
+   contents of "VALUE" MUST contain the contents of the structure
+   ("...") rather than the serialization of the whole item ("t(...)").
+   Specifically, the length of the structure MUST NOT be prepended to
+   "VALUE".  This helps to eliminate redundant data.
+
+   If an error occurs, the value of "PROP_LAST_STATUS" will be emitted
+   with the value set to the generated status code for the error.
+
+4.6.  CMD 5: (Host->NCP) CMD_PROP_VALUE_REMOVE
+
+      +---------+--------+-----------------------+---------+-------+
+      | Octets: |   1    |           1           |   1-3   |   n   |
+      +---------+--------+-----------------------+---------+-------+
+      | Fields: | HEADER | CMD_PROP_VALUE_REMOVE | PROP_ID | VALUE |
+      +---------+--------+-----------------------+---------+-------+
+
+   Remove value from property command.  Instructs the NCP to remove the
+   given value from a list-oriented property, without affecting other
+   items in the list.  The resulting order of items in the list is
+   defined by the individual property being operated on.
+
+   Note that this command operates _by value_, not by index!
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the value to be removed.  The exact format of the value is defined
+   by the property.
+
+   If the type signature of the property specified by "PROP_ID" consists
+   of a single structure enclosed by an array ("A(t(...))"), then the
+   contents of "VALUE" MUST contain the contents of the structure
+   ("...") rather than the serialization of the whole item ("t(...)").
+   Specifically, the length of the structure MUST NOT be prepended to
+   "VALUE".  This helps to eliminate redundant data.
+
+   If an error occurs, the value of "PROP_LAST_STATUS" will be emitted
+   with the value set to the generated status code for the error.
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 16]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+4.7.  CMD 6: (NCP->Host) CMD_PROP_VALUE_IS
+
+        +---------+--------+-------------------+---------+-------+
+        | Octets: |   1    |         1         |   1-3   |   n   |
+        +---------+--------+-------------------+---------+-------+
+        | Fields: | HEADER | CMD_PROP_VALUE_IS | PROP_ID | VALUE |
+        +---------+--------+-------------------+---------+-------+
+
+   Property value notification command.  This command can be sent by the
+   NCP in response to a previous command from the host, or it can be
+   sent by the NCP in an unsolicited fashion to notify the host of
+   various state changes asynchronously.
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the current value of the given property.
+
+4.8.  CMD 7: (NCP->Host) CMD_PROP_VALUE_INSERTED
+
+     +---------+--------+-------------------------+---------+-------+
+     | Octets: |   1    |            1            |   1-3   |   n   |
+     +---------+--------+-------------------------+---------+-------+
+     | Fields: | HEADER | CMD_PROP_VALUE_INSERTED | PROP_ID | VALUE |
+     +---------+--------+-------------------------+---------+-------+
+
+   Property value insertion notification command.  This command can be
+   sent by the NCP in response to the "CMD_PROP_VALUE_INSERT" command,
+   or it can be sent by the NCP in an unsolicited fashion to notify the
+   host of various state changes asynchronously.
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the value that was inserted into the given property.
+
+   If the type signature of the property specified by "PROP_ID" consists
+   of a single structure enclosed by an array ("A(t(...))"), then the
+   contents of "VALUE" MUST contain the contents of the structure
+   ("...") rather than the serialization of the whole item ("t(...)").
+   Specifically, the length of the structure MUST NOT be prepended to
+   "VALUE".  This helps to eliminate redundant data.
+
+   The resulting order of items in the list is defined by the given
+   property.
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 17]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+4.9.  CMD 8: (NCP->Host) CMD_PROP_VALUE_REMOVED
+
+      +---------+--------+------------------------+---------+-------+
+      | Octets: |   1    |           1            |   1-3   |   n   |
+      +---------+--------+------------------------+---------+-------+
+      | Fields: | HEADER | CMD_PROP_VALUE_REMOVED | PROP_ID | VALUE |
+      +---------+--------+------------------------+---------+-------+
+
+   Property value removal notification command.  This command can be
+   sent by the NCP in response to the "CMD_PROP_VALUE_REMOVE" command,
+   or it can be sent by the NCP in an unsolicited fashion to notify the
+   host of various state changes asynchronously.
+
+   Note that this command operates _by value_, not by index!
+
+   The payload for this command is the property identifier encoded in
+   the packed unsigned integer format described in Section 3.2, followed
+   by the value that was removed from the given property.
+
+   If the type signature of the property specified by "PROP_ID" consists
+   of a single structure enclosed by an array ("A(t(...))"), then the
+   contents of "VALUE" MUST contain the contents of the structure
+   ("...") rather than the serialization of the whole item ("t(...)").
+   Specifically, the length of the structure MUST NOT be prepended to
+   "VALUE".  This helps to eliminate redundant data.
+
+   The resulting order of items in the list is defined by the given
+   property.
+
+4.10.  CMD 18: (Host->NCP) CMD_PEEK
+
+             +---------+--------+----------+---------+-------+
+             | Octets: |   1    |    1     |    4    |   2   |
+             +---------+--------+----------+---------+-------+
+             | Fields: | HEADER | CMD_PEEK | ADDRESS | COUNT |
+             +---------+--------+----------+---------+-------+
+
+   This command allows the NCP to fetch values from the RAM of the NCP
+   for debugging purposes.  Upon success, "CMD_PEEK_RET" is sent from
+   the NCP to the host.  Upon failure, "PROP_LAST_STATUS" is emitted
+   with the appropriate error indication.
+
+   Due to the low-level nature of this command, certain error conditions
+   may induce the NCP to reset.
+
+   The NCP MAY prevent certain regions of memory from being accessed.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 18]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   The implementation of this command has security implications.  See
+   Section 13 for more information.
+
+   This command requires the capability "CAP_PEEK_POKE" to be present.
+
+4.11.  CMD 19: (NCP->Host) CMD_PEEK_RET
+
+       +---------+--------+--------------+---------+-------+-------+
+       | Octets: |   1    |      1       |    4    |   2   |   n   |
+       +---------+--------+--------------+---------+-------+-------+
+       | Fields: | HEADER | CMD_PEEK_RET | ADDRESS | COUNT | BYTES |
+       +---------+--------+--------------+---------+-------+-------+
+
+   This command contains the contents of memory that was requested by a
+   previous call to "CMD_PEEK".
+
+   This command requires the capability "CAP_PEEK_POKE" to be present.
+
+4.12.  CMD 20: (Host->NCP) CMD_POKE
+
+         +---------+--------+----------+---------+-------+-------+
+         | Octets: |   1    |    1     |    4    |   2   |   n   |
+         +---------+--------+----------+---------+-------+-------+
+         | Fields: | HEADER | CMD_POKE | ADDRESS | COUNT | BYTES |
+         +---------+--------+----------+---------+-------+-------+
+
+   This command writes the bytes to the specified memory address for
+   debugging purposes.
+
+   Due to the low-level nature of this command, certain error conditions
+   may induce the NCP to reset.
+
+   The implementation of this command has security implications.  See
+   Section 13 for more information.
+
+   This command requires the capability "CAP_PEEK_POKE" to be present.
+
+4.13.  CMD 21: (Host->NCP) CMD_PROP_VALUE_MULTI_GET
+
+   o  Argument-Encoding: "A(i)"
+   o  Required Capability: "CAP_CMD_MULTI"
+
+   Fetch the value of multiple properties in one command.  Arguments are
+   an array of property IDs.  If all properties are fetched
+   successfully, a "CMD_PROP_VALUES_ARE" command is sent back to the
+   host containing the propertyid and value of each fetched property.
+   The order of the results in "CMD_PROP_VALUES_ARE" match the order of
+   properties given in "CMD_PROP_VALUE_GET".
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 19]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Errors fetching individual properties are reflected as indicating a
+   change to "PROP_LAST_STATUS" for that property's place.
+
+   Not all properties can be fetched using this method.  As a general
+   rule of thumb, any property that blocks when getting will fail for
+   that individual property with "STATUS_INVALID_COMMAND_FOR_PROP".
+
+4.14.  CMD 22: (Host->NCP) CMD_PROP_VALUE_MULTI_SET
+
+   o  Argument-Encoding: "A(iD)"
+   o  Required Capability: "CAP_CMD_MULTI"
+
+   +---------+--------+--------------------------+---------------------+
+   | Octets: |   1    |            1             |          n          |
+   +---------+--------+--------------------------+---------------------+
+   | Fields: | HEADER | CMD_PROP_VALUE_MULTI_SET |    Property/Value   |
+   |         |        |                          |        Pairs        |
+   +---------+--------+--------------------------+---------------------+
+
+   With each property/value pair being:
+
+                +---------+--------+---------+------------+
+                | Octets: |   2    |   1-3   |     n      |
+                +---------+--------+---------+------------+
+                | Fields: | LENGTH | PROP_ID | PROP_VALUE |
+                +---------+--------+---------+------------+
+
+   This command sets the value of several properties at once in the
+   given order.  The setting of properties stops at the first error,
+   ignoring any later properties.
+
+   The result of this command is generally "CMD_PROP_VALUES_ARE" unless
+   (for example) a parsing error has occured (in which case
+   "CMD_PROP_VALUE_IS" for "PROP_LAST_STATUS" would be the result).  The
+   order of the results in "CMD_PROP_VALUES_ARE" match the order of
+   properties given in "CMD_PROP_VALUE_MULTI_SET".
+
+   Since the processing of properties to set stops at the first error,
+   the resulting "CMD_PROP_VALUES_ARE" can contain fewer items than the
+   requested number of properties to set.
+
+   Not all properties can be set using this method.  As a general rule
+   of thumb, any property that blocks when setting will fail for that
+   individual property with "STATUS_INVALID_COMMAND_FOR_PROP".
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 20]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+4.15.  CMD 23: (NCP->Host) CMD_PROP_VALUES_ARE
+
+   o  Argument-Encoding: "A(iD)"
+   o  Required Capability: "CAP_CMD_MULTI"
+
+     +---------+--------+---------------------+----------------------+
+     | Octets: |   1    |          1          |          n           |
+     +---------+--------+---------------------+----------------------+
+     | Fields: | HEADER | CMD_PROP_VALUES_ARE | Property/Value Pairs |
+     +---------+--------+---------------------+----------------------+
+
+   With each property/value pair being:
+
+                +---------+--------+---------+------------+
+                | Octets: |   2    |   1-3   |     n      |
+                +---------+--------+---------+------------+
+                | Fields: | LENGTH | PROP_ID | PROP_VALUE |
+                +---------+--------+---------+------------+
+
+   This command is emitted by the NCP as the response to both the
+   "CMD_PROP_VALUE_MULTI_GET" and "CMD_PROP_VALUE_MULTI_SET" commands.
+   It is roughly analogous to "CMD_PROP_VALUE_IS", except that it
+   contains more than one property.
+
+   This command SHOULD NOT be emitted asynchronously, or in response to
+   any command other than "CMD_PROP_VALUE_MULTI_GET" or
+   "CMD_PROP_VALUE_MULTI_SET".
+
+   The arguments are a list of structures containing the emitted
+   property and the associated value.  These are presented in the same
+   order as given in the associated initiating command.  In cases where
+   getting or setting a specific property resulted in an error, the
+   associated slot in this command will describe "PROP_LAST_STATUS".
+
+5.  Properties
+
+   Spinel is largely a property-based protocol, similar to
+   representational state transfer (REST), with a property defined for
+   every attribute that an OS needs to create, read, update or delete in
+   the function of an IPv6 interface.  The inspiration of this approach
+   was memory-mapped hardware registers for peripherals.  The goal is to
+   avoid, as much as possible, the use of large complicated structures
+   and/or method argument lists.  The reason for avoiding these is
+   because they have a tendency to change, especially early in
+   development.  Adding or removing a property from a structure can
+   render the entire protocol incompatible.  By using properties, you
+   simply extend the protocol with an additional property.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 21]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Almost all features and capabilities are implemented using
+   properties.  Most new features that are initially proposed as
+   commands can be adapted to be property-based instead.  Notable
+   exceptions include "Host Buffer Offload" (Section 9) and "Network
+   Save" (Section 8).
+
+   In Spinel, properties are keyed by an unsigned integer between 0 and
+   2,097,151 (See Section 3.2).
+
+5.1.  Property Methods
+
+   Properties may support one or more of the following methods:
+
+   o  "VALUE_GET" (Section 4.3)
+   o  "VALUE_SET" (Section 4.4)
+   o  "VALUE_INSERT" (Section 4.5)
+   o  "VALUE_REMOVE" (Section 4.6)
+
+   Additionally, the NCP can send updates to the host (either
+   synchronously or asynchronously) that inform the host about changes
+   to specific properties:
+
+   o  "VALUE_IS" (Section 4.7)
+   o  "VALUE_INSERTED" (Section 4.8)
+   o  "VALUE_REMOVED" (Section 4.9)
+
+5.2.  Property Types
+
+   Conceptually, there are three different types of properties:
+
+   o  Single-value properties
+   o  Multiple-value (Array) properties
+   o  Stream properties
+
+5.2.1.  Single-Value Properties
+
+   Single-value properties are properties that have a simple
+   representation of a single value.  Examples would be:
+
+   o  Current radio channel (Represented as an unsigned 8-bit integer)
+   o  Network name (Represented as a UTF-8 encoded string)
+   o  802.15.4 PAN ID (Represented as an unsigned 16-bit integer)
+
+   The valid operations on these sorts of properties are "GET" and
+   "SET".
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 22]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.2.2.  Multiple-Value Properties
+
+   Multiple-Value Properties have more than one value associated with
+   them.  Examples would be:
+
+   o  List of channels supported by the radio hardware.
+   o  List of IPv6 addresses assigned to the interface.
+   o  List of capabilities supported by the NCP.
+
+   The valid operations on these sorts of properties are "VALUE_GET",
+   "VALUE_SET", "VALUE_INSERT", and "VALUE_REMOVE".
+
+   When the value is fetched using "VALUE_GET", the returned value is
+   the concatenation of all of the individual values in the list.  If
+   the length of the value for an individual item in the list is not
+   defined by the type then each item returned in the list is prepended
+   with a length (See Section 3.5).  The order of the returned items,
+   unless explicitly defined for that specific property, is undefined.
+
+   "VALUE_SET" provides a way to completely replace all previous values.
+   Calling "VALUE_SET" with an empty value effectively instructs the NCP
+   to clear the value of that property.
+
+   "VALUE_INSERT" and "VALUE_REMOVE" provide mechanisms for the
+   insertion or removal of individual items _by value_. The payload for
+   these commands is a plain single value.
+
+5.2.3.  Stream Properties
+
+   Stream properties are special properties representing streams of
+   data.  Examples would be:
+
+   o  Network packet stream (Section 5.6.3)
+   o  Raw packet stream (Section 5.6.2)
+   o  Debug message stream (Section 5.6.1)
+   o  Network Beacon stream (Section 5.8.4)
+
+   All such properties emit changes asynchronously using the "VALUE_IS"
+   command, sent from the NCP to the host.  For example, as IPv6 traffic
+   is received by the NCP, the IPv6 packets are sent to the host by way
+   of asynchronous "VALUE_IS" notifications.
+
+   Some of these properties also support the host send data back to the
+   NCP.  For example, this is how the host sends IPv6 traffic to the
+   NCP.
+
+   These types of properties generally do not support "VALUE_GET", as it
+   is meaningless.
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 23]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.3.  Property Numbering
+
+   While the majority of the properties that allow the configuration of
+   network connectivity are network protocol specific, there are several
+   properties that are required in all implementations.
+
+   Future property allocations SHALL be made from the following
+   allocation plan:
+
+    +-----------------------+-----------------------------------------+
+    | Property ID Range     | Description                             |
+    +-----------------------+-----------------------------------------+
+    | 0 - 127               | Reserved for frequently-used properties |
+    | 128 - 15,359          | Unallocated                             |
+    | 15,360 - 16,383       | Vendor-specific                         |
+    | 16,384 - 1,999,999    | Unallocated                             |
+    | 2,000,000 - 2,097,151 | Experimental use only                   |
+    +-----------------------+-----------------------------------------+
+
+   For an explanation of the data format encoding shorthand used
+   throughout this document, see Section 3.
+
+5.4.  Property Sections
+
+   The currently assigned properties are broken up into several
+   sections, each with reserved ranges of property identifiers.  These
+   ranges are:
+
+      +--------+------------------------------+---------------------+
+      |  Name  |      Range (Inclusive)       |    Documentation    |
+      +--------+------------------------------+---------------------+
+      |  Core  | 0x00 - 0x1F, 0x1000 - 0x11FF |     Section 5.5     |
+      |  PHY   | 0x20 - 0x2F, 0x1200 - 0x12FF |     Section 5.7     |
+      |  MAC   | 0x30 - 0x3F, 0x1300 - 0x13FF |     Section 5.8     |
+      |  NET   | 0x40 - 0x4F, 0x1400 - 0x14FF |     Section 5.9     |
+      |  Tech  | 0x50 - 0x5F, 0x1500 - 0x15FF | Technology-specific |
+      |  IPv6  | 0x60 - 0x6F, 0x1600 - 0x16FF |     Section 5.10    |
+      | Stream | 0x70 - 0x7F, 0x1700 - 0x17FF |     Section 5.5     |
+      | Debug  |       0x4000 - 0x4400        |     Section 5.11    |
+      +--------+------------------------------+---------------------+
+
+   Note that some of the property sections have two reserved ranges: a
+   primary range (which is encoded as a single byte) and an extended
+   range (which is encoded as two bytes).  properties which are used
+   more frequently are generally allocated from the former range.
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 24]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.5.  Core Properties
+
+5.5.1.  PROP 0: PROP_LAST_STATUS
+
+   o  Type: Read-Only
+   o  Encoding: "i"
+
+                         +---------+-------------+
+                         | Octets: |     1-3     |
+                         +---------+-------------+
+                         | Fields: | LAST_STATUS |
+                         +---------+-------------+
+
+   Describes the status of the last operation.  Encoded as a packed
+   unsigned integer.
+
+   This property is emitted often to indicate the result status of
+   pretty much any Host-to-NCP operation.
+
+   It is emitted automatically at NCP startup with a value indicating
+   the reset reason.
+
+   See Section 6 for the complete list of status codes.
+
+5.5.2.  PROP 1: PROP_PROTOCOL_VERSION
+
+   o  Type: Read-Only
+   o  Encoding: "ii"
+
+                +---------+---------------+---------------+
+                | Octets: |      1-3      |      1-3      |
+                +---------+---------------+---------------+
+                | Fields: | MAJOR_VERSION | MINOR_VERSION |
+                +---------+---------------+---------------+
+
+   Describes the protocol version information.  This property contains
+   four fields, each encoded as a packed unsigned integer:
+
+   o  Major Version Number
+   o  Minor Version Number
+
+   This document describes major version 4, minor version 3 of this
+   protocol.
+
+   The host MUST only use this property from NLI 0.  Behavior when used
+   from other NLIs is undefined.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 25]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.5.2.1.  Major Version Number
+
+   The major version number is used to identify large and incompatible
+   differences between protocol versions.
+
+   The host MUST enter a FAULT state if it does not explicitly support
+   the given major version number.
+
+5.5.2.2.  Minor Version Number
+
+   The minor version number is used to identify small but otherwise
+   compatible differences between protocol versions.  A mismatch between
+   the advertised minor version number and the minor version that is
+   supported by the host SHOULD NOT be fatal to the operation of the
+   host.
+
+5.5.3.  PROP 2: PROP_NCP_VERSION
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "U"
+
+                      +---------+-------------------+
+                      | Octets: |         n         |
+                      +---------+-------------------+
+                      | Fields: | NCP_VESION_STRING |
+                      +---------+-------------------+
+
+   Contains a string which describes the firmware currently running on
+   the NCP.  Encoded as a zero-terminated UTF-8 string.
+
+   The format of the string is not strictly defined, but it is intended
+   to present similarly to the "User-Agent" string from HTTP.  The
+   RECOMMENDED format of the string is as follows:
+
+ STACK-NAME/STACK-VERSION[BUILD_INFO][; OTHER_INFO]; BUILD_DATE_AND_TIME
+
+   Examples:
+
+   o  "OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04"
+   o  "ConnectIP/2.0b125 s1 ALPHA; Sept 24 2015 20:49:19"
+
+   The host MUST only use this property from NLI 0.  Behavior when used
+   from other NLIs is undefined.
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 26]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.5.4.  PROP 3: PROP_INTERFACE_TYPE
+
+   o  Type: Read-Only
+   o  Encoding: "i"
+
+                       +---------+----------------+
+                       | Octets: |      1-3       |
+                       +---------+----------------+
+                       | Fields: | INTERFACE_TYPE |
+                       +---------+----------------+
+
+   This integer identifies what the network protocol for this NCP.
+   Currently defined values are:
+
+   o  0: Bootloader
+   o  2: ZigBee IP(TM)
+   o  3: Thread(R)
+
+   The host MUST enter a FAULT state if it does not recognize the
+   protocol given by the NCP.
+
+5.5.5.  PROP 4: PROP_INTERFACE_VENDOR_ID
+
+   o  Type: Read-Only
+   o  Encoding: "i"
+
+                          +---------+-----------+
+                          | Octets: |    1-3    |
+                          +---------+-----------+
+                          | Fields: | VENDOR_ID |
+                          +---------+-----------+
+
+   Vendor identifier.
+
+5.5.6.  PROP 5: PROP_CAPS
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "A(i)"
+
+                     +---------+-------+-------+-----+
+                     | Octets: |  1-3  |  1-3  | ... |
+                     +---------+-------+-------+-----+
+                     | Fields: | CAP_1 | CAP_2 | ... |
+                     +---------+-------+-------+-----+
+
+   Describes the supported capabilities of this NCP.  Encoded as a list
+   of packed unsigned integers.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 27]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   A capability is defined as a 21-bit integer that describes a subset
+   of functionality which is supported by the NCP.
+
+   Currently defined values are:
+
+   o  1: "CAP_LOCK"
+   o  2: "CAP_NET_SAVE"
+   o  3: "CAP_HBO": Host Buffer Offload.  See Section 9.
+   o  4: "CAP_POWER_SAVE"
+   o  5: "CAP_COUNTERS"
+   o  6: "CAP_JAM_DETECT": Jamming detection.  See Section 10
+   o  7: "CAP_PEEK_POKE": PEEK/POKE debugging commands.
+   o  8: "CAP_WRITABLE_RAW_STREAM": "PROP_STREAM_RAW" is writable.
+   o  9: "CAP_GPIO": Support for GPIO access.  See Section 11.
+   o  10: "CAP_TRNG": Support for true random number generation.  See
+      Section 12.
+   o  11: "CAP_CMD_MULTI": Support for "CMD_PROP_VALUE_MULTI_GET"
+      (Section 4.13), "CMD_PROP_VALUE_MULTI_SET" (Section 4.14, and
+      "CMD_PROP_VALUES_ARE" (Section 4.15).
+   o  12: "CAP_UNSOL_UPDATE_FILTER": Support for
+      "PROP_UNSOL_UPDATE_FILTER" (Section 5.5.12) and
+      "PROP_UNSOL_UPDATE_LIST" (Section 5.5.13).
+   o  16: "CAP_802_15_4_2003"
+   o  17: "CAP_802_15_4_2006"
+   o  18: "CAP_802_15_4_2011"
+   o  21: "CAP_802_15_4_PIB"
+   o  24: "CAP_802_15_4_2450MHZ_OQPSK"
+   o  25: "CAP_802_15_4_915MHZ_OQPSK"
+   o  26: "CAP_802_15_4_868MHZ_OQPSK"
+   o  27: "CAP_802_15_4_915MHZ_BPSK"
+   o  28: "CAP_802_15_4_868MHZ_BPSK"
+   o  29: "CAP_802_15_4_915MHZ_ASK"
+   o  30: "CAP_802_15_4_868MHZ_ASK"
+   o  48: "CAP_ROLE_ROUTER"
+   o  49: "CAP_ROLE_SLEEPY"
+   o  52: "CAP_NET_THREAD_1_0"
+   o  512: "CAP_MAC_WHITELIST"
+   o  513: "CAP_MAC_RAW"
+   o  514: "CAP_OOB_STEERING_DATA"
+   o  1024: "CAP_THREAD_COMMISSIONER"
+   o  1025: "CAP_THREAD_TMF_PROXY"
+
+   Additionally, future capability allocations SHALL be made from the
+   following allocation plan:
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 28]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+        +-----------------------+--------------------------------+
+        |    Capability Range   |          Description           |
+        +-----------------------+--------------------------------+
+        |        0 - 127        | Reserved for core capabilities |
+        |      128 - 15,359     |         _UNALLOCATED_          |
+        |    15,360 - 16,383    |        Vendor-specific         |
+        |   16,384 - 1,999,999  |         _UNALLOCATED_          |
+        | 2,000,000 - 2,097,151 |     Experimental use only      |
+        +-----------------------+--------------------------------+
+
+5.5.7.  PROP 6: PROP_INTERFACE_COUNT
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "C"
+
+                      +---------+-------------------+
+                      | Octets: |         1         |
+                      +---------+-------------------+
+                      | Fields: | "INTERFACE_COUNT" |
+                      +---------+-------------------+
+
+   Describes the number of concurrent interfaces supported by this NCP.
+   Since the concurrent interface mechanism is still TBD, this value
+   MUST always be one.
+
+   This value is encoded as an unsigned 8-bit integer.
+
+   The host MUST only use this property from NLI 0.  Behavior when used
+   from other NLIs is undefined.
+
+5.5.8.  PROP 7: PROP_POWER_STATE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+                         +---------+-------------+
+                         | Octets: |      1      |
+                         +---------+-------------+
+                         | Fields: | POWER_STATE |
+                         +---------+-------------+
+
+   Describes the current power state of the NCP.  By writing to this
+   property you can manage the lower state of the NCP.  Enumeration is
+   encoded as a single unsigned byte.
+
+   Defined values are:
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 29]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  0: "POWER_STATE_OFFLINE": NCP is physically powered off.
+      (Enumerated for completeness sake, not expected on the wire)
+   o  1: "POWER_STATE_DEEP_SLEEP": Almost everything on the NCP is shut
+      down, but can still be resumed via a command or interrupt.
+   o  2: "POWER_STATE_STANDBY": NCP is in the lowest power state that
+      can still be awoken by an event from the radio (e.g. waiting for
+      alarm)
+   o  3: "POWER_STATE_LOW_POWER": NCP is responsive (and possibly
+      connected), but using less power. (e.g.  "Sleepy" child node)
+   o  4: "POWER_STATE_ONLINE": NCP is fully powered. (e.g.  "Parent"
+      node)
+
+   [CREF2]
+
+5.5.9.  PROP 8: PROP_HWADDR
+
+   o  Type: Read-Only*
+   o  Packed-Encoding: "E"
+
+                           +---------+--------+
+                           | Octets: |   8    |
+                           +---------+--------+
+                           | Fields: | HWADDR |
+                           +---------+--------+
+
+   The static EUI64 address of the device, used as a serial number.
+   This value is read-only, but may be writable under certain vendor-
+   defined circumstances.
+
+5.5.10.  PROP 9: PROP_LOCK
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+                            +---------+------+
+                            | Octets: |  1   |
+                            +---------+------+
+                            | Fields: | LOCK |
+                            +---------+------+
+
+   Property lock.  Used for grouping changes to several properties to
+   take effect at once, or to temporarily prevent the automatic updating
+   of property values.  When this property is set, the execution of the
+   NCP is effectively frozen until it is cleared.
+
+   This property is only supported if the "CAP_LOCK" capability is
+   present.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 30]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Unlike most other properties, setting this property to true when the
+   value of the property is already true MUST fail with a last status of
+   "STATUS_ALREADY".
+
+5.5.11.  PROP 10: PROP_HOST_POWER_STATE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+   o  Default value: 4
+
+                     +---------+--------------------+
+                     | Octets: |         1          |
+                     +---------+--------------------+
+                     | Fields: | "HOST_POWER_STATE" |
+                     +---------+--------------------+
+
+   Describes the current power state of the _host_. This property is
+   used by the host to inform the NCP when it has changed power states.
+   The NCP can then use this state to determine which properties need
+   asynchronous updates.  Enumeration is encoded as a single unsigned
+   byte.  These states are defined in similar terms to
+   "PROP_POWER_STATE" (Section 5.5.8).
+
+   Defined values are:
+
+   o  0: "HOST_POWER_STATE_OFFLINE": Host is physically powered off and
+      cannot be woken by the NCP.  All asynchronous commands are
+      squelched.
+   o  1: "HOST_POWER_STATE_DEEP_SLEEP": The host is in a low power state
+      where it can be woken by the NCP but will potentially require more
+      than two seconds to become fully responsive.  The NCP MUST avoid
+      sending unnecessary property updates, such as child table updates
+      or non-critical messages on the debug stream.  If the NCP needs to
+      wake the host for traffic, the NCP MUST first take action to wake
+      the host.  Once the NCP signals to the host that it should wake
+      up, the NCP MUST wait for some activity from the host (indicating
+      that it is fully awake) before sending frames.
+   o  2: *RESERVED*. This value MUST NOT be set by the host.  If
+      received by the NCP, the NCP SHOULD consider this as a synonym of
+      "HOST_POWER_STATE_DEEP_SLEEP".
+   o  3: "HOST_POWER_STATE_LOW_POWER": The host is in a low power state
+      where it can be immediately woken by the NCP.  The NCP SHOULD
+      avoid sending unnecessary property updates, such as child table
+      updates or non-critical messages on the debug stream.
+   o  4: "HOST_POWER_STATE_ONLINE": The host is awake and responsive.
+      No special filtering is performed by the NCP on asynchronous
+      updates.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 31]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  All other values are *RESERVED*. They MUST NOT be set by the host.
+      If received by the NCP, the NCP SHOULD consider the value as a
+      synonym of "HOST_POWER_STATE_LOW_POWER".
+
+   [CREF3]
+
+   After setting this power state, any further commands from the host to
+   the NCP will cause "HOST_POWER_STATE" to automatically revert to
+   "HOST_POWER_STATE_ONLINE".
+
+   When the host is entering a low-power state, it should wait for the
+   response from the NCP acknowledging the command (with
+   "CMD_VALUE_IS").  Once that acknowledgement is received the host may
+   enter the low-power state.
+
+   If the NCP has the "CAP_UNSOL_UPDATE_FILTER" capability, any
+   unsolicited property updates masked by "PROP_UNSOL_UPDATE_FILTER"
+   should be honored while the host indicates it is in a low-power
+   state.  After resuming to the "HOST_POWER_STATE_ONLINE" state, the
+   value of "PROP_UNSOL_UPDATE_FILTER" MUST be unchanged from the value
+   assigned prior to the host indicating it was entering a low-power
+   state.
+
+   The host MUST only use this property from NLI 0.  Behavior when used
+   from other NLIs is undefined.
+
+5.5.12.  PROP 4104: PROP_UNSOL_UPDATE_FILTER
+
+   o  Required only if "CAP_UNSOL_UPDATE_FILTER" is set.
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(I)"
+   o  Default value: Empty.
+
+   Contains a list of properties which are _excluded_ from generating
+   unsolicited value updates.  This property MUST be empty after reset.
+
+   In other words, the host may opt-out of unsolicited property updates
+   for a specific property by adding that property id to this list.
+
+   Hosts SHOULD NOT add properties to this list which are not present in
+   "PROP_UNSOL_UPDATE_LIST".  If such properties are added, the NCP MUST
+   ignore the unsupported properties.
+
+   [CREF4]
+
+   Implementations of this property are only REQUIRED to support and use
+   the following commands:
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 32]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  "CMD_PROP_VALUE_GET" (Section 4.3)
+   o  "CMD_PROP_VALUE_SET" (Section 4.4)
+   o  "CMD_PROP_VALUE_IS" (Section 4.7)
+
+   Implementations of this property MAY optionally support and use the
+   following commands:
+
+   o  "CMD_PROP_VALUE_INSERT" (Section 4.5)
+   o  "CMD_PROP_VALUE_REMOVE" (Section 4.6)
+   o  "CMD_PROP_VALUE_INSERTED" (Section 4.8)
+   o  "CMD_PROP_VALUE_REMOVED" (Section 4.9)
+
+   Host implementations which are aiming to maximize their compatability
+   across different firmwre implementations SHOULD NOT assume the
+   availability of the optional commands for this property.
+
+   The value of this property SHALL be independent for each NLI.
+
+5.5.13.  PROP 4105: PROP_UNSOL_UPDATE_LIST
+
+   o  Required only if "CAP_UNSOL_UPDATE_FILTER" is set.
+   o  Type: Read-Only
+   o  Packed-Encoding: "A(I)"
+
+   Contains a list of properties which are capable of generating
+   unsolicited value updates.  This list can be used when populating
+   "PROP_UNSOL_UPDATE_FILTER" to disable all unsolicited property
+   updates.
+
+   This property is intended to effectively behave as a constant for a
+   given NCP firmware.
+
+   Note that not all properties that support unsolicited updates need to
+   be listed here.  Scan results, for example, are only generated due to
+   direct action on the part of the host, so those properties MUST NOT
+   not be included in this list.
+
+   The value of this property MAY be different across available NLIs.
+
+5.6.  Stream Properties
+
+5.6.1.  PROP 112: PROP_STREAM_DEBUG
+
+   o  Type: Read-Only-Stream
+   o  Packed-Encoding: "D"
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 33]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+                          +---------+-----------+
+                          | Octets: |     n     |
+                          +---------+-----------+
+                          | Fields: | UTF8_DATA |
+                          +---------+-----------+
+
+   This property is a streaming property, meaning that you cannot
+   explicitly fetch the value of this property.  The stream provides
+   human-readable debugging output which may be displayed in the host
+   logs.
+
+   The location of newline characters is not assumed by the host: it is
+   the NCP's responsibility to insert newline characters where needed,
+   just like with any other text stream.
+
+   To receive the debugging stream, you wait for "CMD_PROP_VALUE_IS"
+   commands for this property from the NCP.
+
+5.6.2.  PROP 113: PROP_STREAM_RAW
+
+   o  Type: Read-Write-Stream
+   o  Packed-Encoding: "dD"
+
+        +---------+----------------+------------+----------------+
+        | Octets: |       2        |     n      |       n        |
+        +---------+----------------+------------+----------------+
+        | Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA |
+        +---------+----------------+------------+----------------+
+
+   This stream provides the capability of sending and receiving raw
+   packets to and from the radio.  The exact format of the frame
+   metadata and data is dependent on the MAC and PHY being used.
+
+   This property is a streaming property, meaning that you cannot
+   explicitly fetch the value of this property.  To receive traffic, you
+   wait for "CMD_PROP_VALUE_IS" commands with this property id from the
+   NCP.
+
+   Implementations may OPTIONALLY support the ability to transmit
+   arbitrary raw packets.  Support for this feature is indicated by the
+   presence of the "CAP_WRITABLE_RAW_STREAM" capability.
+
+   If the capability "CAP_WRITABLE_RAW_STREAM" is set, then packets
+   written to this stream with "CMD_PROP_VALUE_SET" will be sent out
+   over the radio.  This allows the caller to use the radio directly,
+   with the stack being implemented on the host instead of the NCP.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 34]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.6.2.1.  Frame Metadata Format
+
+   Any data past the end of "FRAME_DATA_LEN" is considered metadata and
+   is OPTIONAL.  Frame metadata MAY be empty or partially specified.
+   Partially specified metadata MUST be accepted.  Default values are
+   used for all unspecified fields.
+
+   The same general format is used for "PROP_STREAM_RAW",
+   "PROP_STREAM_NET", and "PROP_STREAM_NET_INSECURE".  It can be used
+   for frames sent from the NCP to the host as well as frames sent from
+   the host to the NCP.
+
+   The frame metadata field consists of the following fields:
+
+     +----------+-----------------------+------------+-----+---------+
+     | Field    | Description           | Type       | Len | Default |
+     +----------+-----------------------+------------+-----+---------+
+     | MD_POWER | (dBm) RSSI/TX-Power   | "c" int8   |  1  |   -128  |
+     | MD_NOISE | (dBm) Noise floor     | "c" int8   |  1  |   -128  |
+     | MD_FLAG  | Flags (defined below) | "S" uint16 |  2  |         |
+     | MD_PHY   | PHY-specific data     | "d" data   | >=2 |         |
+     | MD_VEND  | Vendor-specific data  | "d" data   | >=2 |         |
+     +----------+-----------------------+------------+-----+---------+
+
+   The following fields are ignored by the NCP for packets sent to it
+   from the host:
+
+   o  MD_NOISE
+   o  MD_FLAG
+
+   When specifying "MD_POWER" for a packet to be transmitted, the actual
+   transmit power is never larger than the current value of
+   "PROP_PHY_TX_POWER" (Section 5.7.6).  When left unspecified (or set
+   to the value -128), an appropriate transmit power will be chosen by
+   the NCP.
+
+   The bit values in "MD_FLAG" are defined as follows:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 35]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   +---------+--------+------------------+-----------------------------+
+   |   Bit   |  Mask  | Name             | Description if set          |
+   +---------+--------+------------------+-----------------------------+
+   |    15   | 0x0001 | MD_FLAG_TX       | Packet was transmitted, not |
+   |         |        |                  | received.                   |
+   |    13   | 0x0004 | MD_FLAG_BAD_FCS  | Packet was received with    |
+   |         |        |                  | bad FCS                     |
+   |    12   | 0x0008 | MD_FLAG_DUPE     | Packet seems to be a        |
+   |         |        |                  | duplicate                   |
+   |  0-11,  | 0xFFF2 | MD_FLAG_RESERVED | Flags reserved for future   |
+   |    14   |        |                  | use.                        |
+   +---------+--------+------------------+-----------------------------+
+
+   The format of "MD_PHY" is specified by the PHY layer currently in
+   use, and may contain information such as the channel, LQI, antenna,
+   or other pertainent information.
+
+5.6.3.  PROP 114: PROP_STREAM_NET
+
+   o  Type: Read-Write-Stream
+   o  Packed-Encoding: "dD"
+
+        +---------+----------------+------------+----------------+
+        | Octets: |       2        |     n      |       n        |
+        +---------+----------------+------------+----------------+
+        | Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA |
+        +---------+----------------+------------+----------------+
+
+   This stream provides the capability of sending and receiving data
+   packets to and from the currently attached network.  The exact format
+   of the frame metadata and data is dependent on the network protocol
+   being used.
+
+   This property is a streaming property, meaning that you cannot
+   explicitly fetch the value of this property.  To receive traffic, you
+   wait for "CMD_PROP_VALUE_IS" commands with this property id from the
+   NCP.
+
+   To send network packets, you call "CMD_PROP_VALUE_SET" on this
+   property with the value of the packet.
+
+   Any data past the end of "FRAME_DATA_LEN" is considered metadata, the
+   format of which is described in Section 5.6.2.1.
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 36]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.6.4.  PROP 115: PROP_STREAM_NET_INSECURE
+
+   o  Type: Read-Write-Stream
+   o  Packed-Encoding: "dD"
+
+        +---------+----------------+------------+----------------+
+        | Octets: |       2        |     n      |       n        |
+        +---------+----------------+------------+----------------+
+        | Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA |
+        +---------+----------------+------------+----------------+
+
+   This stream provides the capability of sending and receiving
+   unencrypted and unauthenticated data packets to and from nearby
+   devices for the purposes of device commissioning.  The exact format
+   of the frame metadata and data is dependent on the network protocol
+   being used.
+
+   This property is a streaming property, meaning that you cannot
+   explicitly fetch the value of this property.  To receive traffic, you
+   wait for "CMD_PROP_VALUE_IS" commands with this property id from the
+   NCP.
+
+   To send network packets, you call "CMD_PROP_VALUE_SET" on this
+   property with the value of the packet.
+
+   Any data past the end of "FRAME_DATA_LEN" is considered metadata, the
+   format of which is described in Section 5.6.2.1.
+
+5.7.  PHY Properties
+
+5.7.1.  PROP 32: PROP_PHY_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b" (bool8)
+
+   Set to 1 if the PHY is enabled, set to 0 otherwise.  May be directly
+   enabled to bypass higher-level packet processing in order to
+   implement things like packet sniffers.  This property can only be
+   written if the "SPINEL_CAP_MAC_RAW" capability is present.
+
+5.7.2.  PROP 33: PROP_PHY_CHAN
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C" (uint8)
+
+   Value is the current channel.  Must be set to one of the values
+   contained in "PROP_PHY_CHAN_SUPPORTED".
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 37]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.7.3.  PROP 34: PROP_PHY_CHAN_SUPPORTED
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "A(C)" (array of uint8)
+   o  Unit: List of channels
+
+   Value is a list of channel values that are supported by the hardware.
+
+5.7.4.  PROP 35: PROP_PHY_FREQ
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "L" (uint32)
+   o  Unit: Kilohertz
+
+   Value is the radio frequency (in kilohertz) of the current channel.
+
+5.7.5.  PROP 36: PROP_PHY_CCA_THRESHOLD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "c" (int8)
+   o  Unit: dBm
+
+   Value is the CCA (clear-channel assessment) threshold.  Set to -128
+   to disable.
+
+   When setting, the value will be rounded down to a value that is
+   supported by the underlying radio hardware.
+
+5.7.6.  PROP 37: PROP_PHY_TX_POWER
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "c" (int8)
+   o  Unit: dBm
+
+   Value is the transmit power of the radio.
+
+   When setting, the value will be rounded down to a value that is
+   supported by the underlying radio hardware.
+
+5.7.7.  PROP 38: PROP_PHY_RSSI
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "c" (int8)
+   o  Unit: dBm
+
+   Value is the current RSSI (Received signal strength indication) from
+   the radio.  This value can be used in energy scans and for
+   determining the ambient noise floor for the operating environment.
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 38]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.7.8.  PROP 39: PROP_PHY_RX_SENSITIVITY
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "c" (int8)
+   o  Unit: dBm
+
+   Value is the radio receive sensitivity.  This value can be used as
+   lower bound noise floor for link metrics computation.
+
+5.8.  MAC Properties
+
+5.8.1.  PROP 48: PROP_MAC_SCAN_STATE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+   o  Unit: Enumeration
+
+   Possible Values:
+
+   o  0: "SCAN_STATE_IDLE"
+   o  1: "SCAN_STATE_BEACON"
+   o  2: "SCAN_STATE_ENERGY"
+   o  3: "SCAN_STATE_DISCOVER"
+
+   Set to "SCAN_STATE_BEACON" to start an active scan.  Beacons will be
+   emitted from "PROP_MAC_SCAN_BEACON".
+
+   Set to "SCAN_STATE_ENERGY" to start an energy scan.  Channel energy
+   result will be reported by emissions of "PROP_MAC_ENERGY_SCAN_RESULT"
+   (per channel).
+
+   Set to "SCAN_STATE_DISOVER" to start a Thread MLE discovery scan
+   operation.  Discovery scan result will be emitted from
+   "PROP_MAC_SCAN_BEACON".
+
+   Value switches to "SCAN_STATE_IDLE" when scan is complete.
+
+5.8.2.  PROP 49: PROP_MAC_SCAN_MASK
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(C)"
+   o  Unit: List of channels to scan
+
+5.8.3.  PROP 50: PROP_MAC_SCAN_PERIOD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "S" (uint16)
+   o  Unit: milliseconds per channel
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 39]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.8.4.  PROP 51: PROP_MAC_SCAN_BEACON
+
+   o  Type: Read-Only-Stream
+   o  Packed-Encoding: "Ccdd" (or "Cct(ESSc)t(iCUdd)")
+
+     +---------+----+------+---------+----------+---------+----------+
+     | Octets: | 1  |  1   |    2    |    n     |    2    |    n     |
+     +---------+----+------+---------+----------+---------+----------+
+     | Fields: | CH | RSSI | MAC_LEN | MAC_DATA | NET_LEN | NET_DATA |
+     +---------+----+------+---------+----------+---------+----------+
+
+   Scan beacons have two embedded structures which contain information
+   about the MAC layer and the NET layer.  Their format depends on the
+   MAC and NET layer currently in use.  The format below is for an
+   802.15.4 MAC with Thread:
+
+   o  "C": Channel
+   o  "c": RSSI of the beacon
+   o  "t": MAC layer properties (802.15.4 layer shown below for
+      convenience)
+
+      *  "E": Long address
+      *  "S": Short address
+      *  "S": PAN-ID
+      *  "c": LQI
+   o  NET layer properties (Standard net layer shown below for
+      convenience)
+
+      *  "i": Protocol Number
+      *  "C": Flags
+      *  "U": Network Name
+      *  "d": XPANID
+      *  "d": Steering data
+
+   Extra parameters may be added to each of the structures in the
+   future, so care should be taken to read the length that prepends each
+   structure.
+
+5.8.5.  PROP 52: PROP_MAC_15_4_LADDR
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "E"
+
+   The 802.15.4 long address of this node.
+
+   This property is only present on NCPs which implement 802.15.4
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 40]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.8.6.  PROP 53: PROP_MAC_15_4_SADDR
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "S"
+
+   The 802.15.4 short address of this node.
+
+   This property is only present on NCPs which implement 802.15.4
+
+5.8.7.  PROP 54: PROP_MAC_15_4_PANID
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "S"
+
+   The 802.15.4 PANID this node is associated with.
+
+   This property is only present on NCPs which implement 802.15.4
+
+5.8.8.  PROP 55: PROP_MAC_RAW_STREAM_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Set to true to enable raw MAC frames to be emitted from
+   "PROP_STREAM_RAW".  See Section 5.6.2.
+
+5.8.9.  PROP 56: PROP_MAC_PROMISCUOUS_MODE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   Possible Values:
+
+   +----+--------------------------------+-----------------------------+
+   | Id |              Name              |         Description         |
+   +----+--------------------------------+-----------------------------+
+   | 0  |   "MAC_PROMISCUOUS_MODE_OFF"   |  Normal MAC filtering is in |
+   |    |                                |            place.           |
+   | 1  | "MAC_PROMISCUOUS_MODE_NETWORK" |   All MAC packets matching  |
+   |    |                                |  network are passed up the  |
+   |    |                                |            stack.           |
+   | 2  |  "MAC_PROMISCUOUS_MODE_FULL"   | All decoded MAC packets are |
+   |    |                                |     passed up the stack.    |
+   +----+--------------------------------+-----------------------------+
+
+   See Section 5.6.2.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 41]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.8.10.  PROP 57: PROP_MAC_ENERGY_SCAN_RESULT
+
+   o  Type: Read-Only-Stream
+   o  Packed-Encoding: "Cc"
+
+   This property is emitted during energy scan operation per scanned
+   channel with following format:
+
+   o  "C": Channel
+   o  "c": RSSI (in dBm)
+
+5.8.11.  PROP 4864: PROP_MAC_WHITELIST
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(T(Ec))"
+   o  Required capability: "CAP_MAC_WHITELIST"
+
+   Structure Parameters:
+
+   o  "E": EUI64 address of node
+   o  "c": Optional RSSI-override value.  The value 127 indicates that
+      the RSSI-override feature is not enabled for this address.  If
+      this value is omitted when setting or inserting, it is assumed to
+      be 127.  This parameter is ignored when removing.
+
+5.8.12.  PROP 4865: PROP_MAC_WHITELIST_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+   o  Required capability: "CAP_MAC_WHITELIST"
+
+5.8.13.  PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED
+
+   o  Type: Write
+   o  Packed-Encoding: "b"
+
+   Set to true to enable radio source matching or false to disable it.
+   This property is only available if the "SPINEL_CAP_MAC_RAW"
+   capability is present.  The source match functionality is used by
+   radios when generating ACKs.  The short and extended address lists
+   are used for settings the Frame Pending bit in the ACKs.
+
+5.8.14.  PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES
+
+   o  Type: Write
+   o  Packed-Encoding: "A(S)"
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 42]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Configures the list of short addresses used for source matching.
+   This property is only available if the "SPINEL_CAP_MAC_RAW"
+   capability is present.
+
+   Structure Parameters:
+
+   o  "S": Short address for hardware generated ACKs
+
+5.8.15.  PROP 4869: SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES
+
+   o  Type: Write
+   o  Packed-Encoding: "A(E)"
+
+   Configures the list of extended addresses used for source matching.
+   This property is only available if the "SPINEL_CAP_MAC_RAW"
+   capability is present.
+
+   Structure Parameters:
+
+   o  "E": EUI64 address for hardware generated ACKs
+
+5.8.16.  PROP 4870: PROP_MAC_BLACKLIST
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(T(E))"
+   o  Required capability: "CAP_MAC_WHITELIST"
+
+   Structure Parameters:
+
+   o  "E": EUI64 address of node
+
+5.8.17.  PROP 4871: PROP_MAC_BLACKLIST_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+   o  Required capability: "CAP_MAC_WHITELIST"
+
+5.9.  NET Properties
+
+5.9.1.  PROP 64: PROP_NET_SAVED
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "b"
+
+   Returns true if there is a network state stored/saved.
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 43]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.9.2.  PROP 65: PROP_NET_IF_UP
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Network interface up/down status.  Non-zero (set to 1) indicates up,
+   zero indicates down.
+
+5.9.3.  PROP 66: PROP_NET_STACK_UP
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+   o  Unit: Enumeration
+
+   Thread stack operational status.  Non-zero (set to 1) indicates up,
+   zero indicates down.
+
+5.9.4.  PROP 67: PROP_NET_ROLE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+   o  Unit: Enumeration
+
+   Values:
+
+   o  0: "NET_ROLE_DETACHED"
+   o  1: "NET_ROLE_CHILD"
+   o  2: "NET_ROLE_ROUTER"
+   o  3: "NET_ROLE_LEADER"
+
+5.9.5.  PROP 68: PROP_NET_NETWORK_NAME
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "U"
+
+5.9.6.  PROP 69: PROP_NET_XPANID
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "D"
+
+5.9.7.  PROP 70: PROP_NET_MASTER_KEY
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "D"
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 44]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+5.9.8.  PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+5.9.9.  PROP 72: PROP_NET_PARTITION_ID
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+   The partition ID of the partition that this node is a member of.
+
+5.9.10.  PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+5.9.11.  PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+5.9.12.  PROP 75: PROP_NET_PSKC
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "D"
+
+5.10.  IPv6 Properties
+
+5.10.1.  PROP 96: PROP_IPV6_LL_ADDR
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "6"
+
+   IPv6 Address
+
+5.10.2.  PROP 97: PROP_IPV6_ML_ADDR
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "6"
+
+   IPv6 Address + Prefix Length
+
+5.10.3.  PROP 98: PROP_IPV6_ML_PREFIX
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "6C"
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 45]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   IPv6 Prefix + Prefix Length
+
+5.10.4.  PROP 99: PROP_IPV6_ADDRESS_TABLE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(t(6CLLC))"
+
+   Array of structures containing:
+
+   o  "6": IPv6 Address
+   o  "C": Network Prefix Length
+   o  "L": Valid Lifetime
+   o  "L": Preferred Lifetime
+   o  "C": Flags
+
+5.10.5.  PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Allow the NCP to directly respond to ICMP ping requests.  If this is
+   turned on, ping request ICMP packets will not be passed to the host.
+
+   Default value is "false".
+
+5.11.  Debug Properties
+
+5.11.1.  PROP 16384: PROP_DEBUG_TEST_ASSERT
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "b"
+
+   Reading this property will cause an assert on the NCP.  This is
+   intended for testing the assert functionality of underlying platform/
+   NCP.  Assert should ideally cause the NCP to reset, but if "assert"
+   is not supported or disabled boolean value of "false" is returned in
+   response.
+
+5.11.2.  PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   Provides access to the NCP log level.  Currently defined values are
+   (which follows the RFC 5424):
+
+   o  0: Emergency (emerg).
+   o  1: Alert (alert).
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 46]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  2: Critical (crit).
+   o  3: Error (err).
+   o  4: Warning (warn).
+   o  5: Notice (notice).
+   o  6: Information (info).
+   o  7: Debug (debug).
+
+   If the NCP supports dynamic log level control, setting this property
+   changes the log level accordingly.  Getting the value returns the
+   current log level.  If the dynamic log level control is not
+   supported, setting this property returns a "PROP_LAST_STATUS" with
+   "STATUS_INVALID_COMMAND_FOR_PROP".
+
+6.  Status Codes
+
+   Status codes are sent from the NCP to the host via "PROP_LAST_STATUS"
+   using the "CMD_VALUE_IS" command to indicate the return status of a
+   previous command.  As with any response, the TID field of the FLAG
+   byte is used to correlate the response with the request.
+
+   Note that most successfully executed commands do not indicate a last
+   status of "STATUS_OK".  The usual way the NCP indicates a successful
+   command is to mirror the property change back to the host.  For
+   example, if you do a "CMD_VALUE_SET" on "PROP_PHY_ENABLED", the NCP
+   would indicate success by responding with a "CMD_VALUE_IS" for
+   "PROP_PHY_ENABLED".  If the command failed, "PROP_LAST_STATUS" would
+   be emitted instead.
+
+   See Section 5.5.1 for more information on "PROP_LAST_STATUS".
+
+   o  0: "STATUS_OK": Operation has completed successfully.
+   o  1: "STATUS_FAILURE": Operation has failed for some undefined
+      reason.
+   o  2: "STATUS_UNIMPLEMENTED": The given operation has not been
+      implemented.
+   o  3: "STATUS_INVALID_ARGUMENT": An argument to the given operation
+      is invalid.
+   o  4: "STATUS_INVALID_STATE" : The given operation is invalid for the
+      current state of the device.
+   o  5: "STATUS_INVALID_COMMAND": The given command is not recognized.
+   o  6: "STATUS_INVALID_INTERFACE": The given Spinel interface is not
+      supported.
+   o  7: "STATUS_INTERNAL_ERROR": An internal runtime error has
+      occurred.
+   o  8: "STATUS_SECURITY_ERROR": A security or authentication error has
+      occurred.
+   o  9: "STATUS_PARSE_ERROR": An error has occurred while parsing the
+      command.
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 47]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  10: "STATUS_IN_PROGRESS": The operation is in progress and will be
+      completed asynchronously.
+   o  11: "STATUS_NOMEM": The operation has been prevented due to memory
+      pressure.
+   o  12: "STATUS_BUSY": The device is currently performing a mutually
+      exclusive operation.
+   o  13: "STATUS_PROP_NOT_FOUND": The given property is not recognized.
+   o  14: "STATUS_PACKET_DROPPED": The packet was dropped.
+   o  15: "STATUS_EMPTY": The result of the operation is empty.
+   o  16: "STATUS_CMD_TOO_BIG": The command was too large to fit in the
+      internal buffer.
+   o  17: "STATUS_NO_ACK": The packet was not acknowledged.
+   o  18: "STATUS_CCA_FAILURE": The packet was not sent due to a CCA
+      failure.
+   o  19: "STATUS_ALREADY": The operation is already in progress or the
+      property was already set to the given value.
+   o  20: "STATUS_ITEM_NOT_FOUND": The given item could not be found in
+      the property.
+   o  21: "STATUS_INVALID_COMMAND_FOR_PROP": The given command cannot be
+      performed on this property.
+   o  22-111: RESERVED
+   o  112-127: Reset Causes
+
+      *  112: "STATUS_RESET_POWER_ON"
+      *  113: "STATUS_RESET_EXTERNAL"
+      *  114: "STATUS_RESET_SOFTWARE"
+      *  115: "STATUS_RESET_FAULT"
+      *  116: "STATUS_RESET_CRASH"
+      *  117: "STATUS_RESET_ASSERT"
+      *  118: "STATUS_RESET_OTHER"
+      *  119: "STATUS_RESET_UNKNOWN"
+      *  120: "STATUS_RESET_WATCHDOG"
+      *  121-127: RESERVED-RESET-CODES
+   o  128 - 15,359: UNALLOCATED
+   o  15,360 - 16,383: Vendor-specific
+   o  16,384 - 1,999,999: UNALLOCATED
+   o  2,000,000 - 2,097,151: Experimental Use Only (MUST NEVER be used
+      in production!)
+
+7.  Technology: Thread(R)
+
+   This section describes all of the properties and semantics required
+   for managing a Thread(R) NCP.
+
+   Thread(R) NCPs have the following requirements:
+
+   o  The property "PROP_INTERFACE_TYPE" must be 3.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 48]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  The non-optional properties in the following sections MUST be
+      implemented: CORE, PHY, MAC, NET, and IPV6.
+
+   All serious implementations of an NCP SHOULD also support the network
+   save feature (See Section 8).
+
+7.1.  Capabilities
+
+   The Thread(R) technology defines the following capabilities:
+
+   o  "CAP_NET_THREAD_1_0" - Indicates that the NCP implements v1.0 of
+      the Thread(R) standard.
+   o  "CAP_NET_THREAD_1_1" - Indicates that the NCP implements v1.1 of
+      the Thread(R) standard.
+
+7.2.  Properties
+
+   Properties for Thread(R) are allocated out of the "Tech" property
+   section (see Section 5.4).
+
+7.2.1.  PROP 80: PROP_THREAD_LEADER_ADDR
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "6"
+
+   The IPv6 address of the leader.  (Note: May change to long and short
+   address of leader)
+
+7.2.2.  PROP 81: PROP_THREAD_PARENT
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "ES"
+   o  LADDR, SADDR
+
+   The long address and short address of the parent of this node.
+
+7.2.3.  PROP 82: PROP_THREAD_CHILD_TABLE
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "A(t(ES))"
+
+   Table containing the long and short addresses of all the children of
+   this node.
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 49]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+7.2.4.  PROP 83: PROP_THREAD_LEADER_RID
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "C"
+
+   The router-id of the current leader.
+
+7.2.5.  PROP 84: PROP_THREAD_LEADER_WEIGHT
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "C"
+
+   The leader weight of the current leader.
+
+7.2.6.  PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   The leader weight for this node.
+
+7.2.7.  PROP 86: PROP_THREAD_NETWORK_DATA
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "D"
+
+   The local network data.
+
+7.2.8.  PROP 87: PROP_THREAD_NETWORK_DATA_VERSION
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "S"
+
+7.2.9.  PROP 88: PROP_THREAD_STABLE_NETWORK_DATA
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "D"
+
+   The local stable network data.
+
+7.2.10.  PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "S"
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 50]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+7.2.11.  PROP 90: PROP_THREAD_ON_MESH_NETS
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(t(6CbCb))"
+
+   Data per item is:
+
+   o  "6": IPv6 Prefix
+   o  "C": Prefix length in bits
+   o  "b": Stable flag
+   o  "C": TLV flags
+   o  "b": "Is defined locally" flag.  Set if this network was locally
+      defined.  Assumed to be true for set, insert and replace.  Clear
+      if the on mesh network was defined by another node.
+
+7.2.12.  PROP 91: PROP_THREAD_OFF_MESH_ROUTES
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(t(6CbCbb))"
+
+   Data per item is:
+
+   o  "6": Route Prefix
+   o  "C": Prefix length in bits
+   o  "b": Stable flag
+   o  "C": Route preference flags
+   o  "b": "Is defined locally" flag.  Set if this route info was
+      locally defined as part of local network data.  Assumed to be true
+      for set, insert and replace.  Clear if the route is part of
+      partition's network data.
+   o  "b": "Next hop is this device" flag.  Set if the next hop for the
+      route is this device itself (i.e., route was added by this device)
+      This value is ignored when adding an external route.  For any
+      added route the next hop is this device.
+
+7.2.13.  PROP 92: PROP_THREAD_ASSISTING_PORTS
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "A(S)"
+
+7.2.14.  PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Set to true before changing local net data.  Set to false when
+   finished.  This allows changes to be aggregated into single events.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 51]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+7.2.15.  PROP 94: PROP_THREAD_MODE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   This property contains the value of the mode TLV for this node.  The
+   meaning of the bits in this bitfield are defined by section 4.5.2 of
+   the Thread(R) specification.
+
+7.2.16.  PROP 5376: PROP_THREAD_CHILD_TIMEOUT
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+   Used when operating in the Child role.
+
+7.2.17.  PROP 5377: PROP_THREAD_RLOC16
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "S"
+
+7.2.18.  PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+7.2.19.  PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+7.2.20.  PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   Allows you to get or set the Thread(R) "NETWORK_ID_TIMEOUT" constant,
+   as defined by the Thread(R) specification.
+
+7.2.21.  PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS
+
+   o  Type: Read-Write/Write-Only
+   o  Packed-Encoding: "A(C)" (List of active thread router ids)
+
+   Note that some implementations may not support "CMD_GET_VALUE" router
+   ids, but may support "CMD_REMOVE_VALUE" when the node is a leader.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 52]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+7.2.22.  PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Allow the HOST to directly observe all IPv6 packets received by the
+   NCP, including ones sent to the RLOC16 address.
+
+   Default value is "false".
+
+7.2.23.  PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+
+   Allow the HOST to indicate whether or not the router role is enabled.
+   If current role is a router, setting this property to "false" starts
+   a re-attach process as an end-device.
+
+7.2.24.  PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+7.2.25.  PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   Specifies the self imposed random delay in seconds a REED waits
+   before registering to become an Active Router.
+
+7.2.26.  PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID
+
+   o  Type: Write-Only
+   o  Packed-Encoding: "C"
+
+   Specifies the preferred Router Id.  Upon becoming a router/leader the
+   node attempts to use this Router Id.  If the preferred Router Id is
+   not set or if it can not be used, a randomly generated router id is
+   picked.  This property can be set only when the device role is either
+   detached or disabled.
+
+7.2.27.  PROP 5387: PROP_THREAD_NEIGHBOR_TABLE
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "A(t(ESLCcCbLL))"
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 53]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Data per item is:
+
+   o  "E": Extended/long address
+   o  "S": RLOC16
+   o  "L": Age
+   o  "C": Link Quality In
+   o  "c": Average RSS
+   o  "C": Mode (bit-flags)
+   o  "b": "true" if neighbor is a child, "false" otherwise.
+   o  "L": Link Frame Counter
+   o  "L": MLE Frame Counter
+
+7.2.28.  PROP 5388: PROP_THREAD_CHILD_COUNT_MAX
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "C"
+
+   Specifies the maximum number of children currently allowed.  This
+   parameter can only be set when Thread(R) protocol operation has been
+   stopped.
+
+7.2.29.  PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "D"
+
+   The leader network data.
+
+7.2.30.  PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "D"
+
+   The stable leader network data.
+
+7.2.31.  PROP 5391: PROP_THREAD_JOINERS
+
+   o  Type: Insert/Remove Only (optionally Read-Write)
+   o  Packed-Encoding: "A(t(ULE))"
+   o  Required capability: "CAP_THREAD_COMMISSIONER"
+
+   Data per item is:
+
+   o  "U": PSKd
+   o  "L": Timeout in seconds
+   o  "E": Extended/long address (optional)
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 54]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Passess Pre-Shared Key for the Device to the NCP in the commissioning
+   process.  When the Extended address is ommited all Devices which
+   provided a valid PSKd are allowed to join the Thread(R) Network.
+
+7.2.32.  PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED
+
+   o  Type: Write only (optionally Read-Write)
+   o  Packed-Encoding: "b"
+   o  Required capability: "CAP_THREAD_COMMISSIONER"
+
+   Set to true to enable the native commissioner.  It is mandatory
+   before adding the joiner to the network.
+
+7.2.33.  PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+   o  Required capability: "CAP_THREAD_TMF_PROXY"
+
+   Set to true to enable the TMF proxy.
+
+7.2.34.  PROP 5394: PROP_THREAD_TMF_PROXY_STREAM
+
+   o  Type: Read-Write-Stream
+   o  Packed-Encoding: "dSS"
+   o  Required capability: "CAP_THREAD_TMF_PROXY"
+
+   Data per item is:
+
+   o  "d": CoAP frame
+   o  "S": source/destination RLOC/ALOC
+   o  "S": source/destination port
+
+               +----------+--------+------+---------+------+
+               | Octects: |   2    |  n   |    2    |  2   |
+               +----------+--------+------+---------+------+
+               | Fields:  | Length | CoAP | locator | port |
+               +----------+--------+------+---------+------+
+
+   This property allows the host to send and receive TMF messages from
+   the NCP's RLOC address and support Thread-specific border router
+   functions.
+
+7.2.35.  PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG
+
+   o  Type: Read-Write
+   o  Packed-Encoding:: "b"
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 55]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   This property specifies the value used in Thread(R) MLE Discovery
+   Request TLV during discovery scan operation.  Default value is
+   "false".
+
+7.2.36.  PROP 5396: PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING
+
+   o  Type: Read-Write
+   o  Packed-Encoding:: "b"
+
+   This property is used to enable/disable EUI64 filtering during
+   discovery scan operation.  Default value is "false".
+
+7.2.37.  PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID
+
+   o  Type: Read-write
+   o  Packed-Encoding:: "S"
+
+   This property specifies the PANID used for filtering during discovery
+   scan operation.  Default value is "0xffff" (broadcast PANID) which
+   disables PANID filtering.
+
+7.2.38.  PROP 5398: PROP_THREAD_STEERING_DATA
+
+   o  Type: Write-Only
+   o  Packed-Encoding: "E"
+   o  Required capability: "CAP_OOB_STEERING_DATA"
+
+   This property can be used to set the steering data for MLE Discovery
+   Response messages.
+
+   o  All zeros to clear the steering data (indicating no steering
+      data).
+   o  All 0xFFs to set the steering data (bloom filter) to accept/allow
+      all.
+   o  A specific EUI64 which is then added to steering data/bloom
+      filter.
+
+8.  Feature: Network Save
+
+   The network save/recall feature is an optional NCP capability that,
+   when present, allows the host to save and recall network credentials
+   and state to and from nonvolatile storage.
+
+   The presence of the save/recall feature can be detected by checking
+   for the presence of the "CAP_NET_SAVE" capability in "PROP_CAPS".
+
+   Network clear feature allows host to erase all network credentials
+   and state from non-volatile memory.
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 56]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+8.1.  Commands
+
+8.1.1.  CMD 9: (Host->NCP) CMD_NET_SAVE
+
+                    +---------+--------+--------------+
+                    | Octets: |   1    |      1       |
+                    +---------+--------+--------------+
+                    | Fields: | HEADER | CMD_NET_SAVE |
+                    +---------+--------+--------------+
+
+   Save network state command.  Saves any current network credentials
+   and state necessary to reconnect to the current network to non-
+   volatile memory.
+
+   This operation affects non-volatile memory only.  The current network
+   information stored in volatile memory is unaffected.
+
+   The response to this command is always a "CMD_PROP_VALUE_IS" for
+   "PROP_LAST_STATUS", indicating the result of the operation.
+
+   This command is only available if the "CAP_NET_SAVE" capability is
+   set.
+
+8.1.2.  CMD 10: (Host->NCP) CMD_NET_CLEAR
+
+                   +---------+--------+---------------+
+                   | Octets: |   1    |       1       |
+                   +---------+--------+---------------+
+                   | Fields: | HEADER | CMD_NET_CLEAR |
+                   +---------+--------+---------------+
+
+   Clear saved network settings command.  Erases all network credentials
+   and state from non-volatile memory.  The erased settings include any
+   data saved automatically by the network stack firmware and/or data
+   saved by "CMD_NET_SAVE" operation.
+
+   This operation affects non-volatile memory only.  The current network
+   information stored in volatile memory is unaffected.
+
+   The response to this command is always a "CMD_PROP_VALUE_IS" for
+   "PROP_LAST_STATUS", indicating the result of the operation.
+
+   This command is always available independent of the value of
+   "CAP_NET_SAVE" capability.
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 57]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+8.1.3.  CMD 11: (Host->NCP) CMD_NET_RECALL
+
+                   +---------+--------+----------------+
+                   | Octets: |   1    |       1        |
+                   +---------+--------+----------------+
+                   | Fields: | HEADER | CMD_NET_RECALL |
+                   +---------+--------+----------------+
+
+   Recall saved network state command.  Recalls any previously saved
+   network credentials and state previously stored by "CMD_NET_SAVE"
+   from non-volatile memory.
+
+   This command will typically generated several unsolicited property
+   updates as the network state is loaded.  At the conclusion of
+   loading, the authoritative response to this command is always a
+   "CMD_PROP_VALUE_IS" for "PROP_LAST_STATUS", indicating the result of
+   the operation.
+
+   This command is only available if the "CAP_NET_SAVE" capability is
+   set.
+
+9.  Feature: Host Buffer Offload
+
+   The memory on an NCP may be much more limited than the memory on the
+   host processor.  In such situations, it is sometimes useful for the
+   NCP to offload buffers to the host processor temporarily so that it
+   can perform other operations.
+
+   Host buffer offload is an optional NCP capability that, when present,
+   allows the NCP to store data buffers on the host processor that can
+   be recalled at a later time.
+
+   The presence of this feature can be detected by the host by checking
+   for the presence of the "CAP_HBO" capability in "PROP_CAPS".
+
+9.1.  Commands
+
+9.1.1.  CMD 12: (NCP->Host) CMD_HBO_OFFLOAD
+
+   o  Argument-Encoding: "LscD"
+
+      *  "OffloadId": 32-bit unique block identifier
+      *  "Expiration": In seconds-from-now
+      *  "Priority": Critical, High, Medium, Low
+      *  "Data": Data to offload
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 58]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+9.1.2.  CMD 13: (NCP->Host) CMD_HBO_RECLAIM
+
+   o  Argument-Encoding: "Lb"
+
+      *  "OffloadId": 32-bit unique block identifier
+      *  "KeepAfterReclaim": If not set to true, the block will be
+         dropped by the host after it is sent to the NCP.
+
+9.1.3.  CMD 14: (NCP->Host) CMD_HBO_DROP
+
+   o  Argument-Encoding: "L"
+
+      *  "OffloadId": 32-bit unique block identifier
+
+9.1.4.  CMD 15: (Host->NCP) CMD_HBO_OFFLOADED
+
+   o  Argument-Encoding: "Li"
+
+      *  "OffloadId": 32-bit unique block identifier
+      *  "Status": Status code for the result of the operation.
+
+9.1.5.  CMD 16: (Host->NCP) CMD_HBO_RECLAIMED
+
+   o  Argument-Encoding: "LiD"
+
+      *  "OffloadId": 32-bit unique block identifier
+      *  "Status": Status code for the result of the operation.
+      *  "Data": Data that was previously offloaded (if any)
+
+9.1.6.  CMD 17: (Host->NCP) CMD_HBO_DROPPED
+
+   o  Argument-Encoding: "Li"
+
+      *  "OffloadId": 32-bit unique block identifier
+      *  "Status": Status code for the result of the operation.
+
+9.2.  Properties
+
+9.2.1.  PROP 10: PROP_HBO_MEM_MAX
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "L"
+
+                     +---------+--------------------+
+                     | Octets: |         4          |
+                     +---------+--------------------+
+                     | Fields: | "PROP_HBO_MEM_MAX" |
+                     +---------+--------------------+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 59]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Describes the number of bytes that may be offloaded from the NCP to
+   the host.  Default value is zero, so this property must be set by the
+   host to a non-zero value before the NCP will begin offloading blocks.
+
+   This value is encoded as an unsigned 32-bit integer.
+
+   This property is only available if the "CAP_HBO" capability is
+   present in "PROP_CAPS".
+
+9.2.2.  PROP 11: PROP_HBO_BLOCK_MAX
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "S"
+
+                    +---------+----------------------+
+                    | Octets: |          2           |
+                    +---------+----------------------+
+                    | Fields: | "PROP_HBO_BLOCK_MAX" |
+                    +---------+----------------------+
+
+   Describes the number of blocks that may be offloaded from the NCP to
+   the host.  Default value is 32.  Setting this value to zero will
+   cause host block offload to be effectively disabled.
+
+   This value is encoded as an unsigned 16-bit integer.
+
+   This property is only available if the "CAP_HBO" capability is
+   present in "PROP_CAPS".
+
+10.  Feature: Jam Detection
+
+   Jamming detection is a feature that allows the NCP to report when it
+   detects high levels of interference that are characteristic of
+   intentional signal jamming.
+
+   The presence of this feature can be detected by checking for the
+   presence of the "CAP_JAM_DETECT" (value 6) capability in "PROP_CAPS".
+
+10.1.  Properties
+
+10.1.1.  PROP 4608: PROP_JAM_DETECT_ENABLE
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "b"
+   o  Default Value: false
+   o  REQUIRED for "CAP_JAM_DETECT"
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 60]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+                  +---------+--------------------------+
+                  | Octets: |            1             |
+                  +---------+--------------------------+
+                  | Fields: | "PROP_JAM_DETECT_ENABLE" |
+                  +---------+--------------------------+
+
+   Indicates if jamming detection is enabled or disabled.  Set to true
+   to enable jamming detection.
+
+   This property is only available if the "CAP_JAM_DETECT" capability is
+   present in "PROP_CAPS".
+
+10.1.2.  PROP 4609: PROP_JAM_DETECTED
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "b"
+   o  REQUIRED for "CAP_JAM_DETECT"
+
+                     +---------+---------------------+
+                     | Octets: |          1          |
+                     +---------+---------------------+
+                     | Fields: | "PROP_JAM_DETECTED" |
+                     +---------+---------------------+
+
+   Set to true if radio jamming is detected.  Set to false otherwise.
+
+   When jamming detection is enabled, changes to the value of this
+   property are emitted asynchronously via "CMD_PROP_VALUE_IS".
+
+   This property is only available if the "CAP_JAM_DETECT" capability is
+   present in "PROP_CAPS".
+
+10.1.3.  PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "c"
+   o  Units: dBm
+   o  Default Value: Implementation-specific
+   o  RECOMMENDED for "CAP_JAM_DETECT"
+
+   This parameter describes the threshold RSSI level (measured in dBm)
+   above which the jamming detection will consider the channel blocked.
+
+10.1.4.  PROP 4611: PROP_JAM_DETECT_WINDOW
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "c"
+   o  Units: Seconds (1-64)
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 61]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  Default Value: Implementation-specific
+   o  RECOMMENDED for "CAP_JAM_DETECT"
+
+   This parameter describes the window period for signal jamming
+   detection.
+
+10.1.5.  PROP 4612: PROP_JAM_DETECT_BUSY
+
+   o  Type: Read-Write
+   o  Packed-Encoding: "i"
+   o  Units: Seconds (1-64)
+   o  Default Value: Implementation-specific
+   o  RECOMMENDED for "CAP_JAM_DETECT"
+
+   This parameter describes the number of aggregate seconds within the
+   detection window where the RSSI must be above
+   "PROP_JAM_DETECT_RSSI_THRESHOLD" to trigger detection.
+
+   The behavior of the jamming detection feature when
+   "PROP_JAM_DETECT_BUSY" is larger than "PROP_JAM_DETECT_WINDOW" is
+   undefined.
+
+10.1.6.  PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP
+
+   o  Type: Read-Only
+   o  Packed-Encoding: "LL"
+   o  Default Value: Implementation-specific
+   o  RECOMMENDED for "CAP_JAM_DETECT"
+
+   This value provides information about current state of jamming
+   detection module for monitoring/debugging purpose.  It returns a
+   64-bit value where each bit corresponds to one second interval
+   starting with bit 0 for the most recent interval and bit 63 for the
+   oldest intervals (63 sec earlier).  The bit is set to 1 if the
+   jamming detection module observed/detected high signal level during
+   the corresponding one second interval.  The value is read-only and is
+   encoded as two "L" (uint32) values in little-endian format (first "L"
+   (uint32) value gives the lower bits corresponding to more recent
+   history).
+
+11.  Feature: GPIO Access
+
+   This feature allows the host to have control over some or all of the
+   GPIO pins on the NCP.  The host can determine which GPIOs are
+   available by examining "PROP_GPIO_CONFIG", described below.  This API
+   supports a maximum of 256 individual GPIO pins.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 62]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Support for this feature can be determined by the presence of
+   "CAP_GPIO".
+
+11.1.  Properties
+
+11.1.1.  PROP 4096: PROP_GPIO_CONFIG
+
+   o  Argument-Encoding: "A(t(CCU))"
+   o  Type: Read-write (Writable only using "CMD_PROP_VALUE_INSERT",
+      Section 4.5)
+
+   An array of structures which contain the following fields:
+
+   o  "C": GPIO Number
+   o  "C": GPIO Configuration Flags
+   o  "U": Human-readable GPIO name
+
+   GPIOs which do not have a corresponding entry are not supported.
+
+   The configuration parameter contains the configuration flags for the
+   GPIO:
+
+                       0   1   2   3   4   5   6   7
+                     +---+---+---+---+---+---+---+---+
+                     |DIR|PUP|PDN|TRIGGER|  RESERVED |
+                     +---+---+---+---+---+---+---+---+
+                             |O/D|
+                             +---+
+
+   o  "DIR": Pin direction.  Clear (0) for input, set (1) for output.
+   o  "PUP": Pull-up enabled flag.
+   o  "PDN"/"O/D": Flag meaning depends on pin direction:
+
+      *  Input: Pull-down enabled.
+      *  Output: Output is an open-drain.
+   o  "TRIGGER": Enumeration describing how pin changes generate
+      asynchronous notification commands (TBD) from the NCP to the host.
+
+      *  0: Feature disabled for this pin
+      *  1: Trigger on falling edge
+      *  2: Trigger on rising edge
+      *  3: Trigger on level change
+   o  "RESERVED": Bits reserved for future use.  Always cleared to zero
+      and ignored when read.
+
+   As an optional feature, the configuration of individual pins may be
+   modified using the "CMD_PROP_VALUE_INSERT" command.  Only the GPIO
+   number and flags fields MUST be present, the GPIO name (if present)
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 63]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   would be ignored.  This command can only be used to modify the
+   configuration of GPIOs which are already exposed---it cannot be used
+   by the host to add addional GPIOs.
+
+11.1.2.  PROP 4098: PROP_GPIO_STATE
+
+   o  Type: Read-Write
+
+   Contains a bit field identifying the state of the GPIOs.  The length
+   of the data associated with these properties depends on the number of
+   GPIOs.  If you have 10 GPIOs, you'd have two bytes.  GPIOs are
+   numbered from most significant bit to least significant bit, so 0x80
+   is GPIO 0, 0x40 is GPIO 1, etc.
+
+   For GPIOs configured as inputs:
+
+   o  "CMD_PROP_VAUE_GET": The value of the associated bit describes the
+      logic level read from the pin.
+   o  "CMD_PROP_VALUE_SET": The value of the associated bit is ignored
+      for these pins.
+
+   For GPIOs configured as outputs:
+
+   o  "CMD_PROP_VAUE_GET": The value of the associated bit is
+      implementation specific.
+   o  "CMD_PROP_VALUE_SET": The value of the associated bit determines
+      the new logic level of the output.  If this pin is configured as
+      an open-drain, setting the associated bit to 1 will cause the pin
+      to enter a Hi-Z state.
+
+   For GPIOs which are not specified in "PROP_GPIO_CONFIG":
+
+   o  "CMD_PROP_VAUE_GET": The value of the associated bit is
+      implementation specific.
+   o  "CMD_PROP_VALUE_SET": The value of the associated bit MUST be
+      ignored by the NCP.
+
+   When writing, unspecified bits are assumed to be zero.
+
+11.1.3.  PROP 4099: PROP_GPIO_STATE_SET
+
+   o  Type: Write-only
+
+   Allows for the state of various output GPIOs to be set without
+   affecting other GPIO states.  Contains a bit field identifying the
+   output GPIOs that should have their state set to 1.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 64]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   When writing, unspecified bits are assumed to be zero.  The value of
+   any bits for GPIOs which are not specified in "PROP_GPIO_CONFIG" MUST
+   be ignored.
+
+11.1.4.  PROP 4100: PROP_GPIO_STATE_CLEAR
+
+   o  Type: Write-only
+
+   Allows for the state of various output GPIOs to be cleared without
+   affecting other GPIO states.  Contains a bit field identifying the
+   output GPIOs that should have their state cleared to 0.
+
+   When writing, unspecified bits are assumed to be zero.  The value of
+   any bits for GPIOs which are not specified in "PROP_GPIO_CONFIG" MUST
+   be ignored.
+
+12.  Feature: True Random Number Generation
+
+   This feature allows the host to have access to any strong hardware
+   random number generator that might be present on the NCP, for things
+   like key generation or seeding PRNGs.
+
+   Support for this feature can be determined by the presence of
+   "CAP_TRNG".
+
+   Note well that implementing a cryptographically-strong software-based
+   true random number generator (that is impervious to things like
+   temperature changes, manufacturing differences across devices, or
+   unexpected output correlations) is non-trivial without a well-
+   designed, dedicated hardware random number generator.  Implementors
+   who have little or no experience in this area are encouraged to not
+   advertise this capability.
+
+12.1.  Properties
+
+12.1.1.  PROP 4101: PROP_TRNG_32
+
+   o  Argument-Encoding: "L"
+   o  Type: Read-Only
+
+   Fetching this property returns a strong random 32-bit integer that is
+   suitable for use as a PRNG seed or for cryptographic use.
+
+   While the exact mechanism behind the calculation of this value is
+   implementation-specific, the implementation must satisfy the
+   following requirements:
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 65]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  Data representing at least 32 bits of fresh entropy (extracted
+      from the primary entropy source) MUST be consumed by the
+      calculation of each query.
+   o  Each of the 32 bits returned MUST be free of bias and have no
+      statistical correlation to any part of the raw data used for the
+      calculation of any query.
+
+   Support for this property is REQUIRED if "CAP_TRNG" is included in
+   the device capabilities.
+
+12.1.2.  PROP 4102: PROP_TRNG_128
+
+   o  Argument-Encoding: "D"
+   o  Type: Read-Only
+
+   Fetching this property returns 16 bytes of strong random data
+   suitable for direct cryptographic use without further processing(For
+   example, as an AES key).
+
+   While the exact mechanism behind the calculation of this value is
+   implementation-specific, the implementation must satisfy the
+   following requirements:
+
+   o  Data representing at least 128 bits of fresh entropy (extracted
+      from the primary entropy source) MUST be consumed by the
+      calculation of each query.
+   o  Each of the 128 bits returned MUST be free of bias and have no
+      statistical correlation to any part of the raw data used for the
+      calculation of any query.
+
+   Support for this property is REQUIRED if "CAP_TRNG" is included in
+   the device capabilities.
+
+12.1.3.  PROP 4103: PROP_TRNG_RAW_32
+
+   o  Argument-Encoding: "D"
+   o  Type: Read-Only
+
+   This property is primarily used to diagnose and debug the behavior of
+   the entropy source used for strong random number generation.
+
+   When queried, returns the raw output from the entropy source used to
+   generate "PROP_TRNG_32", prior to any reduction/whitening and/or
+   mixing with prior state.
+
+   The length of the returned buffer is implementation specific and
+   should be expected to be non-deterministic.
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 66]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   Support for this property is RECOMMENDED if "CAP_TRNG" is included in
+   the device capabilities.
+
+13.  Security Considerations
+
+13.1.  Raw Application Access
+
+   Spinel MAY be used as an API boundary for allowing processes to
+   configure the NCP.  However, such a system MUST NOT give unprivileged
+   processess the ability to send or receive arbitrary command frames to
+   the NCP.  Only the specific commands and properties that are required
+   should be allowed to be passed, and then only after being checked for
+   proper format.
+
+14.  References
+
+14.1.  URIs
+
+   [1] https://www.w3.org/TR/exi/#encodingUnsignedInteger
+
+   [2] http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
+
+   [3] https://github.com/miekg/mmark
+
+   [4] http://xml2rfc.ietf.org/
+
+Appendix A.  Framing Protocol
+
+   Since this NCP protocol is defined independently of the physical
+   transport or framing, any number of transports and framing protocols
+   could be used successfully.  However, in the interests of
+   compatibility, this document provides some recommendations.
+
+A.1.  UART Recommendations
+
+   The recommended default UART settings are:
+
+   o  Bit rate: 115200
+   o  Start bits: 1
+   o  Data bits: 8
+   o  Stop bits: 1
+   o  Parity: None
+   o  Flow Control: Hardware
+
+   These values may be adjusted depending on the individual needs of the
+   application or product, but some sort of flow control MUST be used.
+   Hardware flow control is preferred over software flow control.  In
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 67]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   the absence of hardware flow control, software flow control (XON/
+   XOFF) MUST be used instead.
+
+   We also *RECOMMEND* an Arduino-style hardware reset, where the DTR
+   signal is coupled to the "R&#773;E&#773;S&#773;" pin through a
+   0.01[micro]F capacitor.  This causes the NCP to automatically reset
+   whenever the serial port is opened.  At the very least we *RECOMMEND*
+   dedicating one of your host pins to controlling the
+   "R&#773;E&#773;S&#773;" pin on the NCP, so that you can easily
+   perform a hardware reset if necessary.
+
+A.1.1.  UART Bit Rate Detection
+
+   When using a UART, the issue of an appropriate bit rate must be
+   considered.  A bitrate of 115200 bits per second has become a defacto
+   standard baud rate for many serial peripherals.  This rate, however,
+   is slower than the theoretical maximum bitrate of the 802.15.4 2.4GHz
+   PHY (250kbit).  In most circumstances this mismatch is not
+   significant because the overall bitrate will be much lower than
+   either of these rates, but there are circumstances where a faster
+   UART bitrate is desirable.  Thus, this document proposes a simple
+   bitrate detection scheme that can be employed by the host to detect
+   when the attached NCP is initially running at a higher bitrate.
+
+   The algorithm is to send successive NOOP commands to the NCP at
+   increasing bitrates.  When a valid "CMD_LAST_STATUS" response has
+   been received, we have identified the correct bitrate.
+
+   In order to limit the time spent hunting for the appropriate bitrate,
+   we RECOMMEND that only the following bitrates be checked:
+
+   o  115200
+   o  230400
+   o  1000000 (1Mbit)
+
+   The bitrate MAY also be changed programmatically by adjusting
+   "PROP_UART_BITRATE", if implemented.
+
+A.1.2.  HDLC-Lite
+
+   _HDLC-Lite_ is the recommended framing protocol for transmitting
+   Spinel frames over a UART.  HDLC-Lite consists of only the framing,
+   escaping, and CRC parts of the larger HDLC protocol---all other parts
+   of HDLC are omitted.  This protocol was chosen because it works well
+   with software flow control and is widely implemented.
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 68]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   To transmit a frame with HDLC-lite, the 16-bit CRC must first be
+   appended to the frame.  The CRC function is defined to be CRC-16/
+   CCITT, otherwise known as the KERMIT CRC [2].
+
+   Individual frames are terminated with a frame delimiter octet called
+   the 'flag' octet ("0x7E").
+
+   The following octets values are considered _special_ and should be
+   escaped when present in data frames:
+
+                 +-------------+------------------------+
+                 | Octet Value |      Description       |
+                 +-------------+------------------------+
+                 |     0x7E    | Frame Delimiter (Flag) |
+                 |     0x7D    |      Escape Byte       |
+                 |     0x11    |          XON           |
+                 |     0x13    |          XOFF          |
+                 |     0xF8    |    Vendor-Specific     |
+                 +-------------+------------------------+
+
+   When present in a data frame, these octet values are escaped by
+   prepending the escape octet ("0x7D") and XORing the value with
+   "0x20".
+
+   When receiving a frame, the CRC must be verified after the frame is
+   unescaped.  If the CRC value does not match what is calculated for
+   the frame data, the frame MUST be discarded.  The implementation MAY
+   indicate the failure to higher levels to handle as they see fit, but
+   MUST NOT attempt to process the deceived frame.
+
+   Consecutive flag octets are entirely legal and MUST NOT be treated as
+   a framing error.  Consecutive flag octets MAY be used as a way to
+   wake up a sleeping NCP.
+
+   When first establishing a connection to the NCP, it is customary to
+   send one or more flag octets to ensure that any previously received
+   data is discarded.
+
+A.2.  SPI Recommendations
+
+   We RECOMMEND the use of the following standard SPI signals:
+
+   o  "C&#773;S&#773;": (Host-to-NCP) Chip Select
+   o  "CLK": (Host-to-NCP) Clock
+   o  "MOSI": Master-Output/Slave-Input
+   o  "MISO": Master-Input/Slave-Output
+   o  "I&#773;N&#773;T&#773;": (NCP-to-Host) Host Interrupt
+   o  "R&#773;E&#773;S&#773;": (Host-to-NCP) NCP Hardware Reset
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 69]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   The "I&#773;N&#773;T&#773;" signal is used by the NCP to indicate to
+   the host that the NCP has frames pending to send to it.  When
+   asserted, the host SHOULD initiate a SPI transaction in a timely
+   manner.
+
+   We RECOMMEND the following SPI properties:
+
+   o  "C&#773;S&#773;" is active low.
+   o  "CLK" is active high.
+   o  "CLK" speed is larger than 500 kHz.
+   o  Data is valid on leading edge of "CLK".
+   o  Data is sent in multiples of 8-bits (octets).
+   o  Octets are sent most-significant bit first.
+
+   This recommended configuration may be adjusted depending on the
+   individual needs of the application or product.
+
+A.2.1.  SPI Framing Protocol
+
+   Each SPI frame starts with a 5-byte frame header:
+
+                  +---------+-----+----------+----------+
+                  | Octets: |  1  |    2     |    2     |
+                  +---------+-----+----------+----------+
+                  | Fields: | HDR | RECV_LEN | DATA_LEN |
+                  +---------+-----+----------+----------+
+
+   o  "HDR": The first byte is the header byte (defined below)
+   o  "RECV_LEN": The second and third bytes indicate the largest frame
+      size that that device is ready to receive.  If zero, then the
+      other device must not send any data.  (Little endian)
+   o  "DATA_LEN": The fourth and fifth bytes indicate the size of the
+      pending data frame to be sent to the other device.  If this value
+      is equal-to or less-than the number of bytes that the other device
+      is willing to receive, then the data of the frame is immediately
+      after the header.  (Little Endian)
+
+   The "HDR" byte is defined as:
+
+                       0   1   2   3   4   5   6   7
+                     +---+---+---+---+---+---+---+---+
+                     |RST|CRC|CCF|  RESERVED |PATTERN|
+                     +---+---+---+---+---+---+---+---+
+
+   o  "RST": This bit is set when that device has been reset since the
+      last time "C&#773;S&#773;" was asserted.
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 70]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  "CRC": This bit is set when that device supports writing a 16-bit
+      CRC at the end of the data.  The CRC length is NOT included in
+      DATA_LEN.
+   o  "CCF": "CRC Check Failure".  Set if the CRC check on the last
+      received frame failed, cleared to zero otherwise.  This bit is
+      only used if both sides support CRC.
+   o  "RESERVED": These bits are all reserved for future used.  They
+      MUST be cleared to zero and MUST be ignored if set.
+   o  "PATTERN": These bits are set to a fixed value to help distinguish
+      valid SPI frames from garbage (by explicitly making "0xFF" and
+      "0x00" invalid values).  Bit 6 MUST be set to be one and bit 7
+      MUST be cleared (0).  A frame received that has any other values
+      for these bits MUST be dropped.
+
+   Prior to a sending or receiving a frame, the master MAY send a
+   5-octet frame with zeros for both the max receive frame size and the
+   the contained frame length.  This will induce the slave device to
+   indicate the length of the frame it wants to send (if any) and
+   indicate the largest frame it is capable of receiving at the moment.
+   This allows the master to calculate the size of the next transaction.
+   Alternatively, if the master has a frame to send it can just go ahead
+   and send a frame of that length and determine if the frame was
+   accepted by checking that the "RECV_LEN" from the slave frame is
+   larger than the frame the master just tried to send.  If the
+   "RECV_LEN" is smaller then the frame wasn't accepted and will need to
+   be transmitted again.
+
+   This protocol can be used either unidirectionally or bidirectionally,
+   determined by the behavior of the master and the slave.
+
+   If the the master notices "PATTERN" is not set correctly, the master
+   should consider the transaction to have failed and try again after 10
+   milliseconds, retrying up to 200 times.  After unsuccessfully trying
+   200 times in a row, the master MAY take appropriate remedial action
+   (like a NCP hardware reset, or indicating a communication failure to
+   a user interface).
+
+   At the end of the data of a frame is an optional 16-bit CRC, support
+   for which is indicated by the "CRC" bit of the "HDR" byte being set.
+   If these bits are set for both the master and slave frames, then CRC
+   checking is enabled on both sides, effectively requiring that frame
+   sizes be two bytes longer than would be otherwise required.  The CRC
+   is calculated using the same mechanism used for the CRC calculation
+   in HDLC-Lite (See Appendix A.1.2).  When both of the "CRC" bits are
+   set, both sides must verify that the "CRC" is valid before accepting
+   the frame.  If not enough bytes were clocked out for the CRC to be
+   read, then the frame must be ignored.  If enough bytes were clocked
+   out to perform a CRC check, but the CRC check fails, then the frame
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 71]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   must be rejected and the "CRC_FAIL" bit on the next frame (and ONLY
+   the next frame) MUST be set.
+
+A.3.  I^2C Recommendations
+
+   TBD
+
+   [CREF5]
+
+A.4.  Native USB Recommendations
+
+   TBD
+
+   [CREF6]
+
+Appendix B.  Test Vectors
+
+B.1.  Test Vector: Packed Unsigned Integer
+
+                 +---------------+-----------------------+
+                 | Decimal Value | Packet Octet Encoding |
+                 +---------------+-----------------------+
+                 |             0 | "00"                  |
+                 |             1 | "01"                  |
+                 |           127 | "7F"                  |
+                 |           128 | "80 01"               |
+                 |           129 | "81 01"               |
+                 |         1,337 | "B9 0A"               |
+                 |        16,383 | "FF 7F"               |
+                 |        16,384 | "80 80 01"            |
+                 |        16,385 | "81 80 01"            |
+                 |     2,097,151 | "FF FF 7F"            |
+                 +---------------+-----------------------+
+
+   [CREF7]
+
+B.2.  Test Vector: Reset Command
+
+   o  NLI: 0
+   o  TID: 0
+   o  CMD: 1 ("CMD_RESET")
+
+   Frame:
+
+                                   80 01
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 72]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+B.3.  Test Vector: Reset Notification
+
+   o  NLI: 0
+   o  TID: 0
+   o  CMD: 6 ("CMD_VALUE_IS")
+   o  PROP: 0 ("PROP_LAST_STATUS")
+   o  VALUE: 114 ("STATUS_RESET_SOFTWARE")
+
+   Frame:
+
+                                80 06 00 72
+
+B.4.  Test Vector: Scan Beacon
+
+   o  NLI: 0
+   o  TID: 0
+   o  CMD: 7 ("CMD_VALUE_INSERTED")
+   o  PROP: 51 ("PROP_MAC_SCAN_BEACON")
+   o  VALUE: Structure, encoded as "Cct(ESSc)t(iCUd)"
+
+      *  CHAN: 15
+      *  RSSI: -60dBm
+      *  MAC_DATA: (0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00)
+
+         +  Long address: B6:40:D4:8C:E9:38:F9:52
+         +  Short address: 0xFFFF
+         +  PAN-ID: 0x04D2
+         +  LQI: 0
+      *  NET_DATA: (13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE
+         EF 00 CA FE)
+
+         +  Protocol Number: 3
+         +  Flags: 0x20
+         +  Network Name: "spinel"
+         +  XPANID: "DE AD 00 BE EF 00 CA FE"
+
+   Frame:
+
+        80 07 33 0F C4 0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00
+        13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE EF 00 CA
+        FE
+
+B.5.  Test Vector: Inbound IPv6 Packet
+
+   CMD_VALUE_IS(PROP_STREAM_NET)
+
+   [CREF8]
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 73]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+B.6.  Test Vector: Outbound IPv6 Packet
+
+   CMD_VALUE_SET(PROP_STREAM_NET)
+
+   [CREF9]
+
+B.7.  Test Vector: Fetch list of on-mesh networks
+
+   o  NLI: 0
+   o  TID: 4
+   o  CMD: 2 ("CMD_VALUE_GET")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+
+   Frame:
+
+                                 84 02 5A
+
+B.8.  Test Vector: Returned list of on-mesh networks
+
+   o  NLI: 0
+   o  TID: 4
+   o  CMD: 6 ("CMD_VALUE_IS")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+   o  VALUE: Array of structures, encoded as "A(t(6CbC))"
+
+       +--------------+---------------+-------------+-------------+
+       | IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags |
+       +--------------+---------------+-------------+-------------+
+       | 2001:DB8:1:: |       64      |     True    |      ??     |
+       | 2001:DB8:2:: |       64      |    False    |      ??     |
+       +--------------+---------------+-------------+-------------+
+
+   Frame:
+
+        84 06 5A 13 00 20 01 0D B8 00 01 00 00 00 00 00 00 00 00 00
+        00 40 01 ?? 13 00 20 01 0D B8 00 02 00 00 00 00 00 00 00 00
+        00 00 40 00 ??
+
+B.9.  Test Vector: Adding an on-mesh network
+
+   o  NLI: 0
+   o  TID: 5
+   o  CMD: 4 ("CMD_VALUE_INSERT")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+   o  VALUE: Structure, encoded as "6CbCb"
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 74]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+       +--------------+---------------+-------------+-------------+
+       | IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags |
+       +--------------+---------------+-------------+-------------+
+       | 2001:DB8:3:: |       64      |     True    |      ??     |
+       +--------------+---------------+-------------+-------------+
+
+   Frame:
+
+        85 03 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+        01 ?? 01
+
+   [CREF10]
+
+B.10.  Test Vector: Insertion notification of an on-mesh network
+
+   o  NLI: 0
+   o  TID: 5
+   o  CMD: 7 ("CMD_VALUE_INSERTED")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+   o  VALUE: Structure, encoded as "6CbCb"
+
+       +--------------+---------------+-------------+-------------+
+       | IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags |
+       +--------------+---------------+-------------+-------------+
+       | 2001:DB8:3:: |       64      |     True    |      ??     |
+       +--------------+---------------+-------------+-------------+
+
+   Frame:
+
+        85 07 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+        01 ?? 01
+
+   [CREF11]
+
+B.11.  Test Vector: Removing a local on-mesh network
+
+   o  NLI: 0
+   o  TID: 6
+   o  CMD: 5 ("CMD_VALUE_REMOVE")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+   o  VALUE: IPv6 Prefix "2001:DB8:3::"
+
+   Frame:
+
+         86 05 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 75]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+B.12.  Test Vector: Removal notification of an on-mesh network
+
+   o  NLI: 0
+   o  TID: 6
+   o  CMD: 8 ("CMD_VALUE_REMOVED")
+   o  PROP: 90 ("PROP_THREAD_ON_MESH_NETS")
+   o  VALUE: IPv6 Prefix "2001:DB8:3::"
+
+   Frame:
+
+         86 08 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+
+Appendix C.  Example Sessions
+
+C.1.  NCP Initialization
+
+   [CREF12]
+
+   Check the protocol version to see if it is supported:
+
+   o  CMD_VALUE_GET:PROP_PROTOCOL_VERSION
+   o  CMD_VALUE_IS:PROP_PROTOCOL_VERSION
+
+   Check the NCP version to see if a firmware update may be necessary:
+
+   o  CMD_VALUE_GET:PROP_NCP_VERSION
+   o  CMD_VALUE_IS:PROP_NCP_VERSION
+
+   Check interface type to make sure that it is what we expect:
+
+   o  CMD_VALUE_GET:PROP_INTERFACE_TYPE
+   o  CMD_VALUE_IS:PROP_INTERFACE_TYPE
+
+   If the host supports using vendor-specific commands, the vendor
+   should be verified before using them:
+
+   o  CMD_VALUE_GET:PROP_VENDOR_ID
+   o  CMD_VALUE_IS:PROP_VENDOR_ID
+
+   Fetch the capability list so that we know what features this NCP
+   supports:
+
+   o  CMD_VALUE_GET:PROP_CAPS
+   o  CMD_VALUE_IS:PROP_CAPS
+
+   If the NCP supports CAP_NET_SAVE, then we go ahead and recall the
+   network:
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 76]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  CMD_NET_RECALL
+
+C.2.  Attaching to a network
+
+   [CREF13]
+
+   We make the assumption that the NCP is not currently associated with
+   a network.
+
+   Set the network properties, if they were not already set:
+
+   o  CMD_VALUE_SET:PROP_PHY_CHAN
+   o  CMD_VALUE_IS:PROP_PHY_CHAN
+   o  CMD_VALUE_SET:PROP_NET_XPANID
+   o  CMD_VALUE_IS:PROP_NET_XPANID
+   o  CMD_VALUE_SET:PROP_MAC_15_4_PANID
+   o  CMD_VALUE_IS:PROP_MAC_15_4_PANID
+   o  CMD_VALUE_SET:PROP_NET_NETWORK_NAME
+   o  CMD_VALUE_IS:PROP_NET_NETWORK_NAME
+   o  CMD_VALUE_SET:PROP_NET_MASTER_KEY
+   o  CMD_VALUE_IS:PROP_NET_MASTER_KEY
+   o  CMD_VALUE_SET:PROP_NET_KEY_SEQUENCE_COUNTER
+   o  CMD_VALUE_IS:PROP_NET_KEY_SEQUENCE_COUNTER
+   o  CMD_VALUE_SET:PROP_NET_KEY_SWITCH_GUARDTIME
+   o  CMD_VALUE_IS:PROP_NET_KEY_SWITCH_GUARDTIME
+
+   Bring the network interface up:
+
+   o  CMD_VALUE_SET:PROP_NET_IF_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_IF_UP:TRUE
+
+   Bring the routing stack up:
+
+   o  CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+   Some asynchronous events from the NCP:
+
+   o  CMD_VALUE_IS:PROP_NET_ROLE
+   o  CMD_VALUE_IS:PROP_NET_PARTITION_ID
+   o  CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+C.3.  Successfully joining a pre-existing network
+
+   [CREF14]
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 77]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   This example session is identical to the above session up to the
+   point where we set PROP_NET_IF_UP to true.  From there, the behavior
+   changes.
+
+   o  CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+   o  CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+
+   Bring the routing stack up:
+
+   o  CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+   Some asynchronous events from the NCP:
+
+   o  CMD_VALUE_IS:PROP_NET_ROLE
+   o  CMD_VALUE_IS:PROP_NET_PARTITION_ID
+   o  CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+   Now let's save the network settings to NVRAM:
+
+   o  CMD_NET_SAVE
+
+C.4.  Unsuccessfully joining a pre-existing network
+
+   This example session is identical to the above session up to the
+   point where we set PROP_NET_IF_UP to true.  From there, the behavior
+   changes.
+
+   o  CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+   o  CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+
+   Bring the routing stack up:
+
+   o  CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+   Some asynchronous events from the NCP:
+
+   o  CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_JOIN_NO_PEERS
+   o  CMD_VALUE_IS:PROP_NET_STACK_UP:FALSE
+
+C.5.  Detaching from a network
+
+   TBD
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 78]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+C.6.  Attaching to a saved network
+
+   [CREF15]
+
+   Recall the saved network if you haven't already done so:
+
+   o  CMD_NET_RECALL
+
+   Bring the network interface up:
+
+   o  CMD_VALUE_SET:PROP_NET_IF_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_IF_UP:TRUE
+
+   Bring the routing stack up:
+
+   o  CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+   o  CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+   Some asynchronous events from the NCP:
+
+   o  CMD_VALUE_IS:PROP_NET_ROLE
+   o  CMD_VALUE_IS:PROP_NET_PARTITION_ID
+   o  CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+C.7.  NCP Software Reset
+
+   [CREF16]
+
+   o  CMD_RESET
+   o  CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_RESET_SOFTWARE
+
+   Then jump to Appendix C.1.
+
+C.8.  Adding an on-mesh prefix
+
+   TBD
+
+C.9.  Entering low-power modes
+
+   TBD
+
+C.10.  Sniffing raw packets
+
+   [CREF17]
+
+   This assumes that the NCP has been initialized.
+
+   Optionally set the channel:
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 79]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   o  CMD_VALUE_SET:PROP_PHY_CHAN:x
+   o  CMD_VALUE_IS:PROP_PHY_CHAN
+
+   Set the filter mode:
+
+   o  CMD_VALUE_SET:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONIT
+      OR
+   o  CMD_VALUE_IS:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONITO
+      R
+
+   Enable the raw stream:
+
+   o  CMD_VALUE_SET:PROP_MAC_RAW_STREAM_ENABLED:TRUE
+   o  CMD_VALUE_IS:PROP_MAC_RAW_STREAM_ENABLED:TRUE
+
+   Enable the PHY directly:
+
+   o  CMD_VALUE_SET:PROP_PHY_ENABLED:TRUE
+   o  CMD_VALUE_IS:PROP_PHY_ENABLED:TRUE
+
+   Now we will get raw 802.15.4 packets asynchronously on
+   PROP_STREAM_RAW:
+
+   o  CMD_VALUE_IS:PROP_STREAM_RAW:...
+   o  CMD_VALUE_IS:PROP_STREAM_RAW:...
+   o  CMD_VALUE_IS:PROP_STREAM_RAW:...
+
+   This mode may be entered even when associated with a network.  In
+   that case, you should set "PROP_MAC_PROMISCUOUS_MODE" to
+   "MAC_PROMISCUOUS_MODE_PROMISCUOUS" or "MAC_PROMISCUOUS_MODE_NORMAL",
+   so that you can avoid receiving packets from other networks or that
+   are destined for other nodes.
+
+Appendix D.  Glossary
+
+   [CREF18]
+
+   FCS
+      Final Checksum.  Bytes added to the end of a packet to help
+      determine if the packet was received without corruption.
+   NCP
+      Network Control Processor.
+   NLI
+      Network Link Identifier.  May be a value between zero and three.
+      See Section 2.1.2 for more information.
+   OS
+      Operating System, i.e. the IPv6 node using Spinel to control and
+      manage one or more of its IPv6 network interfaces.
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 80]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   PHY
+      Physical layer.  Refers to characteristics and parameters related
+      to the physical implementation and operation of a networking
+      medium.
+   PUI
+      Packed Unsigned Integer.  A way to serialize an unsigned integer
+      using one, two, or three bytes.  Used throughout the Spinel
+      protocol.  See Section 3.2 for more information.
+   TID
+      Transaction Identifier.  May be a value between zero and fifteen.
+      See Section 2.1.3 for more information.
+
+Appendix E.  Acknowledgments
+
+   Thread is a registered trademark of The Thread Group, Inc.
+
+   Special thanks to Nick Banks, Jonathan Hui, Abtin Keshavarzian, Yakun
+   Xu, Piotr Szkotak, Arjuna Sivasithambaresan and Martin Turon for
+   their substantial contributions and feedback related to this
+   document.
+
+   This document was prepared using mmark [3] by (Miek Gieben) and
+   xml2rfc (version 2) [4].
+
+Editorial Comments
+
+[CREF1] RQ: Eventually, when https://github.com/miekg/mmark/issues/95 is
+        addressed, the above table should be swapped out with this: |
+        0 | 1 | 2 | 3 | 4 | 5 | 6 |
+        7 | |---|---|---|---|---|---|---|---| | FLG || NLI || TID ||||
+
+[CREF2] RQ: We should consider reversing the numbering here so that 0 is
+        `POWER_STATE_ONLINE`. We may also want to include some extra
+        values between the defined values for future expansion, so that
+        we can preserve the ordered relationship. --
+
+[CREF3] RQ: We should consider reversing the numbering here so that 0 is
+        `POWER_STATE_ONLINE`. We may also want to include some extra
+        values between the defined values for future expansion, so that
+        we can preserve the ordered relationship. --
+
+[CREF4] RQ: The justification for the above behavior is to attempt to
+        avoid possible future interop problems by explicitly making sure
+        that unknown properties are ignored.  Since unknown properties
+        will obviously not be generating unsolicited updates, it seems
+        fairly harmless.  An implementation may print out a warning to
+        the debug stream.  Note that the error is still detectable: If
+        you VALUE\_SET unsupported properties, the resulting VALUE\_IS
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 81]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+        would contain only the supported properties of that set(since
+        the unsupported properties would be ignored).  If an
+        implementation cares that much about getting this right then it
+        needs to make sure that it checks PROP\_UNSOL\_UPDATE\_LIST
+        first.  --
+
+[CREF5] RQ: It may make sense to have a look at what Bluetooth HCI is
+        doing for native I^2C framing and go with that.
+
+[CREF6] RQ: It may make sense to have a look at what Bluetooth HCI is
+        doing for native USB framing and go with that.
+
+[CREF7] RQ: The PUI test-vector encodings need to be verified.
+
+[CREF8] RQ: FIXME: This test vector is incomplete.
+
+[CREF9] RQ: FIXME: This test vector is incomplete.
+
+[CREF10] RQ: FIXME: This test vector is incomplete.
+
+[CREF11] RQ: FIXME: This test vector is incomplete.
+
+[CREF12] RQ: FIXME: This example session is incomplete.
+
+[CREF13] RQ: FIXME: This example session is incomplete.
+
+[CREF14] RQ: FIXME: This example session is incomplete.
+
+[CREF15] RQ: FIXME: This example session is incomplete.
+
+[CREF16] RQ: FIXME: This example session is incomplete.
+
+[CREF17] RQ: FIXME: This example session is incomplete.
+
+[CREF18] RQ: Alphabetize before finalization.
+
+Authors' Addresses
+
+   Robert S. Quattlebaum
+   Nest Labs, Inc.
+   3400 Hillview Ave.
+   Palo Alto, California  94304
+   USA
+
+   Email: rquattle@nestlabs.com
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 82]
+
+Internet-Draft          Spinel Protocol (Unified)              June 2017
+
+
+   James Woodyatt (editor)
+   Nest Labs, Inc.
+   3400 Hillview Ave.
+   Palo Alto, California  94304
+   USA
+
+   Email: jhw@nestlabs.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Quattlebaum & Woodyatt  Expires December 24, 2017              [Page 83]
diff --git a/doc/header.html b/doc/header.html
new file mode 100644
index 0000000..e4284d7
--- /dev/null
+++ b/doc/header.html
@@ -0,0 +1,55 @@
+<!-- HTML header for doxygen 1.8.9.1-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen $doxygenversion"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath^jquery.js"></script>
+<script type="text/javascript" src="$relpath^dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+  <!--BEGIN PROJECT_LOGO-->
+  <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
+  <!--END PROJECT_LOGO-->
+  <!--BEGIN PROJECT_NAME-->
+  <td style="padding-left: 0.5em;">
+   <div id="projectname">
+   <!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
+   </div>
+   <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
+  </td>
+  <!--END PROJECT_NAME-->
+  <!--BEGIN !PROJECT_NAME-->
+   <!--BEGIN PROJECT_BRIEF-->
+    <td style="padding-left: 0.5em;">
+    <div id="projectbrief">$projectbrief</div>
+    </td>
+   <!--END PROJECT_BRIEF-->
+  <!--END !PROJECT_NAME-->
+  <!--BEGIN DISABLE_INDEX-->
+   <!--BEGIN SEARCHENGINE-->
+   <td>$searchbox</td>
+   <!--END SEARCHENGINE-->
+  <!--END DISABLE_INDEX-->
+ </tr>
+ </tbody>
+</table>
+</div>
+<!--END TITLEAREA-->
+<!-- end header part -->
diff --git a/doc/images/Open-Thread-Logo-200x42.png b/doc/images/Open-Thread-Logo-200x42.png
new file mode 100644
index 0000000..3be9551
--- /dev/null
+++ b/doc/images/Open-Thread-Logo-200x42.png
Binary files differ
diff --git a/doc/images/certified.svg b/doc/images/certified.svg
new file mode 100644
index 0000000..d7c65c4
--- /dev/null
+++ b/doc/images/certified.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.1 42.6" style="enable-background:new 0 0 92.1 42.6;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FF6633;}
+	.st1{fill:#FFFFFF;}
+</style>
+<g>
+	<g>
+		<path class="st0" d="M86,0L21.3,0C9.5,0,0,9.5,0,21.3C0,33,9.5,42.6,21.3,42.6l64.7,0c3.4,0,6.1-2.7,6.1-6.1V6.1
+			C92.1,2.7,89.4,0,86,0z"/>
+	</g>
+	<g>
+		<g>
+			<path class="st1" d="M21.3,6.1c-8.4,0-15.2,6.8-15.2,15.2c0,8.3,6.7,15.1,15,15.2V21.3h-5c-1.5,0-2.7,1.2-2.7,2.7
+				c0,1.5,1.2,2.7,2.7,2.7V30c-3.3,0-6-2.7-6-6c0-3.3,2.7-6,6-6h5v-1.7c0-2.8,2.2-5,5-5c2.7,0,5,2.2,5,5c0,2.8-2.2,5-5,5h-1.7v14.9
+				c6.9-1.5,12.1-7.6,12.1-14.9C36.5,12.9,29.7,6.1,21.3,6.1z"/>
+			<path class="st1" d="M27.8,16.3c0-0.9-0.8-1.7-1.7-1.7c-0.9,0-1.7,0.8-1.7,1.7V18h1.7C27,18,27.8,17.2,27.8,16.3z"/>
+		</g>
+		<g>
+			<g>
+				<path class="st1" d="M46.9,20.8c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.5L47.9,22c-0.3-0.3-0.6-0.4-1.1-0.4
+					c-0.2,0-0.4,0-0.6,0.1c-0.2,0.1-0.3,0.2-0.5,0.3c-0.1,0.1-0.2,0.3-0.3,0.5c-0.1,0.2-0.1,0.4-0.1,0.6c0,0.2,0,0.4,0.1,0.6
+					c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3c0.2,0.1,0.4,0.1,0.6,0.1c0.4,0,0.8-0.1,1.1-0.4l0.5,0.5
+					c-0.2,0.2-0.4,0.4-0.7,0.5c-0.3,0.1-0.6,0.2-0.9,0.2c-0.3,0-0.6-0.1-0.9-0.2c-0.3-0.1-0.5-0.3-0.7-0.5c-0.2-0.2-0.4-0.4-0.5-0.7
+					c-0.1-0.3-0.2-0.6-0.2-0.9c0-0.3,0.1-0.6,0.2-0.9c0.1-0.3,0.3-0.5,0.5-0.7c0.2-0.2,0.4-0.4,0.7-0.5
+					C46.3,20.9,46.6,20.8,46.9,20.8z"/>
+				<path class="st1" d="M53,20.8v0.8h-2.3v1.1H53v0.7h-2.3v1.1H53v0.8h-3v-4.5H53z"/>
+				<path class="st1" d="M57.5,25.4l-0.9-1.5h-1.3v1.5h-0.7v-4.5h2.3c0.4,0,0.8,0.1,1.1,0.4c0.3,0.3,0.4,0.6,0.4,1.1
+					c0,0.3-0.1,0.6-0.3,0.9c-0.2,0.3-0.4,0.4-0.7,0.5l0.9,1.6H57.5z M56.8,21.6h-1.5v1.5l1.5,0c0.2,0,0.4-0.1,0.5-0.2
+					c0.1-0.1,0.2-0.3,0.2-0.5c0-0.2-0.1-0.4-0.2-0.5C57.2,21.7,57,21.6,56.8,21.6z"/>
+				<path class="st1" d="M59.3,20.8h3.8v0.8h-1.5v3.8h-0.8v-3.8h-1.5V20.8z"/>
+				<path class="st1" d="M64.5,20.8h0.8v4.5h-0.8V20.8z"/>
+				<path class="st1" d="M69.8,20.8v0.8h-2.3v1.1h2.3v0.7h-2.3v1.9h-0.7v-4.5H69.8z"/>
+				<path class="st1" d="M71.4,20.8h0.7v4.5h-0.7V20.8z"/>
+				<path class="st1" d="M76.7,20.8v0.8h-2.3v1.1h2.3v0.7h-2.3v1.1h2.3v0.8h-3v-4.5H76.7z"/>
+				<path class="st1" d="M78.2,20.8h1.5c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.5c0.2,0.2,0.4,0.4,0.5,0.7
+					c0.1,0.3,0.2,0.6,0.2,0.9c0,0.3-0.1,0.6-0.2,0.9c-0.1,0.3-0.3,0.5-0.5,0.7c-0.2,0.2-0.4,0.4-0.7,0.5c-0.3,0.1-0.6,0.2-0.9,0.2
+					h-1.5V20.8z M79.7,24.6c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5c0.1-0.2,0.1-0.4,0.1-0.6
+					c0-0.2,0-0.4-0.1-0.6c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.3-0.2-0.5-0.3c-0.2-0.1-0.4-0.1-0.6-0.1H79v3H79.7z"/>
+			</g>
+			<g>
+				<path class="st1" d="M42.2,28c0.3,0,0.6,0.1,0.9,0.2c0.3,0.1,0.5,0.3,0.7,0.5l-0.5,0.5c-0.3-0.3-0.6-0.4-1.1-0.4
+					c-0.2,0-0.4,0-0.6,0.1c-0.2,0.1-0.3,0.2-0.5,0.3c-0.1,0.1-0.2,0.3-0.3,0.5c-0.1,0.2-0.1,0.4-0.1,0.6c0,0.2,0,0.4,0.1,0.6
+					c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3c0.2,0.1,0.4,0.1,0.6,0.1c0.4,0,0.8-0.1,1.1-0.4l0.5,0.5
+					c-0.2,0.2-0.4,0.4-0.7,0.5c-0.3,0.1-0.6,0.2-0.9,0.2c-0.3,0-0.6-0.1-0.9-0.2c-0.3-0.1-0.5-0.3-0.7-0.5c-0.2-0.2-0.4-0.4-0.5-0.7
+					c-0.1-0.3-0.2-0.6-0.2-0.9c0-0.3,0.1-0.6,0.2-0.9c0.1-0.3,0.3-0.5,0.5-0.7c0.2-0.2,0.4-0.4,0.7-0.5C41.6,28.1,41.9,28,42.2,28z"
+					/>
+				<path class="st1" d="M47,28c0.6,0,1.2,0.2,1.6,0.7c0.4,0.4,0.7,1,0.7,1.6c0,0.6-0.2,1.2-0.7,1.6c-0.4,0.4-1,0.7-1.6,0.7
+					c-0.6,0-1.2-0.2-1.6-0.7c-0.4-0.4-0.7-1-0.7-1.6c0-0.6,0.2-1.1,0.7-1.6C45.8,28.2,46.3,28,47,28z M47,28.8
+					c-0.4,0-0.8,0.1-1.1,0.4c-0.3,0.3-0.4,0.6-0.4,1.1c0,0.4,0.1,0.8,0.4,1.1c0.3,0.3,0.7,0.4,1.1,0.4c0.4,0,0.8-0.1,1.1-0.4
+					c0.3-0.3,0.4-0.7,0.4-1.1c0-0.4-0.1-0.8-0.4-1.1C47.7,28.9,47.4,28.8,47,28.8z"/>
+				<path class="st1" d="M51.3,29.8v2.7h-0.7V28l2.3,2.3l2.3-2.3v4.5h-0.8v-2.7l-1.5,1.5L51.3,29.8z"/>
+				<path class="st1" d="M57.7,31v1.5H57V28h2.3c0.4,0,0.8,0.1,1.1,0.4c0.3,0.3,0.4,0.6,0.4,1.1c0,0.4-0.1,0.8-0.4,1.1
+					C60,30.9,59.7,31,59.2,31H57.7z M57.7,28.8v1.5h1.5c0.2,0,0.4-0.1,0.5-0.2c0.1-0.1,0.2-0.3,0.2-0.5c0-0.2-0.1-0.4-0.2-0.5
+					c-0.1-0.1-0.3-0.2-0.5-0.2H57.7z"/>
+				<path class="st1" d="M64.1,28c0.6,0,1.2,0.2,1.6,0.7c0.4,0.4,0.7,1,0.7,1.6c0,0.6-0.2,1.2-0.7,1.6c-0.4,0.4-1,0.7-1.6,0.7
+					c-0.6,0-1.2-0.2-1.6-0.7c-0.4-0.4-0.7-1-0.7-1.6c0-0.6,0.2-1.1,0.7-1.6C62.9,28.2,63.5,28,64.1,28z M64.1,28.8
+					c-0.4,0-0.8,0.1-1.1,0.4c-0.3,0.3-0.4,0.6-0.4,1.1c0,0.4,0.1,0.8,0.4,1.1c0.3,0.3,0.7,0.4,1.1,0.4c0.4,0,0.8-0.1,1.1-0.4
+					c0.3-0.3,0.4-0.7,0.4-1.1c0-0.4-0.1-0.8-0.4-1.1C64.9,28.9,64.5,28.8,64.1,28.8z"/>
+				<path class="st1" d="M67.7,28l3,2.7V28h0.8v4.5v0v0l-3-2.7v2.7h-0.8V28z"/>
+				<path class="st1" d="M76,28v0.8h-2.3v1.1H76v0.7h-2.3v1.1H76v0.8h-3V28H76z"/>
+				<path class="st1" d="M77.6,28l3,2.7V28h0.8v4.5v0v0l-3-2.7v2.7h-0.7V28z"/>
+				<path class="st1" d="M82.9,28h3.8v0.8h-1.5v3.8h-0.8v-3.8h-1.5V28z"/>
+			</g>
+			<g>
+				<path class="st1" d="M39.9,10h5.7v1.1h-2.3v5.6h-1.1v-5.6h-2.3V10z"/>
+				<path class="st1" d="M48.2,10h1.1v2.8h3.4V10h1.1v6.8h-1.1V14h-3.4v2.8h-1.1V10z"/>
+				<path class="st1" d="M61.1,16.8l-1.3-2.3h-1.9v2.3h-1.1V10h3.4c0.6,0,1.1,0.2,1.6,0.7c0.4,0.4,0.7,1,0.7,1.6
+					c0,0.5-0.1,0.9-0.4,1.3c-0.3,0.4-0.6,0.6-1,0.8l1.4,2.4H61.1z M60.2,11.2h-2.3v2.2l2.3,0c0.3,0,0.6-0.1,0.8-0.3
+					c0.2-0.2,0.3-0.5,0.3-0.8c0-0.3-0.1-0.6-0.3-0.8C60.7,11.3,60.5,11.2,60.2,11.2z"/>
+				<path class="st1" d="M69.7,10v1.1h-3.4v1.7h3.4V14h-3.4v1.7h3.4v1.1h-4.5V10H69.7z"/>
+				<path class="st1" d="M76.9,15.7h-3.1l-0.6,1.1H72l3.4-6.8l3.4,6.8h-1.3L76.9,15.7z M74.4,14.6h2l-1-2L74.4,14.6z"/>
+				<path class="st1" d="M81,10h2.3c0.5,0,0.9,0.1,1.3,0.3c0.4,0.2,0.8,0.4,1.1,0.7c0.3,0.3,0.6,0.7,0.7,1.1
+					c0.2,0.4,0.3,0.8,0.3,1.3c0,0.5-0.1,0.9-0.3,1.3c-0.2,0.4-0.4,0.8-0.7,1.1c-0.3,0.3-0.7,0.6-1.1,0.7c-0.4,0.2-0.9,0.3-1.3,0.3
+					H81V10z M83.3,15.7c0.3,0,0.6-0.1,0.9-0.2c0.3-0.1,0.5-0.3,0.7-0.5c0.2-0.2,0.4-0.5,0.5-0.7c0.1-0.3,0.2-0.6,0.2-0.9
+					c0-0.3-0.1-0.6-0.2-0.9c-0.1-0.3-0.3-0.5-0.5-0.7c-0.2-0.2-0.5-0.4-0.7-0.5c-0.3-0.1-0.6-0.2-0.9-0.2h-1.1v4.5H83.3z"/>
+			</g>
+		</g>
+	</g>
+</g>
+</svg>
diff --git a/doc/images/openthread_contrib.png b/doc/images/openthread_contrib.png
new file mode 100644
index 0000000..73d80f3
--- /dev/null
+++ b/doc/images/openthread_contrib.png
Binary files differ
diff --git a/doc/images/openthread_logo.png b/doc/images/openthread_logo.png
new file mode 100644
index 0000000..b6a65ca
--- /dev/null
+++ b/doc/images/openthread_logo.png
Binary files differ
diff --git a/doc/images/ot-codelab.png b/doc/images/ot-codelab.png
new file mode 100644
index 0000000..d1a49d6
--- /dev/null
+++ b/doc/images/ot-codelab.png
Binary files differ
diff --git a/doc/images/ot-contrib-arm.png b/doc/images/ot-contrib-arm.png
new file mode 100644
index 0000000..dc4556e
--- /dev/null
+++ b/doc/images/ot-contrib-arm.png
Binary files differ
diff --git a/doc/images/ot-contrib-atmel.png b/doc/images/ot-contrib-atmel.png
new file mode 100644
index 0000000..878322f
--- /dev/null
+++ b/doc/images/ot-contrib-atmel.png
Binary files differ
diff --git a/doc/images/ot-contrib-dialog.png b/doc/images/ot-contrib-dialog.png
new file mode 100644
index 0000000..372617e
--- /dev/null
+++ b/doc/images/ot-contrib-dialog.png
Binary files differ
diff --git a/doc/images/ot-contrib-ms.png b/doc/images/ot-contrib-ms.png
new file mode 100644
index 0000000..a403a40
--- /dev/null
+++ b/doc/images/ot-contrib-ms.png
Binary files differ
diff --git a/doc/images/ot-contrib-nest.png b/doc/images/ot-contrib-nest.png
new file mode 100644
index 0000000..2e79d4f
--- /dev/null
+++ b/doc/images/ot-contrib-nest.png
Binary files differ
diff --git a/doc/images/ot-contrib-nordic.png b/doc/images/ot-contrib-nordic.png
new file mode 100644
index 0000000..6629aef
--- /dev/null
+++ b/doc/images/ot-contrib-nordic.png
Binary files differ
diff --git a/doc/images/ot-contrib-nxp.png b/doc/images/ot-contrib-nxp.png
new file mode 100644
index 0000000..dac40a0
--- /dev/null
+++ b/doc/images/ot-contrib-nxp.png
Binary files differ
diff --git a/doc/images/ot-contrib-qc.png b/doc/images/ot-contrib-qc.png
new file mode 100644
index 0000000..4efce33
--- /dev/null
+++ b/doc/images/ot-contrib-qc.png
Binary files differ
diff --git a/doc/images/ot-contrib-synopsys.png b/doc/images/ot-contrib-synopsys.png
new file mode 100644
index 0000000..18a8629
--- /dev/null
+++ b/doc/images/ot-contrib-synopsys.png
Binary files differ
diff --git a/doc/images/ot-contrib-ti.png b/doc/images/ot-contrib-ti.png
new file mode 100644
index 0000000..2c8901a
--- /dev/null
+++ b/doc/images/ot-contrib-ti.png
Binary files differ
diff --git a/doc/images/windows-app-details.png b/doc/images/windows-app-details.png
new file mode 100644
index 0000000..a2977c1
--- /dev/null
+++ b/doc/images/windows-app-details.png
Binary files differ
diff --git a/doc/images/windows-app-interface-list.png b/doc/images/windows-app-interface-list.png
new file mode 100644
index 0000000..922b5f2
--- /dev/null
+++ b/doc/images/windows-app-interface-list.png
Binary files differ
diff --git a/doc/images/windows-app-talk-client.png b/doc/images/windows-app-talk-client.png
new file mode 100644
index 0000000..b40c9a4
--- /dev/null
+++ b/doc/images/windows-app-talk-client.png
Binary files differ
diff --git a/doc/images/windows-app-talk-server.png b/doc/images/windows-app-talk-server.png
new file mode 100644
index 0000000..165689b
--- /dev/null
+++ b/doc/images/windows-app-talk-server.png
Binary files differ
diff --git a/doc/images/windows_design.png b/doc/images/windows_design.png
new file mode 100644
index 0000000..c5fa343
--- /dev/null
+++ b/doc/images/windows_design.png
Binary files differ
diff --git a/doc/spinel-protocol-src/Makefile b/doc/spinel-protocol-src/Makefile
new file mode 100644
index 0000000..6e1fb21
--- /dev/null
+++ b/doc/spinel-protocol-src/Makefile
@@ -0,0 +1,114 @@
+#
+#  Copyright (c) 2016, Nest Labs, Inc.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+XML2RFC_CACHE_DIR ?= $(HOME)/.cache/xml2rfc
+
+TOOL_PREFIX        = $(DOCKER) run --rm --user=`id -u`:`id -g` -v `pwd`:/rfc -v $(XML2RFC_CACHE_DIR):/var/cache/xml2rfc paulej/rfctools
+
+DOCKER            ?= docker
+MD2RFC            ?= $(TOOL_PREFIX) md2rfc
+XML2RFC           ?= $(TOOL_PREFIX) xml2rfc
+MMARK             ?= $(TOOL_PREFIX) mmark
+SED               ?= sed
+RM_F		      ?= rm -f
+MKDIR_P		      ?= mkdir -p
+
+SOURCE_DATE       := $(shell (TZ=UTC git log -n 1 --date=iso-strict-local --pretty=format:%ad 2> /dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ" ) | sed 's/+00:00$$/Z/')
+SOURCE_VERSION    ?= $(shell git describe --dirty --always --match "--PoIsOn--" 2> /dev/null)
+
+# -------------
+
+SRC   := $(wildcard draft-*.md) $(wildcard draft-*.md.in)
+XML   := $(patsubst %.md,%.xml,$(patsubst %.md.in,%.xml,$(SRC)))
+TXT   := $(patsubst %.md,%.txt,$(patsubst %.md.in,%.txt,$(SRC)))
+HTML  := $(patsubst %.md,%.html,$(patsubst %.md.in,%.html,$(SRC)))
+
+all: $(XML) $(TXT) $(HTML)
+
+clean:
+	$(RM_F) $(XML) $(TXT) $(HTML) $(patsubst %.md.in,%.md,$(wildcard draft-*.md.in))
+
+$(XML2RFC_CACHE_DIR):
+	$(MKDIR_P) "$(XML2RFC_CACHE_DIR)"
+
+%.md: %.md.in
+	$(SED) 's/@SOURCE_VERSION@/$(SOURCE_VERSION)/g;s/@SOURCE_DATE@/$(SOURCE_DATE)/g' < $< > $@
+
+%.xml: %.md
+	$(MMARK) -xml2 -page $< $@
+	$(SED) -i "" -e 's/fullname="James Woodyatt"/fullname="James Woodyatt" role="editor"/' $@
+
+%.html: %.xml $(XML2RFC_CACHE_DIR)
+	$(XML2RFC) --html $<
+
+%.txt: %.xml $(XML2RFC_CACHE_DIR)
+	$(XML2RFC) --text $<
+
+# -------------
+
+draft-rquattle-spinel-basis.xml: \
+	draft-rquattle-spinel-basis.md \
+	spinel-commands.md \
+	spinel-data-packing.md \
+	spinel-example-sessions.md \
+	spinel-feature-host-buffer-offload.md \
+	spinel-feature-network-save.md \
+	spinel-frame-format.md \
+	spinel-framing.md \
+	spinel-prop-core.md \
+	spinel-prop-debug.md \
+	spinel-prop-ipv6.md \
+	spinel-prop-mac.md \
+	spinel-prop-net.md \
+	spinel-prop-phy.md \
+	spinel-prop.md \
+	spinel-status-codes.md \
+	spinel-tech-thread.md \
+	spinel-test-vectors.md \
+	$(NULL)
+
+draft-rquattle-spinel-unified.xml: \
+	draft-rquattle-spinel-unified.md \
+	spinel-commands.md \
+	spinel-data-packing.md \
+	spinel-example-sessions.md \
+	spinel-feature-host-buffer-offload.md \
+	spinel-feature-network-save.md \
+	spinel-frame-format.md \
+	spinel-framing.md \
+	spinel-prop-core.md \
+	spinel-prop-debug.md \
+	spinel-prop-ipv6.md \
+	spinel-prop-mac.md \
+	spinel-prop-net.md \
+	spinel-prop-phy.md \
+	spinel-prop.md \
+	spinel-status-codes.md \
+	spinel-tech-thread.md \
+	spinel-test-vectors.md \
+	$(NULL)
diff --git a/doc/spinel-protocol-src/draft-rquattle-spinel-basis.md.in b/doc/spinel-protocol-src/draft-rquattle-spinel-basis.md.in
new file mode 100644
index 0000000..3c50a44
--- /dev/null
+++ b/doc/spinel-protocol-src/draft-rquattle-spinel-basis.md.in
@@ -0,0 +1,109 @@
+%%%
+    title           = "Spinel: A protocol basis for control and management of IPv6 network interface co-processors"
+    abbrev          = "Spinel Basis"
+    category        = "std"
+    docName         = "draft-rquattle-spinel-basis"
+    ipr             = "trust200902"
+    keyword         = ["Spinel", "IPv6", "NCP"]
+    date            = @SOURCE_DATE@
+    
+    [pi]
+    editing         = "yes"
+    compact         = "yes"
+    subcompact      = "yes"
+    comments        = "yes"
+    
+    [[author]]
+    initials        = "R."
+    surname         = "Quattlebaum"
+    fullname        = "Robert S. Quattlebaum"
+    organization    = "Nest Labs, Inc."
+    
+        [author.address]
+        email       = "rquattle@nestlabs.com"
+        
+        [author.address.postal]
+        street      = "3400 Hillview Ave."
+        city        = "Palo Alto"
+        region      = "California"
+        code        = "94304"
+        country     = "USA"
+    
+    [[author]]
+    initials        = "j.h."
+    surname         = "woodyatt"
+    fullname        = "james woodyatt"
+    organization    = "Nest Labs, Inc."
+    role            = "editor"
+    
+        [author.address]
+        email       = "jhw@nestlabs.com"
+        
+        [author.address.postal]
+        street      = "3400 Hillview Ave."
+        city        = "Palo Alto"
+        region      = "California"
+        code        = "94304"
+        country     = "USA"
+%%%
+
+.# Abstract
+
+This document specifies the basis of the Spinel protocol, which facilitates the control and management of IPv6 network interfaces on devices where general purpose application processors offload network functions at their interfaces to network co-processors (NCP) connected by simple communication links like serial data channels. Spinel was initially designed for use with Thread network co-processors, but its basis is general purpose and intended to be easily adapted to other types of IPv6 network interface.
+
+{mainmatter}
+
+# Introduction #
+
+Spinel is a host-controller protocol designed to enable interoperation over simple serial connections between general purpose device operating systems (OS) and network co-processors (NCP) for the purpose of controlling and managing their IPv6 network interfaces, achieving the following goals:
+
+*   Adopt a layered approach to the protocol design, allowing future support for other types of IPv6 link.
+*   Minimize the number of required commands/methods by supporting a rich, property-based programming interface.
+*   Support NCPs capable of multiple simultaneous IPv6 interfaces.
+*   Support NCPs capable of communicating simultaneously on more than one physical link.
+*   Gracefully handle the addition of new features and capabilities without necessarily breaking backward compatibility.
+*   Be as minimal and light-weight as possible without unnecessarily sacrificing flexibility.
+
+On top of this core framework, properties and commands enable various common features of IPv6. In related and forthcoming documents, the Spinel protocol is extended to support NCP implementations for specific IPv6 link types, e.g. Thread.
+
+{{spinel-frame-format.md}}
+
+{{spinel-data-packing.md}}
+
+{{spinel-commands.md}}
+
+{{spinel-prop.md}}
+
+{{spinel-status-codes.md}}
+
+{{spinel-tech-thread.md}}
+
+{{spinel-feature-network-save.md}}
+
+{{spinel-feature-host-buffer-offload.md}}
+
+{{spinel-feature-jam-detect.md}}
+
+{{spinel-feature-gpio.md}}
+
+{{spinel-feature-trng.md}}
+
+{{spinel-security-considerations.md}}
+
+{backmatter}
+
+{{spinel-framing.md}}
+
+{{spinel-test-vectors.md}}
+
+{{spinel-example-sessions.md}}
+
+{{spinel-basis-glossary.md}}
+
+# Acknowledgments #
+
+Thread is a registered trademark of The Thread Group, Inc.
+
+Special thanks to Nick Banks, Jonathan Hui, Abtin Keshavarzian, Piotr Szkotak, Arjuna Sivasithambaresan and Martin Turon for their substantial contributions and feedback related to this document.
+
+This document was prepared using [mmark](https://github.com/miekg/mmark) by (Miek Gieben) and [xml2rfc (version 2)](http://xml2rfc.ietf.org/).
diff --git a/doc/spinel-protocol-src/draft-rquattle-spinel-unified.md.in b/doc/spinel-protocol-src/draft-rquattle-spinel-unified.md.in
new file mode 100644
index 0000000..d468f63
--- /dev/null
+++ b/doc/spinel-protocol-src/draft-rquattle-spinel-unified.md.in
@@ -0,0 +1,174 @@
+%%%
+    title           = "Spinel Host-Controller Protocol"
+    abbrev          = "Spinel Protocol (Unified)"
+    category        = "info"
+    docName         = "draft-rquattle-spinel-unified-@SOURCE_VERSION@"
+    ipr             = "noDerivativesTrust200902"
+    keyword         = ["Spinel", "IPv6", "NCP"]
+    date            = @SOURCE_DATE@
+    submissionType  = "independent"
+    
+    [pi]
+    editing         = "yes"
+    compact         = "yes"
+    subcompact      = "yes"
+    comments        = "yes"
+    
+    [[author]]
+    initials        = "R."
+    surname         = "Quattlebaum"
+    fullname        = "Robert S. Quattlebaum"
+    organization    = "Nest Labs, Inc."
+    
+        [author.address]
+        email       = "rquattle@nestlabs.com"
+        
+        [author.address.postal]
+        street      = "3400 Hillview Ave."
+        city        = "Palo Alto"
+        region      = "California"
+        code        = "94304"
+        country     = "USA"
+    
+    [[author]]
+    role            = "editor"
+    initials        = "J.H."
+    surname         = "Woodyatt"
+    fullname        = "James Woodyatt"
+    organization    = "Nest Labs, Inc."
+    
+        [author.address]
+        email       = "jhw@nestlabs.com"
+        
+        [author.address.postal]
+        street      = "3400 Hillview Ave."
+        city        = "Palo Alto"
+        region      = "California"
+        code        = "94304"
+        country     = "USA"
+%%%
+
+.# Abstract
+
+This document describes the Spinel protocol, which facilitates the control and
+management of IPv6 network interfaces on devices where general purpose
+application processors offload network functions at their interfaces to network
+co-processors (NCP) connected by simple communication links like serial data
+channels. While initially developed to support Thread(R), Spinel's layered
+design allows it to be easily adapted to other similar network technologies.
+
+This document also describes various Spinel specializations, including support
+for the Thread(R) low-power mesh network technology.
+
+
+{mainmatter}
+
+# Introduction #
+
+Spinel is a host-controller protocol designed to enable interoperation over simple serial connections between general purpose device operating systems (OS) and network co-processors (NCP) for the purpose of controlling and managing their IPv6 network interfaces, achieving the following goals:
+
+*   Adopt a layered approach to the protocol design, allowing future
+    support for other network protocols.
+*   Minimize the number of required commands/methods by providing a
+    rich, property-based API.
+*   Support NCPs capable of being connected to more than one network
+    at a time.
+*   Gracefully handle the addition of new features and capabilities
+    without necessarily breaking backward compatibility.
+*   Be as minimal and light-weight as possible without unnecessarily
+    sacrificing flexibility.
+
+On top of this core framework, we define the properties and commands
+to enable various features and network protocols.
+
+## About this Draft ##
+
+This document is currently in a draft status and is changing often.
+This section discusses some ideas for changes to the protocol that
+haven't yet been fully specified, as well as some of the impetus for
+the current design.
+
+### Scope ###
+
+The eventual intent is to have two documents: A Spinel basis document
+which discusses the network-technology-agnostic mechanisms and a
+Thread(R) specialization document which describes all of the Thread(R)-specific
+implementation details. Currently, this document covers both.
+
+### Renumbering ###
+
+Efforts are currently maintained to try to prevent overtly
+backward-incompatible changes to the existing protocol, but if you are
+implementing Spinel in your own products you should expect there to be
+at least one large renumbering event and major version number change
+before the standard is considered "baked". All changes will be clearly
+marked and documented to make such a transition as easy as possible.
+
+To allow conclusive detection of protocol (in)compatibility between
+the host and the NCP, the following commands and properties are
+already considered to be "baked" and will not change:
+
+*   Command IDs zero through eight. (Reset, No-op, and Property-Value
+    Commands)
+*   Property IDs zero through two. (Last status, Protocol Version, and
+    NCP Version)
+
+Renumbering would be undertaken in order to better organize the
+allocation of property IDs and capability IDs. One of the initial
+goals of this protocol was for it to be possible for a host or NCP to
+only implement properties with values less than 127 and for the NCP to
+still be usable---relegating all larger property values for extra
+features or other capabilities that aren't strictly necessary. This
+would allow simple implementations to avoid the need to implement
+support for PUIs ((#packed-unsigned-integer)).
+
+As time has gone by and the protocol has become more fleshed out, it
+has become clear that some of the initial allocations were inadequate
+and should be revisited if we want to try to achieve the original
+goal.
+
+
+{{spinel-frame-format.md}}
+
+{{spinel-data-packing.md}}
+
+{{spinel-commands.md}}
+
+{{spinel-prop.md}}
+
+{{spinel-status-codes.md}}
+
+{{spinel-tech-thread.md}}
+
+{{spinel-feature-network-save.md}}
+
+{{spinel-feature-host-buffer-offload.md}}
+
+{{spinel-feature-jam-detect.md}}
+
+{{spinel-feature-gpio.md}}
+
+{{spinel-feature-trng.md}}
+
+{{spinel-security-considerations.md}}
+
+{backmatter}
+
+{{spinel-framing.md}}
+
+{{spinel-test-vectors.md}}
+
+{{spinel-example-sessions.md}}
+
+{{spinel-basis-glossary.md}}
+
+# Acknowledgments #
+
+Thread is a registered trademark of The Thread Group, Inc.
+
+Special thanks to Nick Banks, Jonathan Hui, Abtin Keshavarzian, Yakun Xu,
+Piotr Szkotak, Arjuna Sivasithambaresan and Martin Turon for their
+substantial contributions and feedback related to this document.
+
+This document was prepared using [mmark](https://github.com/miekg/mmark)
+by (Miek Gieben) and [xml2rfc (version 2)](http://xml2rfc.ietf.org/).
diff --git a/doc/spinel-protocol-src/spinel-basis-glossary.md b/doc/spinel-protocol-src/spinel-basis-glossary.md
new file mode 100644
index 0000000..a9abf34
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-basis-glossary.md
@@ -0,0 +1,25 @@
+# Glossary #
+
+<!-- RQ -- Alphabetize before finalization. -->
+
+FCS
+: Final Checksum. Bytes added to the end of a packet to help determine if the packet was received without corruption.
+
+NCP
+: Network Control Processor.
+
+NLI
+: Network Link Identifier. May be a value between zero and three. See (#nli-network-link-identifier) for more information.
+
+OS
+: Operating System, i.e. the IPv6 node using Spinel to control and manage one or more of its IPv6 network interfaces.
+
+PHY
+: Physical layer. Refers to characteristics and parameters related to the physical implementation and operation of a networking medium.
+
+PUI
+: Packed Unsigned Integer. A way to serialize an unsigned integer using one, two, or three bytes. Used throughout the Spinel protocol. See (#packed-unsigned-integer) for more information.
+
+TID
+: Transaction Identifier. May be a value between zero and fifteen. See (#tid-transaction-identifier) for more information.
+
diff --git a/doc/spinel-protocol-src/spinel-commands.md b/doc/spinel-protocol-src/spinel-commands.md
new file mode 100644
index 0000000..0c6bae0
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-commands.md
@@ -0,0 +1,330 @@
+# Commands
+
+## CMD 0: (Host->NCP) CMD_NOOP {#cmd-noop}
+
+Octets: |    1   |     1
+--------|--------|----------
+Fields: | HEADER | CMD_NOOP
+
+No-Operation command. Induces the NCP to send a success status back to
+the host. This is primarily used for liveliness checks.
+
+The command payload for this command SHOULD be empty. The receiver
+MUST ignore any non-empty command payload.
+
+There is no error condition for this command.
+
+
+
+## CMD 1: (Host->NCP) CMD_RESET {#cmd-reset}
+
+Octets: |    1   |     1
+--------|--------|----------
+Fields: | HEADER | CMD_RESET
+
+Reset NCP command. Causes the NCP to perform a software reset. Due to
+the nature of this command, the TID is ignored. The host should
+instead wait for a `CMD_PROP_VALUE_IS` command from the NCP indicating
+`PROP_LAST_STATUS` has been set to `STATUS_RESET_SOFTWARE`.
+
+The command payload for this command SHOULD be empty. The receiver
+MUST ignore any non-empty command payload.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+instead with the value set to the generated status code for the error.
+
+
+
+## CMD 2: (Host->NCP) CMD_PROP_VALUE_GET {#cmd-prop-value-get}
+
+Octets: |    1   |          1         |   1-3
+--------|--------|--------------------|---------
+Fields: | HEADER | CMD_PROP_VALUE_GET | PROP_ID
+
+Get property value command. Causes the NCP to emit a
+`CMD_PROP_VALUE_IS` command for the given property identifier.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer).
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+instead with the value set to the generated status code for the error.
+
+
+
+## CMD 3: (Host->NCP) CMD_PROP_VALUE_SET {#cmd-prop-value-set}
+
+Octets: |    1   |          1         |   1-3   |    *n*
+--------|--------|--------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_SET | PROP_ID | VALUE
+
+Set property value command. Instructs the NCP to set the given
+property to the specific given value, replacing any previous value.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the property value. The exact format of the property value is defined
+by the property.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+
+## CMD 4: (Host->NCP) CMD_PROP_VALUE_INSERT {#cmd-prop-value-insert}
+
+Octets: |    1   |          1            |   1-3   |    *n*
+--------|--------|-----------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_INSERT | PROP_ID | VALUE
+
+Insert value into property command. Instructs the NCP to insert the
+given value into a list-oriented property, without removing other
+items in the list. The resulting order of items in the list is defined
+by the individual property being operated on.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the value to be inserted. The exact format of the value is defined by
+the property.
+
+If the type signature of the property specified by `PROP_ID` consists
+of a single structure enclosed by an array (`A(t(...))`), then the
+contents of `VALUE` MUST contain the contents of the structure (`...`)
+rather than the serialization of the whole item (`t(...)`).  Specifically,
+the length of the structure MUST NOT be prepended to `VALUE`. This
+helps to eliminate redundant data.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+
+## CMD 5: (Host->NCP) CMD_PROP_VALUE_REMOVE {#cmd-prop-value-remove}
+
+Octets: |    1   |          1            |   1-3   |    *n*
+--------|--------|-----------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_REMOVE | PROP_ID | VALUE
+
+Remove value from property command. Instructs the NCP to remove the
+given value from a list-oriented property, without affecting other
+items in the list. The resulting order of items in the list is defined
+by the individual property being operated on.
+
+Note that this command operates *by value*, not by index!
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the value to be removed. The exact format of the value is defined by
+the property.
+
+If the type signature of the property specified by `PROP_ID` consists
+of a single structure enclosed by an array (`A(t(...))`), then the
+contents of `VALUE` MUST contain the contents of the structure (`...`)
+rather than the serialization of the whole item (`t(...)`).  Specifically,
+the length of the structure MUST NOT be prepended to `VALUE`. This
+helps to eliminate redundant data.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+## CMD 6: (NCP->Host) CMD_PROP_VALUE_IS {#cmd-prop-value-is}
+
+Octets: |    1   |          1        |   1-3   |    *n*
+--------|--------|-------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_IS | PROP_ID | VALUE
+
+Property value notification command. This command can be sent by the
+NCP in response to a previous command from the host, or it can be sent
+by the NCP in an unsolicited fashion to notify the host of various
+state changes asynchronously.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the current value of the given property.
+
+
+
+## CMD 7: (NCP->Host) CMD_PROP_VALUE_INSERTED {#cmd-prop-value-inserted}
+
+Octets: |    1   |            1            |   1-3   |    *n*
+--------|--------|-------------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_INSERTED | PROP_ID | VALUE
+
+Property value insertion notification command. This command can be
+sent by the NCP in response to the `CMD_PROP_VALUE_INSERT` command, or
+it can be sent by the NCP in an unsolicited fashion to notify the host
+of various state changes asynchronously.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the value that was inserted into the given property.
+
+If the type signature of the property specified by `PROP_ID` consists
+of a single structure enclosed by an array (`A(t(...))`), then the
+contents of `VALUE` MUST contain the contents of the structure (`...`)
+rather than the serialization of the whole item (`t(...)`).  Specifically,
+the length of the structure MUST NOT be prepended to `VALUE`. This
+helps to eliminate redundant data.
+
+The resulting order of items in the list is defined by the given
+property.
+
+## CMD 8: (NCP->Host) CMD_PROP_VALUE_REMOVED {#cmd-prop-value-removed}
+
+Octets: |    1   |            1           |   1-3   |    *n*
+--------|--------|------------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_REMOVED | PROP_ID | VALUE
+
+Property value removal notification command. This command can be sent
+by the NCP in response to the `CMD_PROP_VALUE_REMOVE` command, or it
+can be sent by the NCP in an unsolicited fashion to notify the host of
+various state changes asynchronously.
+
+Note that this command operates *by value*, not by index!
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in (#packed-unsigned-integer), followed by
+the value that was removed from the given property.
+
+If the type signature of the property specified by `PROP_ID` consists
+of a single structure enclosed by an array (`A(t(...))`), then the
+contents of `VALUE` MUST contain the contents of the structure (`...`)
+rather than the serialization of the whole item (`t(...)`).  Specifically,
+the length of the structure MUST NOT be prepended to `VALUE`. This
+helps to eliminate redundant data.
+
+The resulting order of items in the list is defined by the given
+property.
+
+
+## CMD 18: (Host->NCP) CMD_PEEK {#cmd-peek}
+
+Octets: |    1   |     1    |    4    | 2
+--------|--------|----------|---------|-------
+Fields: | HEADER | CMD_PEEK | ADDRESS | COUNT
+
+This command allows the NCP to fetch values from the RAM of the NCP
+for debugging purposes. Upon success, `CMD_PEEK_RET` is sent from the
+NCP to the host. Upon failure, `PROP_LAST_STATUS` is emitted with
+the appropriate error indication.
+
+Due to the low-level nature of this command, certain error conditions
+may induce the NCP to reset.
+
+The NCP MAY prevent certain regions of memory from being accessed.
+
+The implementation of this command has security implications.
+See (#security-considerations) for more information.
+
+This command requires the capability `CAP_PEEK_POKE` to be present.
+
+## CMD 19: (NCP->Host) CMD_PEEK_RET {#cmd-peek-ret}
+
+Octets: |    1   |     1        |    4    | 2     | *n*
+--------|--------|--------------|---------|-------|-------
+Fields: | HEADER | CMD_PEEK_RET | ADDRESS | COUNT | BYTES
+
+This command contains the contents of memory that was requested by
+a previous call to `CMD_PEEK`.
+
+This command requires the capability `CAP_PEEK_POKE` to be present.
+
+## CMD 20: (Host->NCP) CMD_POKE {#cmd-poke}
+
+Octets: |    1   |     1    |    4    | 2     | *n*
+--------|--------|----------|---------|-------|-------
+Fields: | HEADER | CMD_POKE | ADDRESS | COUNT | BYTES
+
+This command writes the bytes to the specified memory address
+for debugging purposes.
+
+Due to the low-level nature of this command, certain error conditions
+may induce the NCP to reset.
+
+The implementation of this command has security implications.
+See (#security-considerations) for more information.
+
+This command requires the capability `CAP_PEEK_POKE` to be present.
+
+## CMD 21: (Host->NCP) CMD_PROP_VALUE_MULTI_GET {#cmd-prop-value-multi-get}
+
+*   Argument-Encoding: `A(i)`
+*   Required Capability: `CAP_CMD_MULTI`
+
+Fetch the value of multiple properties in one command. Arguments are
+an array of property IDs. If all properties are fetched successfully,
+a `CMD_PROP_VALUES_ARE` command is sent back to the host containing
+the propertyid and value of each fetched property. The order of the
+results in `CMD_PROP_VALUES_ARE` match the order of properties given
+in `CMD_PROP_VALUE_GET`.
+
+Errors fetching individual properties are reflected as indicating a
+change to `PROP_LAST_STATUS` for that property's place.
+
+Not all properties can be fetched using this method. As a general rule
+of thumb, any property that blocks when getting will fail for that
+individual property with `STATUS_INVALID_COMMAND_FOR_PROP`.
+
+## CMD 22: (Host->NCP) CMD_PROP_VALUE_MULTI_SET {#cmd-prop-value-multi-set}
+
+*   Argument-Encoding: `A(iD)`
+*   Required Capability: `CAP_CMD_MULTI`
+
+Octets: |    1   |          1               |   *n*
+--------|--------|--------------------------|----------------------
+Fields: | HEADER | CMD_PROP_VALUE_MULTI_SET | Property/Value Pairs
+
+With each property/value pair being:
+
+Octets: |    2   |   1-3   |  *n*
+--------|--------|---------|------------
+Fields: | LENGTH | PROP_ID | PROP_VALUE
+
+This command sets the value of several properties at once in the given
+order. The setting of properties stops at the first error, ignoring
+any later properties.
+
+The result of this command is generally `CMD_PROP_VALUES_ARE` unless
+(for example) a parsing error has occured (in which case
+`CMD_PROP_VALUE_IS` for `PROP_LAST_STATUS` would be the result). The
+order of the results in `CMD_PROP_VALUES_ARE` match the order of
+properties given in `CMD_PROP_VALUE_MULTI_SET`.
+
+Since the processing of properties to set stops at the first error,
+the resulting `CMD_PROP_VALUES_ARE` can contain fewer items than the
+requested number of properties to set.
+
+Not all properties can be set using this method. As a general rule
+of thumb, any property that blocks when setting will fail for that
+individual property with `STATUS_INVALID_COMMAND_FOR_PROP`.
+
+## CMD 23: (NCP->Host) CMD_PROP_VALUES_ARE {#cmd-prop-values-are}
+
+*   Argument-Encoding: `A(iD)`
+*   Required Capability: `CAP_CMD_MULTI`
+
+Octets: |    1   |          1          |   *n*
+--------|--------|---------------------|----------------------
+Fields: | HEADER | CMD_PROP_VALUES_ARE | Property/Value Pairs
+
+With each property/value pair being:
+
+Octets: |    2   |   1-3   |  *n*
+--------|--------|---------|------------
+Fields: | LENGTH | PROP_ID | PROP_VALUE
+
+This command is emitted by the NCP as the response to both the
+`CMD_PROP_VALUE_MULTI_GET` and `CMD_PROP_VALUE_MULTI_SET` commands. It
+is roughly analogous to `CMD_PROP_VALUE_IS`, except that it contains
+more than one property.
+
+This command SHOULD NOT be emitted asynchronously, or in response to
+any command other than `CMD_PROP_VALUE_MULTI_GET` or
+`CMD_PROP_VALUE_MULTI_SET`.
+
+The arguments are a list of structures containing the emitted property
+and the associated value. These are presented in the same order as
+given in the associated initiating command. In cases where getting or
+setting a specific property resulted in an error, the associated slot
+in this command will describe `PROP_LAST_STATUS`.
diff --git a/doc/spinel-protocol-src/spinel-data-packing.md b/doc/spinel-protocol-src/spinel-data-packing.md
new file mode 100644
index 0000000..a222e73
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-data-packing.md
@@ -0,0 +1,167 @@
+# Data Packing
+
+Data serialization for properties is performed using a light-weight
+data packing format which was loosely inspired by D-Bus. The format of
+a serialization is defined by a specially formatted string.
+
+This packing format is used for notational convenience. While this
+string-based datatype format has been designed so that the strings may
+be directly used by a structured data parser, such a thing is not
+required to implement Spinel. Indeed, higly constrained applications
+may find such a thing to be too heavyweight.
+
+Goals:
+
+ *  Be lightweight and favor direct representation of values.
+ *  Use an easily readable and memorable format string.
+ *  Support lists and structures.
+ *  Allow properties to be appended to structures while maintaining
+    backward compatibility.
+
+Each primitive datatype has an ASCII character associated with it.
+Structures can be represented as strings of these characters. For
+example:
+
+ *  `C`: A single unsigned byte.
+ *  `C6U`: A single unsigned byte, followed by a 128-bit IPv6
+    address, followed by a zero-terminated UTF8 string.
+ *  `A(6)`: An array of concatenated IPv6 addresses
+
+In each case, the data is represented exactly as described. For
+example, an array of 10 IPv6 address is stored as 160 bytes.
+
+## Primitive Types
+
+Char | Name                | Description
+-----|:--------------------|:------------------------------
+ `.` | DATATYPE_VOID        | Empty data type. Used internally.
+ `b` | DATATYPE_BOOL        | Boolean value. Encoded in 8-bits as either 0x00 or 0x01. All other values are illegal.
+ `C` | DATATYPE_UINT8       | Unsigned 8-bit integer.
+ `c` | DATATYPE_INT8        | Signed 8-bit integer.
+ `S` | DATATYPE_UINT16      | Unsigned 16-bit integer.
+ `s` | DATATYPE_INT16       | Signed 16-bit integer.
+ `L` | DATATYPE_UINT32      | Unsigned 32-bit integer.
+ `l` | DATATYPE_INT32       | Signed 32-bit integer.
+ `i` | DATATYPE_UINT_PACKED | Packed Unsigned Integer. See (#packed-unsigned-integer).
+ `6` | DATATYPE_IPv6ADDR    | IPv6 Address. (Big-endian)
+ `E` | DATATYPE_EUI64       | EUI-64 Address. (Big-endian)
+ `e` | DATATYPE_EUI48       | EUI-48 Address. (Big-endian)
+ `D` | DATATYPE_DATA        | Arbitrary data. See (#data-blobs).
+ `d` | DATATYPE_DATA_WLEN   | Arbitrary data with prepended length. See (#data-blobs).
+ `U` | DATATYPE_UTF8        | Zero-terminated UTF8-encoded string.
+ `t(...)` | DATATYPE_STRUCT | Structured datatype with prepended length. See (#structured-data).
+ `A(...)` | DATATYPE_ARRAY  | Array of datatypes. Compound type. See (#arrays).
+
+All multi-byte values are little-endian unless explicitly stated
+otherwise.
+
+## Packed Unsigned Integer
+
+For certain types of integers, such command or property identifiers,
+usually have a value on the wire that is less than 127. However, in
+order to not preclude the use of values larger than 255, we would need
+to add an extra byte. Doing this would add an extra byte to the
+majority of instances, which can add up in terms of bandwidth.
+
+The packed unsigned integer format is based on the [unsigned integer
+format in EXI][EXI], except that we limit the maximum value to the
+largest value that can be encoded into three bytes(2,097,151).
+
+[EXI]: https://www.w3.org/TR/exi/#encodingUnsignedInteger
+
+For all values less than 127, the packed form of the number is simply
+a single byte which directly represents the number. For values larger
+than 127, the following process is used to encode the value:
+
+1.  The unsigned integer is broken up into *n* 7-bit chunks and placed
+    into *n* octets, leaving the most significant bit of each octet
+    unused.
+2.  Order the octets from least-significant to most-significant.
+    (Little-endian)
+3.  Clear the most significant bit of the most significant octet. Set
+    the least significant bit on all other octets.
+
+Where *n* is the smallest number of 7-bit chunks you can use to
+represent the given value.
+
+Take the value 1337, for example:
+
+    1337 => 0x0539
+         => [39 0A]
+         => [B9 0A]
+
+To decode the value, you collect the 7-bit chunks until you find an
+octet with the most significant bit clear.
+
+## Data Blobs
+
+There are two types for data blobs: `d` and `D`.
+
+*   `d` has the length of the data (in bytes) prepended to the data
+    (with the length encoded as type `S`). The size of the length
+    field is not included in the length.
+*   `D` does not have a prepended length: the length of the data is
+    implied by the bytes remaining to be parsed. It is an error for
+    `D` to not be the last type in a type in a type signature.
+
+This dichotomy allows for more efficient encoding by eliminating
+redundency. If the rest of the buffer is a data blob, encoding the
+length would be redundant because we already know how many bytes are
+in the rest of the buffer.
+
+In some cases we use `d` even if it is the last field in a type signature.
+We do this to allow for us to be able to append additional fields
+to the type signature if necessary in the future. This is usually the
+case with embedded structs, like in the scan results.
+
+For example, let's say we have a buffer that is encoded with the
+datatype signature of `CLLD`. In this case, it is pretty easy to tell
+where the start and end of the data blob is: the start is 9 bytes from
+the start of the buffer, and its length is the length of the buffer
+minus 9. (9 is the number of bytes taken up by a byte and two longs)
+
+The datatype signature `CLLDU` is illegal because we can't determine
+where the last field (a zero-terminated UTF8 string) starts. But the
+datatype `CLLdU` *is* legal, because the parser can determine the
+exact length of the data blob-- allowing it to know where the start
+of the next field would be.
+
+## Structured Data
+
+The structure data type (`t(...)`) is a way of bundling together
+several fields into a single structure. It can be thought of as a
+`d` type except that instead of being opaque, the fields in the
+content are known. This is useful for things like scan results where
+you have substructures which are defined by different layers.
+
+For example, consider the type signature `Lt(ES)t(6C)`. In this
+hypothetical case, the first struct is defined by the MAC layer, and
+the second struct is defined by the PHY layer. Because of the use of
+structures, we know exactly what part comes from that layer.
+Additionally, we can add fields to each structure without introducing
+backward compatability problems: Data encoded as `Lt(ESU)t(6C)` (Notice
+the extra `U`) will
+decode just fine as `Lt(ES)t(6C)`. Additionally, if we don't care
+about the MAC layer and only care about the network layer, we could
+parse as `Lt()t(6C)`.
+
+Note that data encoded as `Lt(ES)t(6C)` will also parse as `Ldd`,
+with the structures from both layers now being opaque data blobs.
+
+## Arrays
+
+An array is simply a concatenated set of *n* data encodings. For example,
+the type `A(6)` is simply a list of IPv6 addresses---one after the other.
+The type `A(6E)` likewise a concatenation of IPv6-address/EUI-64 pairs.
+
+If an array contains many fields, the fields will often be surrounded
+by a structure (`t(...)`). This effectively prepends each item in the
+array with its length. This is useful for improving parsing performance
+or to allow additional fields to be added in the future in a backward
+compatible way. If there is a high certainty that additional
+fields will never be added, the struct may be omitted (saving two bytes
+per item).
+
+This specification does not define a way to embed an array as a field
+alongside other fields.
+
diff --git a/doc/spinel-protocol-src/spinel-example-sessions.md b/doc/spinel-protocol-src/spinel-example-sessions.md
new file mode 100644
index 0000000..4141c67
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-example-sessions.md
@@ -0,0 +1,210 @@
+# Example Sessions
+
+## NCP Initialization
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+Check the protocol version to see if it is supported:
+
+* CMD_VALUE_GET:PROP_PROTOCOL_VERSION
+* CMD_VALUE_IS:PROP_PROTOCOL_VERSION
+
+Check the NCP version to see if a firmware update may be necessary:
+
+* CMD_VALUE_GET:PROP_NCP_VERSION
+* CMD_VALUE_IS:PROP_NCP_VERSION
+
+Check interface type to make sure that it is what we expect:
+
+* CMD_VALUE_GET:PROP_INTERFACE_TYPE
+* CMD_VALUE_IS:PROP_INTERFACE_TYPE
+
+If the host supports using vendor-specific commands, the vendor should
+be verified before using them:
+
+* CMD_VALUE_GET:PROP_VENDOR_ID
+* CMD_VALUE_IS:PROP_VENDOR_ID
+
+Fetch the capability list so that we know what features this NCP
+supports:
+
+* CMD_VALUE_GET:PROP_CAPS
+* CMD_VALUE_IS:PROP_CAPS
+
+If the NCP supports CAP_NET_SAVE, then we go ahead and recall the network:
+
+* CMD_NET_RECALL
+
+## Attaching to a network
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+We make the assumption that the NCP is not currently associated
+with a network.
+
+Set the network properties, if they were not already set:
+
+* CMD_VALUE_SET:PROP_PHY_CHAN
+* CMD_VALUE_IS:PROP_PHY_CHAN
+
+* CMD_VALUE_SET:PROP_NET_XPANID
+* CMD_VALUE_IS:PROP_NET_XPANID
+
+* CMD_VALUE_SET:PROP_MAC_15_4_PANID
+* CMD_VALUE_IS:PROP_MAC_15_4_PANID
+
+* CMD_VALUE_SET:PROP_NET_NETWORK_NAME
+* CMD_VALUE_IS:PROP_NET_NETWORK_NAME
+
+* CMD_VALUE_SET:PROP_NET_MASTER_KEY
+* CMD_VALUE_IS:PROP_NET_MASTER_KEY
+
+* CMD_VALUE_SET:PROP_NET_KEY_SEQUENCE_COUNTER
+* CMD_VALUE_IS:PROP_NET_KEY_SEQUENCE_COUNTER
+
+* CMD_VALUE_SET:PROP_NET_KEY_SWITCH_GUARDTIME
+* CMD_VALUE_IS:PROP_NET_KEY_SWITCH_GUARDTIME
+
+Bring the network interface up:
+
+* CMD_VALUE_SET:PROP_NET_IF_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_IF_UP:TRUE
+
+Bring the routing stack up:
+
+* CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+Some asynchronous events from the NCP:
+
+* CMD_VALUE_IS:PROP_NET_ROLE
+* CMD_VALUE_IS:PROP_NET_PARTITION_ID
+* CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+## Successfully joining a pre-existing network
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+This example session is identical to the above session up to the point
+where we set PROP_NET_IF_UP to true. From there, the behavior changes.
+
+* CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+* CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+
+Bring the routing stack up:
+
+* CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+Some asynchronous events from the NCP:
+
+* CMD_VALUE_IS:PROP_NET_ROLE
+* CMD_VALUE_IS:PROP_NET_PARTITION_ID
+* CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+Now let's save the network settings to NVRAM:
+
+* CMD_NET_SAVE
+
+## Unsuccessfully joining a pre-existing network
+
+This example session is identical to the above session up to the point
+where we set PROP_NET_IF_UP to true. From there, the behavior changes.
+
+* CMD_VALUE_SET:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+* CMD_VALUE_IS:PROP_NET_REQUIRE_JOIN_EXISTING:TRUE
+
+Bring the routing stack up:
+
+* CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+Some asynchronous events from the NCP:
+
+* CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_JOIN_NO_PEERS
+* CMD_VALUE_IS:PROP_NET_STACK_UP:FALSE
+
+## Detaching from a network
+
+TBD
+
+## Attaching to a saved network
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+Recall the saved network if you haven't already done so:
+
+* CMD_NET_RECALL
+
+Bring the network interface up:
+
+* CMD_VALUE_SET:PROP_NET_IF_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_IF_UP:TRUE
+
+Bring the routing stack up:
+
+* CMD_VALUE_SET:PROP_NET_STACK_UP:TRUE
+* CMD_VALUE_IS:PROP_NET_STACK_UP:TRUE
+
+Some asynchronous events from the NCP:
+
+* CMD_VALUE_IS:PROP_NET_ROLE
+* CMD_VALUE_IS:PROP_NET_PARTITION_ID
+* CMD_VALUE_IS:PROP_THREAD_ON_MESH_NETS
+
+## NCP Software Reset
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+* CMD_RESET
+* CMD_VALUE_IS:PROP_LAST_STATUS:STATUS_RESET_SOFTWARE
+
+Then jump to (#ncp-initialization).
+
+## Adding an on-mesh prefix
+
+TBD
+
+## Entering low-power modes
+
+TBD
+
+## Sniffing raw packets
+
+<!-- RQ -- FIXME: This example session is incomplete. -->
+
+This assumes that the NCP has been initialized.
+
+Optionally set the channel:
+
+* CMD_VALUE_SET:PROP_PHY_CHAN:x
+* CMD_VALUE_IS:PROP_PHY_CHAN
+
+Set the filter mode:
+
+* CMD_VALUE_SET:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONITOR
+* CMD_VALUE_IS:PROP_MAC_PROMISCUOUS_MODE:MAC_PROMISCUOUS_MODE_MONITOR
+
+Enable the raw stream:
+
+* CMD_VALUE_SET:PROP_MAC_RAW_STREAM_ENABLED:TRUE
+* CMD_VALUE_IS:PROP_MAC_RAW_STREAM_ENABLED:TRUE
+
+Enable the PHY directly:
+
+* CMD_VALUE_SET:PROP_PHY_ENABLED:TRUE
+* CMD_VALUE_IS:PROP_PHY_ENABLED:TRUE
+
+Now we will get raw 802.15.4 packets asynchronously on
+PROP_STREAM_RAW:
+
+* CMD_VALUE_IS:PROP_STREAM_RAW:...
+* CMD_VALUE_IS:PROP_STREAM_RAW:...
+* CMD_VALUE_IS:PROP_STREAM_RAW:...
+
+This mode may be entered even when associated with a network.
+In that case, you should set `PROP_MAC_PROMISCUOUS_MODE` to
+`MAC_PROMISCUOUS_MODE_PROMISCUOUS` or `MAC_PROMISCUOUS_MODE_NORMAL`, so that
+you can avoid receiving packets from other networks or that are destined
+for other nodes.
+
diff --git a/doc/spinel-protocol-src/spinel-feature-gpio.md b/doc/spinel-protocol-src/spinel-feature-gpio.md
new file mode 100644
index 0000000..f43f578
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-feature-gpio.md
@@ -0,0 +1,114 @@
+# Feature: GPIO Access {#feature-gpio-access}
+
+This feature allows the host to have control over some or all of the
+GPIO pins on the NCP. The host can determine which GPIOs are available
+by examining `PROP_GPIO_CONFIG`, described below. This API supports a
+maximum of 256 individual GPIO pins.
+
+Support for this feature can be determined by the presence of `CAP_GPIO`.
+
+## Properties ##
+
+### PROP 4096: PROP\_GPIO\_CONFIG ###
+
+*   Argument-Encoding: `A(t(CCU))`
+*   Type: Read-write (Writable only using `CMD_PROP_VALUE_INSERT`,
+    (#cmd-prop-value-insert))
+
+An array of structures which contain the following fields:
+
+*   `C`: GPIO Number
+*   `C`: GPIO Configuration Flags
+*   `U`: Human-readable GPIO name
+
+GPIOs which do not have a corresponding entry are not supported.
+
+The configuration parameter contains the configuration flags for the
+GPIO:
+
+      0   1   2   3   4   5   6   7
+    +---+---+---+---+---+---+---+---+
+    |DIR|PUP|PDN|TRIGGER|  RESERVED |
+    +---+---+---+---+---+---+---+---+
+            |O/D|
+            +---+
+
+*   `DIR`: Pin direction. Clear (0) for input, set (1) for output.
+*   `PUP`: Pull-up enabled flag.
+*   `PDN`/`O/D`: Flag meaning depends on pin direction:
+    *   Input: Pull-down enabled.
+    *   Output: Output is an open-drain.
+*   `TRIGGER`: Enumeration describing how pin changes generate
+    asynchronous notification commands (TBD) from the NCP to the host.
+    *   0: Feature disabled for this pin
+    *   1: Trigger on falling edge
+    *   2: Trigger on rising edge
+    *   3: Trigger on level change
+*   `RESERVED`: Bits reserved for future use. Always cleared to zero
+    and ignored when read.
+
+As an optional feature, the configuration of individual pins may be
+modified using the `CMD_PROP_VALUE_INSERT` command. Only the GPIO
+number and flags fields MUST be present, the GPIO name (if present)
+would be ignored. This command can only be used to modify the
+configuration of GPIOs which are already exposed---it cannot be used
+by the host to add addional GPIOs.
+
+### PROP 4098: PROP\_GPIO\_STATE ###
+
+*   Type: Read-Write
+
+Contains a bit field identifying the state of the GPIOs. The length of
+the data associated with these properties depends on the number of
+GPIOs. If you have 10 GPIOs, you'd have two bytes. GPIOs are numbered
+from most significant bit to least significant bit, so 0x80 is GPIO 0,
+0x40 is GPIO 1, etc.
+
+For GPIOs configured as inputs:
+
+*   `CMD_PROP_VAUE_GET`: The value of the associated bit describes the
+    logic level read from the pin.
+*   `CMD_PROP_VALUE_SET`: The value of the associated bit is ignored
+    for these pins.
+
+For GPIOs configured as outputs:
+
+*   `CMD_PROP_VAUE_GET`: The value of the associated bit is
+    implementation specific.
+*   `CMD_PROP_VALUE_SET`: The value of the associated bit determines
+    the new logic level of the output. If this pin is configured as an
+    open-drain, setting the associated bit to 1 will cause the pin to
+    enter a Hi-Z state.
+
+For GPIOs which are not specified in `PROP_GPIO_CONFIG`:
+
+*   `CMD_PROP_VAUE_GET`: The value of the associated bit is
+    implementation specific.
+*   `CMD_PROP_VALUE_SET`: The value of the associated bit MUST be
+    ignored by the NCP.
+
+When writing, unspecified bits are assumed to be zero.
+
+### PROP 4099: PROP\_GPIO\_STATE\_SET ###
+
+*   Type: Write-only
+
+Allows for the state of various output GPIOs to be set without
+affecting other GPIO states. Contains a bit field identifying the
+output GPIOs that should have their state set to 1.
+
+When writing, unspecified bits are assumed to be zero. The value of
+any bits for GPIOs which are not specified in `PROP_GPIO_CONFIG` MUST
+be ignored.
+
+### PROP 4100: PROP\_GPIO\_STATE\_CLEAR ###
+
+*   Type: Write-only
+
+Allows for the state of various output GPIOs to be cleared without
+affecting other GPIO states. Contains a bit field identifying the
+output GPIOs that should have their state cleared to 0.
+
+When writing, unspecified bits are assumed to be zero. The value of
+any bits for GPIOs which are not specified in `PROP_GPIO_CONFIG` MUST
+be ignored.
diff --git a/doc/spinel-protocol-src/spinel-feature-host-buffer-offload.md b/doc/spinel-protocol-src/spinel-feature-host-buffer-offload.md
new file mode 100644
index 0000000..9e5dfeb
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-feature-host-buffer-offload.md
@@ -0,0 +1,95 @@
+# Feature: Host Buffer Offload
+
+The memory on an NCP may be much more limited than the memory on
+the host processor. In such situations, it is sometimes useful
+for the NCP to offload buffers to the host processor temporarily
+so that it can perform other operations.
+
+Host buffer offload is an optional NCP capability that, when
+present, allows the NCP to store data buffers on the host processor
+that can be recalled at a later time.
+
+The presence of this feature can be detected by the host by
+checking for the presence of the `CAP_HBO`
+capability in `PROP_CAPS`.
+
+## Commands
+
+### CMD 12: (NCP->Host) CMD_HBO_OFFLOAD
+
+* Argument-Encoding: `LscD`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Expiration`: In seconds-from-now
+    * `Priority`: Critical, High, Medium, Low
+    * `Data`: Data to offload
+
+### CMD 13: (NCP->Host) CMD_HBO_RECLAIM
+ *  Argument-Encoding: `Lb`
+     *  `OffloadId`: 32-bit unique block identifier
+     *  `KeepAfterReclaim`: If not set to true, the block will be
+        dropped by the host after it is sent to the NCP.
+
+### CMD 14: (NCP->Host) CMD_HBO_DROP
+
+* Argument-Encoding: `L`
+    * `OffloadId`: 32-bit unique block identifier
+
+### CMD 15: (Host->NCP) CMD_HBO_OFFLOADED
+
+* Argument-Encoding: `Li`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+
+### CMD 16: (Host->NCP) CMD_HBO_RECLAIMED
+
+* Argument-Encoding: `LiD`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+    * `Data`: Data that was previously offloaded (if any)
+
+### CMD 17: (Host->NCP) CMD_HBO_DROPPED
+
+* Argument-Encoding: `Li`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+
+## Properties
+
+### PROP 10: PROP_HBO_MEM_MAX {#prop-hbo-mem-max}
+
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+Octets: |       4
+--------|-----------------
+Fields: | `PROP_HBO_MEM_MAX`
+
+Describes the number of bytes that may be offloaded from the NCP to
+the host. Default value is zero, so this property must be set by the
+host to a non-zero value before the NCP will begin offloading blocks.
+
+This value is encoded as an unsigned 32-bit integer.
+
+This property is only available if the `CAP_HBO`
+capability is present in `PROP_CAPS`.
+
+### PROP 11: PROP_HBO_BLOCK_MAX  {#prop-hbo-block-max}
+
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+Octets: |       2
+--------|-----------------
+Fields: | `PROP_HBO_BLOCK_MAX`
+
+Describes the number of blocks that may be offloaded from the NCP to
+the host. Default value is 32. Setting this value to zero will cause
+host block offload to be effectively disabled.
+
+This value is encoded as an unsigned 16-bit integer.
+
+This property is only available if the `CAP_HBO`
+capability is present in `PROP_CAPS`.
+
+
+
diff --git a/doc/spinel-protocol-src/spinel-feature-jam-detect.md b/doc/spinel-protocol-src/spinel-feature-jam-detect.md
new file mode 100644
index 0000000..c56a65e
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-feature-jam-detect.md
@@ -0,0 +1,100 @@
+# Feature: Jam Detection {#feature-jam-detect}
+
+Jamming detection is a feature that allows the NCP to report when it
+detects high levels of interference that are characteristic of intentional
+signal jamming.
+
+The presence of this feature can be detected by checking for the
+presence of the `CAP_JAM_DETECT` (value 6) capability in `PROP_CAPS`.
+
+## Properties
+
+### PROP 4608: PROP_JAM_DETECT_ENABLE {#prop-jam-detect-enable}
+
+* Type: Read-Write
+* Packed-Encoding: `b`
+* Default Value: false
+* REQUIRED for `CAP_JAM_DETECT`
+
+Octets: |       1
+--------|-----------------
+Fields: | `PROP_JAM_DETECT_ENABLE`
+
+Indicates if jamming detection is enabled or disabled. Set to true
+to enable jamming detection.
+
+This property is only available if the `CAP_JAM_DETECT`
+capability is present in `PROP_CAPS`.
+
+### PROP 4609: PROP_JAM_DETECTED {#prop-jam-detected}
+
+* Type: Read-Only
+* Packed-Encoding: `b`
+* REQUIRED for `CAP_JAM_DETECT`
+
+Octets: |       1
+--------|-----------------
+Fields: | `PROP_JAM_DETECTED`
+
+Set to true if radio jamming is detected. Set to false otherwise.
+
+When jamming detection is enabled, changes to the value of this
+property are emitted asynchronously via `CMD_PROP_VALUE_IS`.
+
+This property is only available if the `CAP_JAM_DETECT`
+capability is present in `PROP_CAPS`.
+
+### PROP 4610: PROP_JAM_DETECT_RSSI_THRESHOLD
+
+* Type: Read-Write
+* Packed-Encoding: `c`
+* Units: dBm
+* Default Value: Implementation-specific
+* RECOMMENDED for `CAP_JAM_DETECT`
+
+This parameter describes the threshold RSSI level (measured in
+dBm) above which the jamming detection will consider the
+channel blocked.
+
+### PROP 4611: PROP_JAM_DETECT_WINDOW
+
+* Type: Read-Write
+* Packed-Encoding: `c`
+* Units: Seconds (1-64)
+* Default Value: Implementation-specific
+* RECOMMENDED for `CAP_JAM_DETECT`
+
+This parameter describes the window period for signal jamming
+detection.
+
+### PROP 4612: PROP_JAM_DETECT_BUSY
+
+* Type: Read-Write
+* Packed-Encoding: `i`
+* Units: Seconds (1-64)
+* Default Value: Implementation-specific
+* RECOMMENDED for `CAP_JAM_DETECT`
+
+This parameter describes the number of aggregate seconds within
+the detection window where the RSSI must be above
+`PROP_JAM_DETECT_RSSI_THRESHOLD` to trigger detection.
+
+The behavior of the jamming detection feature when `PROP_JAM_DETECT_BUSY`
+is larger than `PROP_JAM_DETECT_WINDOW` is undefined.
+
+### PROP 4613: PROP_JAM_DETECT_HISTORY_BITMAP
+
+* Type: Read-Only
+* Packed-Encoding: `LL`
+* Default Value: Implementation-specific
+* RECOMMENDED for `CAP_JAM_DETECT`
+
+This value provides information about current state of jamming detection
+module for monitoring/debugging purpose. It returns a 64-bit value where
+each bit corresponds to one second interval starting with bit 0 for the
+most recent interval and bit 63 for the oldest intervals (63 sec earlier).
+The bit is set to 1 if the jamming detection module observed/detected
+high signal level during the corresponding one second interval.
+The value is read-only and is encoded as two `L` (uint32) values in
+little-endian format (first `L` (uint32) value gives the lower bits
+corresponding to more recent history).
diff --git a/doc/spinel-protocol-src/spinel-feature-network-save.md b/doc/spinel-protocol-src/spinel-feature-network-save.md
new file mode 100644
index 0000000..5655aac
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-feature-network-save.md
@@ -0,0 +1,74 @@
+# Feature: Network Save
+
+The network save/recall feature is an optional NCP capability that, when
+present, allows the host to save and recall network credentials and
+state to and from nonvolatile storage.
+
+The presence of the save/recall feature can be detected by checking for
+the presence of the `CAP_NET_SAVE` capability in `PROP_CAPS`.
+
+Network clear feature allows host to erase all network credentials and
+state from non-volatile memory.
+
+## Commands
+
+### CMD 9: (Host->NCP) CMD_NET_SAVE
+
+Octets: |    1   |      1
+--------|--------|--------------
+Fields: | HEADER | CMD_NET_SAVE
+
+Save network state command. Saves any current network credentials and
+state necessary to reconnect to the current network to non-volatile
+memory.
+
+This operation affects non-volatile memory only. The current network
+information stored in volatile memory is unaffected.
+
+The response to this command is always a `CMD_PROP_VALUE_IS` for
+`PROP_LAST_STATUS`, indicating the result of the operation.
+
+This command is only available if the `CAP_NET_SAVE` capability is
+set.
+
+### CMD 10: (Host->NCP) CMD_NET_CLEAR
+
+Octets: |    1   |      1
+--------|--------|---------------
+Fields: | HEADER | CMD_NET_CLEAR
+
+Clear saved network settings command. Erases all network credentials
+and state from non-volatile memory. The erased settings include any data
+saved automatically by the network stack firmware and/or data saved by
+`CMD_NET_SAVE` operation.
+
+This operation affects non-volatile memory only. The current network
+information stored in volatile memory is unaffected.
+
+The response to this command is always a `CMD_PROP_VALUE_IS` for
+`PROP_LAST_STATUS`, indicating the result of the operation.
+
+This command is always available independent of the value of
+`CAP_NET_SAVE` capability.
+
+
+### CMD 11: (Host->NCP) CMD_NET_RECALL
+
+Octets: |    1   |      1
+--------|--------|----------------
+Fields: | HEADER | CMD_NET_RECALL
+
+Recall saved network state command. Recalls any previously saved
+network credentials and state previously stored by `CMD_NET_SAVE` from
+non-volatile memory.
+
+This command will typically generated several unsolicited property
+updates as the network state is loaded. At the conclusion of loading,
+the authoritative response to this command is always a
+`CMD_PROP_VALUE_IS` for `PROP_LAST_STATUS`, indicating the result of
+the operation.
+
+This command is only available if the `CAP_NET_SAVE` capability is
+set.
+
+
diff --git a/doc/spinel-protocol-src/spinel-feature-trng.md b/doc/spinel-protocol-src/spinel-feature-trng.md
new file mode 100644
index 0000000..c310b43
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-feature-trng.md
@@ -0,0 +1,77 @@
+# Feature: True Random Number Generation {#feature-trng}
+
+This feature allows the host to have access to any strong hardware
+random number generator that might be present on the NCP, for things
+like key generation or seeding PRNGs.
+
+Support for this feature can be determined by the presence of `CAP_TRNG`.
+
+Note well that implementing a cryptographically-strong software-based true
+random number generator (that is impervious to things like temperature
+changes, manufacturing differences across devices, or unexpected output
+correlations) is non-trivial without a well-designed, dedicated hardware
+random number generator. Implementors who have little or no experience in
+this area are encouraged to not advertise this capability.
+
+## Properties ##
+
+### PROP 4101: PROP_TRNG_32 ###
+
+*   Argument-Encoding: `L`
+*   Type: Read-Only
+
+Fetching this property returns a strong random 32-bit integer that is suitable
+for use as a PRNG seed or for cryptographic use.
+
+While the exact mechanism behind the calculation of this value is
+implementation-specific, the implementation must satisfy the following
+requirements:
+
+* Data representing at least 32 bits of fresh entropy (extracted from the
+  primary entropy source) MUST be consumed by the calculation of each query.
+* Each of the 32 bits returned MUST be free of bias and have no statistical
+  correlation to any part of the raw data used for the calculation of any
+  query.
+
+Support for this property is REQUIRED if `CAP_TRNG` is included in the
+device capabilities.
+
+### PROP 4102: PROP_TRNG_128 ###
+
+*   Argument-Encoding: `D`
+*   Type: Read-Only
+
+Fetching this property returns 16 bytes of strong random data suitable for
+direct cryptographic use without further processing(For example, as an
+AES key).
+
+While the exact mechanism behind the calculation of this value is
+implementation-specific, the implementation must satisfy the following
+requirements:
+
+* Data representing at least 128 bits of fresh entropy (extracted from the
+  primary entropy source) MUST be consumed by the calculation of each query.
+* Each of the 128 bits returned MUST be free of bias and have no statistical
+  correlation to any part of the raw data used for the calculation of any
+  query.
+
+Support for this property is REQUIRED if `CAP_TRNG` is included in the
+device capabilities.
+
+### PROP 4103: PROP_TRNG_RAW_32 ###
+
+*   Argument-Encoding: `D`
+*   Type: Read-Only
+
+This property is primarily used to diagnose and debug the behavior
+of the entropy source used for strong random number generation.
+
+When queried, returns the raw output from the entropy source used to
+generate `PROP_TRNG_32`, prior to any reduction/whitening and/or mixing
+with prior state.
+
+The length of the returned buffer is implementation specific and should be
+expected to be non-deterministic.
+
+Support for this property is RECOMMENDED if `CAP_TRNG` is included in the
+device capabilities.
diff --git a/doc/spinel-protocol-src/spinel-frame-format.md b/doc/spinel-protocol-src/spinel-frame-format.md
new file mode 100644
index 0000000..1cf1a95
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-frame-format.md
@@ -0,0 +1,83 @@
+# Frame Format ##
+
+A frame is defined simply as the concatenation of
+
+ *  A header byte
+ *  A command (up to three bytes, see (#packed-unsigned-integer) for format)
+ *  An optional command payload
+
+Octets: |    1   | 1-3 |    *n*
+--------|--------|-----|-------------
+Fields: | HEADER | CMD | CMD_PAYLOAD
+
+
+## Header Format ###
+
+The header byte is broken down as follows:
+
+      0   1   2   3   4   5   6   7
+    +---+---+---+---+---+---+---+---+
+    |  FLG  |  NLI  |      TID      |
+    +---+---+---+---+---+---+---+---+
+
+<!-- RQ -- Eventually, when https://github.com/miekg/mmark/issues/95
+is addressed, the above table should be swapped out with this:
+
+| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+|---|---|---|---|---|---|---|---|
+|  FLG ||  NLI ||      TID   ||||
+-->
+
+### FLG: Flag
+
+The flag field of the header byte (`FLG`) is always set to the value
+two (or `10` in binary). Any frame received with these bits set to
+any other value else MUST NOT be considered a Spinel frame.
+
+This convention allows Spinel to be line compatible with BTLE HCI. By
+defining the first two bit in this way we can disambiguate between
+Spinel frames and HCI frames (which always start with either `0x01`
+or `0x04`) without any additional framing overhead.
+
+### NLI: Network Link Identifier
+
+The Network Link Identifier (NLI) is a number between 0 and 3, which is associated by the OS with one of up to four IPv6 zone indices corresponding to conceptual IPv6 interfaces on the NCP. This allows the protocol to support IPv6 nodes connecting simultaneously to more than one IPv6 network link using a single NCP instance. The first Network Link Identifier (0) MUST refer to a distinguished conceptual interface provided by the NCP for its IPv6 link type. The other three Network Link Identifiers (1, 2 and 3) MAY be dissociated from any conceptual interface.
+
+### TID: Transaction Identifier
+
+The least significant bits of the header represent the Transaction
+Identifier(TID). The TID is used for correlating responses to the
+commands which generated them.
+
+When a command is sent from the host, any reply to that command sent
+by the NCP will use the same value for the TID. When the host receives
+a frame that matches the TID of the command it sent, it can easily
+recognize that frame as the actual response to that command.
+
+The TID value of zero (0) is used for commands to which a correlated
+response is not expected or needed, such as for unsolicited update
+commands sent to the host from the NCP.
+
+### Command Identifier (CMD) ####
+
+The command identifier is a 21-bit unsigned integer encoded in up to
+three bytes using the packed unsigned integer format described in
+(#packed-unsigned-integer). This encoding allows for up to 2,097,152 individual
+commands, with the first 127 commands represented as a single byte.
+Command identifiers larger than 2,097,151 are explicitly forbidden.
+
+CID Range             | Description
+----------------------|------------------
+0 - 63                | Reserved for core commands
+64 - 15,359           | *UNALLOCATED*
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | *UNALLOCATED*
+2,000,000 - 2,097,151 | Experimental use only
+
+### Command Payload (Optional) ####
+
+Depending on the semantics of the command in question, a payload MAY
+be included in the frame. The exact composition and length of the
+payload is defined by the command identifier.
+
+
diff --git a/doc/spinel-protocol-src/spinel-framing.md b/doc/spinel-protocol-src/spinel-framing.md
new file mode 100644
index 0000000..fa3593d
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-framing.md
@@ -0,0 +1,223 @@
+
+# Framing Protocol
+
+Since this NCP protocol is defined independently of the physical
+transport or framing, any number of transports and framing protocols
+could be used successfully. However, in the interests of compatibility,
+this document provides some recommendations.
+
+## UART Recommendations ###
+
+The recommended default UART settings are:
+
+* Bit rate:     115200
+* Start bits:   1
+* Data bits:    8
+* Stop bits:    1
+* Parity:       None
+* Flow Control: Hardware
+
+These values may be adjusted depending on the individual needs of
+the application or product, but some sort of flow control **MUST** be used.
+Hardware flow control is preferred over software flow control. In the
+absence of hardware flow control, software flow control (XON/XOFF) **MUST**
+be used instead.
+
+We also **RECOMMEND** an Arduino-style hardware reset, where the DTR
+signal is coupled to the `RÌ…EÌ…SÌ…` pin through a 0.01µF capacitor. This
+causes the NCP to automatically reset whenever the serial port is
+opened. At the very least we **RECOMMEND** dedicating one of your host
+pins to controlling the `RÌ…EÌ…SÌ…` pin on the NCP, so that you can
+easily perform a hardware reset if necessary.
+
+### UART Bit Rate Detection ###
+
+When using a UART, the issue of an appropriate bit rate must be
+considered. A bitrate of 115200 bits per second has become a defacto
+standard baud rate for many serial peripherals. This rate, however,
+is slower than the theoretical maximum bitrate of the 802.15.4 2.4GHz
+PHY (250kbit). In most circumstances this mismatch is not significant
+because the overall bitrate will be much lower than either of these
+rates, but there are circumstances where a faster UART bitrate is
+desirable. Thus, this document proposes a simple bitrate detection
+scheme that can be employed by the host to detect when the attached
+NCP is initially running at a higher bitrate.
+
+The algorithm is to send successive NOOP commands to the NCP at increasing
+bitrates. When a valid `CMD_LAST_STATUS` response has been received, we
+have identified the correct bitrate.
+
+In order to limit the time spent hunting for the appropriate bitrate,
+we RECOMMEND that only the following bitrates be checked:
+
+* 115200
+* 230400
+* 1000000 (1Mbit)
+
+The bitrate MAY also be changed programmatically by adjusting
+`PROP_UART_BITRATE`, if implemented.
+
+### HDLC-Lite {#hdlc-lite}
+
+*HDLC-Lite* is the recommended framing protocol for transmitting
+Spinel frames over a UART. HDLC-Lite consists of only the framing,
+escaping, and CRC parts of the larger HDLC protocol---all other parts
+of HDLC are omitted. This protocol was chosen because it works well
+with software flow control and is widely implemented.
+
+To transmit a frame with HDLC-lite, the 16-bit CRC must first be
+appended to the frame. The CRC function is defined to be CRC-16/CCITT,
+otherwise known as the [KERMIT CRC][].
+
+[KERMIT CRC]: http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
+
+Individual frames are terminated with a frame delimiter octet called
+the 'flag' octet (`0x7E`).
+
+The following octets values are considered *special* and should be
+escaped when present in data frames:
+
+Octet Value | Description  
+------------|-----------------------  
+       0x7E | Frame Delimiter (Flag)  
+       0x7D | Escape Byte  
+       0x11 | XON  
+       0x13 | XOFF  
+       0xF8 | Vendor-Specific  
+
+When present in a data frame, these octet values are escaped by
+prepending the escape octet (`0x7D`) and XORing the value with `0x20`.
+
+When receiving a frame, the CRC must be verified after the frame is
+unescaped. If the CRC value does not match what is calculated for the
+frame data, the frame MUST be discarded. The implementation MAY
+indicate the failure to higher levels to handle as they see fit, but
+MUST NOT attempt to process the deceived frame.
+
+Consecutive flag octets are entirely legal and MUST NOT be treated as
+a framing error. Consecutive flag octets MAY be used as a way to wake
+up a sleeping NCP.
+
+When first establishing a connection to the NCP, it is customary to
+send one or more flag octets to ensure that any previously received
+data is discarded.
+
+## SPI Recommendations ###
+
+We RECOMMEND the use of the following standard SPI signals:
+
+*   `CÌ…SÌ…`:   (Host-to-NCP) Chip Select
+*   `CLK`:  (Host-to-NCP) Clock
+*   `MOSI`: Master-Output/Slave-Input
+*   `MISO`: Master-Input/Slave-Output
+*   `IÌ…NÌ…TÌ…`:  (NCP-to-Host) Host Interrupt
+*   `RÌ…EÌ…SÌ…`:  (Host-to-NCP) NCP Hardware Reset
+
+The `IÌ…NÌ…TÌ…` signal is used by the NCP to indicate to the host that
+the NCP has frames pending to send to it. When asserted, the host
+SHOULD initiate a SPI transaction in a timely manner.
+
+We RECOMMEND the following SPI properties:
+
+*   `CÌ…SÌ…` is active low.
+*   `CLK` is active high.
+*   `CLK` speed is larger than 500 kHz.
+*   Data is valid on leading edge of `CLK`.
+*   Data is sent in multiples of 8-bits (octets).
+*   Octets are sent most-significant bit first.
+
+This recommended configuration may be adjusted depending on the
+individual needs of the application or product.
+
+### SPI Framing Protocol ####
+
+Each SPI frame starts with a 5-byte frame header:
+
+Octets: |  1  |    2     |     2  
+--------|-----|----------|----------  
+Fields: | HDR | RECV_LEN | DATA_LEN  
+
+*   `HDR`: The first byte is the header byte (defined below)
+*   `RECV_LEN`: The second and third bytes indicate the largest frame
+    size that that device is ready to receive. If zero, then the other
+    device must not send any data. (Little endian)
+*   `DATA_LEN`: The fourth and fifth bytes indicate the size of the
+    pending data frame to be sent to the other device. If this value
+    is equal-to or less-than the number of bytes that the other device
+    is willing to receive, then the data of the frame is immediately
+    after the header. (Little Endian)
+
+The `HDR` byte is defined as:
+
+      0   1   2   3   4   5   6   7
+    +---+---+---+---+---+---+---+---+
+    |RST|CRC|CCF|  RESERVED |PATTERN|
+    +---+---+---+---+---+---+---+---+
+
+*   `RST`: This bit is set when that device has been reset since the
+    last time `CÌ…SÌ…` was asserted.
+*   `CRC`: This bit is set when that device supports writing a 16-bit
+    CRC at the end of the data. The CRC length is NOT included in DATA_LEN.
+*   `CCF`: "CRC Check Failure". Set if the CRC check on the last received
+    frame failed, cleared to zero otherwise. This bit is only used if both
+    sides support CRC.
+*   `RESERVED`: These bits are all reserved for future used. They
+    MUST be cleared to zero and MUST be ignored if set.
+*   `PATTERN`: These bits are set to a fixed value to help distinguish
+    valid SPI frames from garbage (by explicitly making `0xFF` and `0x00`
+    invalid values). Bit 6 MUST be set to be one and bit 7 MUST be
+    cleared (0). A frame received that has any other values for these bits
+    MUST be dropped.
+
+Prior to a sending or receiving a frame, the master MAY send a
+5-octet frame with zeros for both the max receive frame size and the
+the contained frame length. This will induce the slave device to
+indicate the length of the frame it wants to send (if any) and
+indicate the largest frame it is capable of receiving at the moment.
+This allows the master to calculate the size of the next transaction.
+Alternatively, if the master has a frame to send it can just go ahead
+and send a frame of that length and determine if the frame was accepted
+by checking that the `RECV_LEN` from the slave frame is larger than
+the frame the master just tried to send. If the `RECV_LEN` is smaller
+then the frame wasn't accepted and will need to be transmitted again.
+
+This protocol can be used either unidirectionally or bidirectionally,
+determined by the behavior of the master and the slave.
+
+If the the master notices `PATTERN` is not set correctly, the master
+should consider the transaction to have failed and try again after 10
+milliseconds, retrying up to 200 times. After unsuccessfully trying
+200 times in a row, the master MAY take appropriate remedial action
+(like a NCP hardware reset, or indicating a communication failure to a
+user interface).
+
+At the end of the data of a frame is an optional 16-bit CRC, support for
+which is indicated by the `CRC` bit of the `HDR` byte being set. If these
+bits are set for both the master and slave frames, then CRC checking is
+enabled on both sides, effectively requiring that frame sizes be two bytes
+longer than would be otherwise required. The CRC is calculated using the
+same mechanism used for the CRC calculation in HDLC-Lite (See (#hdlc-lite)).
+When both of the `CRC` bits are set, both sides must verify that the `CRC`
+is valid before accepting the frame. If not enough bytes were clocked out
+for the CRC to be read, then the frame must be ignored. If enough bytes
+were clocked out to perform a CRC check, but the CRC check fails, then
+the frame must be rejected and the `CRC_FAIL` bit on the next frame (and
+ONLY the next frame) MUST be set.
+
+## I²C Recommendations {#i2c-recommendations}
+
+TBD
+
+<!-- RQ
+  -- It may make sense to have a look at what Bluetooth HCI is doing
+     for native I²C framing and go with that.
+  -->
+
+## Native USB Recommendations ###
+
+TBD
+
+<!-- RQ
+  -- It may make sense to have a look at what Bluetooth HCI is doing
+     for native USB framing and go with that.
+  -->
diff --git a/doc/spinel-protocol-src/spinel-prop-core.md b/doc/spinel-protocol-src/spinel-prop-core.md
new file mode 100644
index 0000000..3138435
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-core.md
@@ -0,0 +1,544 @@
+## Core Properties {#prop-core}
+
+### PROP 0: PROP_LAST_STATUS {#prop-last-status}
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |    1-3
+-------:|-------------
+Fields: | LAST_STATUS
+
+Describes the status of the last operation. Encoded as a packed
+unsigned integer.
+
+This property is emitted often to indicate the result status of
+pretty much any Host-to-NCP operation.
+
+It is emitted automatically at NCP startup with a value indicating
+the reset reason.
+
+See (#status-codes) for the complete list of status codes.
+
+### PROP 1: PROP_PROTOCOL_VERSION {#prop-protocol-version}
+
+* Type: Read-Only
+* Encoding: `ii`
+
+Octets: |       1-3      |      1-3
+--------|----------------|---------------
+Fields: |  MAJOR_VERSION | MINOR_VERSION
+
+Describes the protocol version information. This property contains
+four fields, each encoded as a packed unsigned integer:
+
+ *  Major Version Number
+ *  Minor Version Number
+
+This document describes major version 4, minor version 3 of this protocol.
+
+The host **MUST** only use this property from NLI 0. Behavior when used
+from other NLIs is undefined.
+
+#### Major Version Number
+
+The major version number is used to identify large and incompatible
+differences between protocol versions.
+
+The host MUST enter a FAULT state if it does not explicitly support
+the given major version number.
+
+#### Minor Version Number
+
+The minor version number is used to identify small but otherwise
+compatible differences between protocol versions. A mismatch between
+the advertised minor version number and the minor version that is
+supported by the host SHOULD NOT be fatal to the operation of the
+host.
+
+### PROP 2: PROP_NCP_VERSION {#prop-ncp-version}
+
+* Type: Read-Only
+* Packed-Encoding: `U`
+
+Octets: |       *n*
+--------|-------------------
+Fields: | NCP_VESION_STRING
+
+Contains a string which describes the firmware currently running on
+the NCP. Encoded as a zero-terminated UTF-8 string.
+
+The format of the string is not strictly defined, but it is intended
+to present similarly to the "User-Agent" string from HTTP. The
+RECOMMENDED format of the string is as follows:
+
+    STACK-NAME/STACK-VERSION[BUILD_INFO][; OTHER_INFO]; BUILD_DATE_AND_TIME
+
+Examples:
+
+ *  `OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04`
+ *  `ConnectIP/2.0b125 s1 ALPHA; Sept 24 2015 20:49:19`
+
+The host **MUST** only use this property from NLI 0. Behavior when used
+from other NLIs is undefined.
+
+### PROP 3: PROP_INTERFACE_TYPE {#prop-interface-type}
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |       1-3
+--------|----------------
+Fields: | INTERFACE_TYPE
+
+This integer identifies what the network protocol for this NCP.
+Currently defined values are:
+
+ *  0: Bootloader
+ *  2: ZigBee IP(TM)
+ *  3: Thread(R)
+
+The host MUST enter a FAULT state if it does not recognize the
+protocol given by the NCP.
+
+### PROP 4: PROP_INTERFACE_VENDOR_ID {#prop-interface-vendor-id}
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |       1-3
+--------|----------------
+Fields: | VENDOR_ID
+
+Vendor identifier.
+
+### PROP 5: PROP_CAPS {#prop-caps}
+
+* Type: Read-Only
+* Packed-Encoding: `A(i)`
+
+Octets: |  1-3  |  1-3  | ...
+--------|-------|-------|-----
+Fields: | CAP_1 | CAP_2 | ...
+
+Describes the supported capabilities of this NCP. Encoded as a list of
+packed unsigned integers.
+
+A capability is defined as a 21-bit integer that describes a subset of
+functionality which is supported by the NCP.
+
+Currently defined values are:
+
+ * 1: `CAP_LOCK`
+ * 2: `CAP_NET_SAVE`
+ * 3: `CAP_HBO`: Host Buffer Offload. See (#feature-host-buffer-offload).
+ * 4: `CAP_POWER_SAVE`
+ * 5: `CAP_COUNTERS`
+ * 6: `CAP_JAM_DETECT`: Jamming detection. See (#feature-jam-detect)
+ * 7: `CAP_PEEK_POKE`: PEEK/POKE debugging commands.
+ * 8: `CAP_WRITABLE_RAW_STREAM`: `PROP_STREAM_RAW` is writable.
+ * 9: `CAP_GPIO`: Support for GPIO access. See (#feature-gpio-access).
+ * 10: `CAP_TRNG`: Support for true random number generation. See (#feature-trng).
+ * 11: `CAP_CMD_MULTI`: Support for `CMD_PROP_VALUE_MULTI_GET` ((#cmd-prop-value-multi-get)), `CMD_PROP_VALUE_MULTI_SET` ((#cmd-prop-value-multi-set), and `CMD_PROP_VALUES_ARE` ((#cmd-prop-values-are)).
+ * 12: `CAP_UNSOL_UPDATE_FILTER`: Support for `PROP_UNSOL_UPDATE_FILTER` ((#prop-unsol-update-filter)) and `PROP_UNSOL_UPDATE_LIST` ((#prop-unsol-update-list)).
+ * 16: `CAP_802_15_4_2003`
+ * 17: `CAP_802_15_4_2006`
+ * 18: `CAP_802_15_4_2011`
+ * 21: `CAP_802_15_4_PIB`
+ * 24: `CAP_802_15_4_2450MHZ_OQPSK`
+ * 25: `CAP_802_15_4_915MHZ_OQPSK`
+ * 26: `CAP_802_15_4_868MHZ_OQPSK`
+ * 27: `CAP_802_15_4_915MHZ_BPSK`
+ * 28: `CAP_802_15_4_868MHZ_BPSK`
+ * 29: `CAP_802_15_4_915MHZ_ASK`
+ * 30: `CAP_802_15_4_868MHZ_ASK`
+ * 48: `CAP_ROLE_ROUTER`
+ * 49: `CAP_ROLE_SLEEPY`
+ * 52: `CAP_NET_THREAD_1_0`
+ * 512: `CAP_MAC_WHITELIST`
+ * 513: `CAP_MAC_RAW`
+ * 514: `CAP_OOB_STEERING_DATA`
+ * 1024: `CAP_THREAD_COMMISSIONER`
+ * 1025: `CAP_THREAD_TMF_PROXY`
+
+
+Additionally, future capability allocations SHALL be made from the
+following allocation plan:
+
+Capability Range      | Description
+----------------------|------------------
+0 - 127               | Reserved for core capabilities
+128 - 15,359          | *UNALLOCATED*
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | *UNALLOCATED*
+2,000,000 - 2,097,151 | Experimental use only
+
+
+### PROP 6: PROP_INTERFACE_COUNT {#prop-interface-count}
+
+* Type: Read-Only
+* Packed-Encoding: `C`
+
+Octets: |       1
+--------|-----------------
+Fields: | `INTERFACE_COUNT`
+
+Describes the number of concurrent interfaces supported by this NCP.
+Since the concurrent interface mechanism is still TBD, this value MUST
+always be one.
+
+This value is encoded as an unsigned 8-bit integer.
+
+The host **MUST** only use this property from NLI 0. Behavior when used
+from other NLIs is undefined.
+
+### PROP 7: PROP_POWER_STATE {#prop-power-state}
+
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Octets: |        1
+--------|------------------
+Fields: | POWER_STATE
+
+Describes the current power state of the NCP. By writing to this
+property you can manage the lower state of the NCP. Enumeration is
+encoded as a single unsigned byte.
+
+Defined values are:
+
+ *  0: `POWER_STATE_OFFLINE`: NCP is physically powered off.
+    (Enumerated for completeness sake, not expected on the wire)
+ *  1: `POWER_STATE_DEEP_SLEEP`: Almost everything on the NCP is shut
+    down, but can still be resumed via a command or interrupt.
+ *  2: `POWER_STATE_STANDBY`: NCP is in the lowest power state that
+    can still be awoken by an event from the radio (e.g. waiting for
+    alarm)
+ *  3: `POWER_STATE_LOW_POWER`: NCP is responsive (and possibly
+    connected), but using less power. (e.g. "Sleepy" child node)
+ *  4: `POWER_STATE_ONLINE`: NCP is fully powered. (e.g. "Parent"
+    node)
+
+<!-- RQ
+  -- We should consider reversing the numbering here so that 0 is
+     `POWER_STATE_ONLINE`. We may also want to include some extra
+     values between the defined values for future expansion, so
+     that we can preserve the ordered relationship. -- -->
+
+### PROP 8: PROP_HWADDR {#prop-hwaddr}
+
+* Type: Read-Only\*
+* Packed-Encoding: `E`
+
+Octets: |    8
+--------|------------
+Fields: | HWADDR
+
+The static EUI64 address of the device, used as a serial number.
+This value is read-only, but may be writable under certain
+vendor-defined circumstances.
+
+### PROP 9: PROP_LOCK {#prop-lock}
+
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Octets: |    1
+--------|------------
+Fields: | LOCK
+
+Property lock. Used for grouping changes to several properties to
+take effect at once, or to temporarily prevent the automatic updating
+of property values. When this property is set, the execution of the
+NCP is effectively frozen until it is cleared.
+
+This property is only supported if the `CAP_LOCK` capability is present.
+
+Unlike most other properties, setting this property to true when the
+value of the property is already true **MUST** fail with a last status
+of `STATUS_ALREADY`.
+
+### PROP 10: PROP_HOST_POWER_STATE {#prop-host-power-state}
+
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Default value: 4
+
+Octets: |        1
+--------|------------------
+Fields: | `HOST_POWER_STATE`
+
+Describes the current power state of the *host*. This property is used
+by the host to inform the NCP when it has changed power states. The
+NCP can then use this state to determine which properties need
+asynchronous updates. Enumeration is encoded as a single unsigned
+byte. These states are defined in similar terms to `PROP_POWER_STATE`
+((#prop-power-state)).
+
+Defined values are:
+
+*   0: `HOST_POWER_STATE_OFFLINE`: Host is physically powered off and
+    cannot be woken by the NCP. All asynchronous commands are
+    squelched.
+*   1: `HOST_POWER_STATE_DEEP_SLEEP`: The host is in a low power state
+    where it can be woken by the NCP but will potentially require more
+    than two seconds to become fully responsive. The NCP **MUST**
+    avoid sending unnecessary property updates, such as child table
+    updates or non-critical messages on the debug stream. If the NCP
+    needs to wake the host for traffic, the NCP **MUST** first take
+    action to wake the host. Once the NCP signals to the host that it
+    should wake up, the NCP **MUST** wait for some activity from the
+    host (indicating that it is fully awake) before sending frames.
+*   2: **RESERVED**. This value **MUST NOT** be set by the host. If
+    received by the NCP, the NCP **SHOULD** consider this as a synonym
+    of `HOST_POWER_STATE_DEEP_SLEEP`.
+*   3: `HOST_POWER_STATE_LOW_POWER`: The host is in a low power state
+    where it can be immediately woken by the NCP. The NCP **SHOULD**
+    avoid sending unnecessary property updates, such as child table
+    updates or non-critical messages on the debug stream.
+*   4: `HOST_POWER_STATE_ONLINE`: The host is awake and responsive. No
+    special filtering is performed by the NCP on asynchronous updates.
+*   All other values are **RESERVED**. They MUST NOT be set by the
+    host. If received by the NCP, the NCP **SHOULD** consider the value as
+    a synonym of `HOST_POWER_STATE_LOW_POWER`.
+
+<!-- RQ
+  -- We should consider reversing the numbering here so that 0 is
+     `POWER_STATE_ONLINE`. We may also want to include some extra
+     values between the defined values for future expansion, so
+     that we can preserve the ordered relationship. -- -->
+
+After setting this power state, any further commands from the host to
+the NCP will cause `HOST_POWER_STATE` to automatically revert to
+`HOST_POWER_STATE_ONLINE`.
+
+When the host is entering a low-power state, it should wait for the
+response from the NCP acknowledging the command (with `CMD_VALUE_IS`).
+Once that acknowledgement is received the host may enter the low-power
+state.
+
+If the NCP has the `CAP_UNSOL_UPDATE_FILTER` capability, any unsolicited
+property updates masked by `PROP_UNSOL_UPDATE_FILTER` should be honored
+while the host indicates it is in a low-power state. After resuming to the
+`HOST_POWER_STATE_ONLINE` state, the value of `PROP_UNSOL_UPDATE_FILTER`
+**MUST** be unchanged from the value assigned prior to the host indicating
+it was entering a low-power state.
+
+The host **MUST** only use this property from NLI 0. Behavior when used
+from other NLIs is undefined.
+
+### PROP 4104: PROP_UNSOL_UPDATE_FILTER {#prop-unsol-update-filter}
+
+* Required only if `CAP_UNSOL_UPDATE_FILTER` is set.
+* Type: Read-Write
+* Packed-Encoding: `A(I)`
+* Default value: Empty.
+
+Contains a list of properties which are *excluded* from generating
+unsolicited value updates. This property **MUST** be empty after reset.
+
+In other words, the host may opt-out of unsolicited property updates
+for a specific property by adding that property id to this list.
+
+Hosts **SHOULD NOT** add properties to this list which are not
+present in `PROP_UNSOL_UPDATE_LIST`. If such properties are added,
+the NCP **MUST** ignore the unsupported properties.
+
+<!-- RQ
+  -- The justification for the above behavior is to attempt to avoid possible
+     future interop problems by explicitly making sure that unknown
+     properties are ignored. Since unknown properties will obviously not be
+     generating unsolicited updates, it seems fairly harmless. An
+     implementation may print out a warning to the debug stream.
+
+     Note that the error is still detectable: If you VALUE\_SET unsupported
+     properties, the resulting VALUE\_IS would contain only the supported
+     properties of that set(since the unsupported properties would be
+     ignored). If an implementation cares that much about getting this
+     right then it needs to make sure that it checks
+     PROP\_UNSOL\_UPDATE\_LIST first.
+  -- -->
+
+Implementations of this property are only **REQUIRED** to support
+and use the following commands:
+
+* `CMD_PROP_VALUE_GET` ((#cmd-prop-value-get))
+* `CMD_PROP_VALUE_SET` ((#cmd-prop-value-set))
+* `CMD_PROP_VALUE_IS` ((#cmd-prop-value-is))
+
+Implementations of this property **MAY** optionally support and use
+the following commands:
+
+* `CMD_PROP_VALUE_INSERT` ((#cmd-prop-value-insert))
+* `CMD_PROP_VALUE_REMOVE` ((#cmd-prop-value-remove))
+* `CMD_PROP_VALUE_INSERTED` ((#cmd-prop-value-inserted))
+* `CMD_PROP_VALUE_REMOVED` ((#cmd-prop-value-removed))
+
+Host implementations which are aiming to maximize their compatability across
+different firmwre implementations **SHOULD NOT** assume the availability of the
+optional commands for this property.
+
+The value of this property **SHALL** be independent for each NLI.
+
+### PROP 4105: PROP_UNSOL_UPDATE_LIST {#prop-unsol-update-list}
+
+* Required only if `CAP_UNSOL_UPDATE_FILTER` is set.
+* Type: Read-Only
+* Packed-Encoding: `A(I)`
+
+Contains a list of properties which are capable of generating
+unsolicited value updates. This list can be used when populating
+`PROP_UNSOL_UPDATE_FILTER` to disable all unsolicited property
+updates.
+
+This property is intended to effectively behave as a constant
+for a given NCP firmware.
+
+Note that not all properties that support unsolicited updates need to
+be listed here. Scan results, for example, are only generated due to
+direct action on the part of the host, so those properties **MUST NOT**
+not be included in this list.
+
+The value of this property **MAY** be different across available
+NLIs.
+
+## Stream Properties {#prop-stream}
+
+### PROP 112: PROP_STREAM_DEBUG {#prop-stream-debug}
+
+* Type: Read-Only-Stream
+* Packed-Encoding: `D`
+
+Octets: |    *n*
+--------|------------
+Fields: | UTF8_DATA
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. The stream provides human-readable debugging
+output which may be displayed in the host logs.
+
+The location of newline characters is not assumed by the host: it is
+the NCP's responsibility to insert newline characters where needed,
+just like with any other text stream.
+
+To receive the debugging stream, you wait for `CMD_PROP_VALUE_IS`
+commands for this property from the NCP.
+
+### PROP 113: PROP_STREAM_RAW {#prop-stream-raw}
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `dD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving raw packets
+to and from the radio. The exact format of the frame metadata and data is
+dependent on the MAC and PHY being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+Implementations may OPTIONALLY support the ability to transmit arbitrary
+raw packets. Support for this feature is indicated by the presence of the
+`CAP_WRITABLE_RAW_STREAM` capability.
+
+If the capability `CAP_WRITABLE_RAW_STREAM` is set, then packets written
+to this stream with `CMD_PROP_VALUE_SET` will be sent out over the radio.
+This allows the caller to use the radio directly, with the stack being
+implemented on the host instead of the NCP.
+
+#### Frame Metadata Format {#frame-metadata-format}
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata and is
+OPTIONAL. Frame metadata MAY be empty or partially specified. Partially
+specified metadata MUST be accepted. Default values are used for all
+unspecified fields.
+
+The same general format is used for `PROP_STREAM_RAW`, `PROP_STREAM_NET`,
+and `PROP_STREAM_NET_INSECURE`. It can be used for frames sent from the
+NCP to the host as well as frames sent from the host to the NCP.
+
+The frame metadata field consists of the following fields:
+
+ Field   | Description                  | Type       | Len   | Default
+:--------|:-----------------------------|:-----------|-------|----------
+MD_POWER | (dBm) RSSI/TX-Power          | `c` int8   | 1     | -128
+MD_NOISE | (dBm) Noise floor            | `c` int8   | 1     | -128
+MD_FLAG  | Flags (defined below)        | `S` uint16 | 2     |
+MD_PHY   | PHY-specific data            | `d` data   | >=2   |
+MD_VEND  | Vendor-specific data         | `d` data   | >=2   |
+
+The following fields are ignored by the NCP for packets sent to it from
+the host:
+
+* MD_NOISE
+* MD_FLAG
+
+When specifying `MD_POWER` for a packet to be transmitted, the actual
+transmit power is never larger than the current value of `PROP_PHY_TX_POWER`
+((#prop-phy-tx-power)). When left unspecified (or set to the value -128),
+an appropriate transmit power will be chosen by the NCP.
+
+The bit values in `MD_FLAG` are defined as follows:
+
+ Bit     | Mask   | Name              | Description if set
+---------|--------|:------------------|:----------------
+15       | 0x0001 | MD_FLAG_TX        | Packet was transmitted, not received.
+13       | 0x0004 | MD_FLAG_BAD_FCS   | Packet was received with bad FCS
+12       | 0x0008 | MD_FLAG_DUPE      | Packet seems to be a duplicate
+0-11, 14 | 0xFFF2 | MD_FLAG_RESERVED  | Flags reserved for future use.
+
+The format of `MD_PHY` is specified by the PHY layer currently in use,
+and may contain information such as the channel, LQI, antenna, or other
+pertainent information.
+
+### PROP 114: PROP_STREAM_NET {#prop-stream-net}
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `dD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving data packets
+to and from the currently attached network. The exact format of the frame
+metadata and data is dependent on the network protocol being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+To send network packets, you call `CMD_PROP_VALUE_SET` on this property with
+the value of the packet.
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata, the
+format of which is described in (#frame-metadata-format).
+
+### PROP 115: PROP_STREAM_NET_INSECURE {#prop-stream-net-insecure}
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `dD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving unencrypted
+and unauthenticated data packets to and from nearby devices for the
+purposes of device commissioning. The exact format of the frame
+metadata and data is dependent on the network protocol being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+To send network packets, you call `CMD_PROP_VALUE_SET` on this property with
+the value of the packet.
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata, the
+format of which is described in (#frame-metadata-format).
+
diff --git a/doc/spinel-protocol-src/spinel-prop-debug.md b/doc/spinel-protocol-src/spinel-prop-debug.md
new file mode 100644
index 0000000..d026261
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-debug.md
@@ -0,0 +1,32 @@
+## Debug Properties {#prop-debug}
+
+### PROP 16384: PROP_DEBUG_TEST_ASSERT {#prop-debug-test-assert}
+* Type: Read-Only
+* Packed-Encoding: `b`
+
+Reading this property will cause an assert on the NCP. This
+is intended for testing the assert functionality of
+underlying platform/NCP. Assert should ideally cause the
+NCP to reset, but if `assert` is not supported or disabled
+boolean value of `false` is returned in response.
+
+### PROP 16385: PROP_DEBUG_NCP_LOG_LEVEL {#prop-debug-ncp-log-level}
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Provides access to the NCP log level. Currently defined values are (which follows
+the RFC 5424):
+
+ *  0: Emergency (emerg).
+ *  1: Alert (alert).
+ *  2: Critical (crit).
+ *  3: Error (err).
+ *  4: Warning (warn).
+ *  5: Notice (notice).
+ *  6: Information (info).
+ *  7: Debug (debug).
+
+If the NCP supports dynamic log level control, setting this property
+changes the log level accordingly. Getting the value returns the current
+log level.  If the dynamic log level control is not supported, setting this
+property returns a `PROP_LAST_STATUS` with `STATUS_INVALID_COMMAND_FOR_PROP`.
diff --git a/doc/spinel-protocol-src/spinel-prop-ipv6.md b/doc/spinel-protocol-src/spinel-prop-ipv6.md
new file mode 100644
index 0000000..0a874da
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-ipv6.md
@@ -0,0 +1,41 @@
+## IPv6 Properties {#prop-ipv6}
+
+### PROP 96: PROP_IPV6_LL_ADDR {#prop-ipv6-ll-addr}
+* Type: Read-Only
+* Packed-Encoding: `6`
+
+IPv6 Address
+
+### PROP 97: PROP_IPV6_ML_ADDR {#prop-ipv6-ml-addr}
+* Type: Read-Only
+* Packed-Encoding: `6`
+
+IPv6 Address + Prefix Length
+
+### PROP 98: PROP_IPV6_ML_PREFIX {#prop-ipv6-ml-prefix}
+* Type: Read-Write
+* Packed-Encoding: `6C`
+
+IPv6 Prefix + Prefix Length
+
+### PROP 99: PROP_IPV6_ADDRESS_TABLE {#prop-ipv6-address-table}
+* Type: Read-Write
+* Packed-Encoding: `A(t(6CLLC))`
+
+Array of structures containing:
+
+* `6`: IPv6 Address
+* `C`: Network Prefix Length
+* `L`: Valid Lifetime
+* `L`: Preferred Lifetime
+* `C`: Flags
+
+### PROP 101: PROP_IPv6_ICMP_PING_OFFLOAD
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Allow the NCP to directly respond to ICMP ping requests. If this is
+turned on, ping request ICMP packets will not be passed to the host.
+
+Default value is `false`.
+
diff --git a/doc/spinel-protocol-src/spinel-prop-mac.md b/doc/spinel-protocol-src/spinel-prop-mac.md
new file mode 100644
index 0000000..4030c14
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-mac.md
@@ -0,0 +1,204 @@
+## MAC Properties {#prop-mac}
+
+### PROP 48: PROP_MAC_SCAN_STATE {#prop-mac-scan-state}
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Unit: Enumeration
+
+Possible Values:
+
+* 0: `SCAN_STATE_IDLE`
+* 1: `SCAN_STATE_BEACON`
+* 2: `SCAN_STATE_ENERGY`
+* 3: `SCAN_STATE_DISCOVER`
+
+Set to `SCAN_STATE_BEACON` to start an active scan.
+Beacons will be emitted from `PROP_MAC_SCAN_BEACON`.
+
+Set to `SCAN_STATE_ENERGY` to start an energy scan.
+Channel energy result will be reported by emissions
+of `PROP_MAC_ENERGY_SCAN_RESULT` (per channel).
+
+Set to `SCAN_STATE_DISOVER` to start a Thread MLE discovery
+scan operation. Discovery scan result will be emitted from
+`PROP_MAC_SCAN_BEACON`.
+
+Value switches to `SCAN_STATE_IDLE` when scan is complete.
+
+### PROP 49: PROP_MAC_SCAN_MASK {#prop-mac-scan-mask}
+* Type: Read-Write
+* Packed-Encoding: `A(C)`
+* Unit: List of channels to scan
+
+
+### PROP 50: PROP_MAC_SCAN_PERIOD {#prop-mac-scan-period}
+* Type: Read-Write
+* Packed-Encoding: `S` (uint16)
+* Unit: milliseconds per channel
+
+### PROP 51: PROP_MAC_SCAN_BEACON {#prop-mac-scan-beacon}
+* Type: Read-Only-Stream
+* Packed-Encoding: `Ccdd` (or `Cct(ESSc)t(iCUdd)`)
+
+Octets: | 1  |   1  |    2    |   *n*    |    2    |   *n*
+--------|----|------|---------|----------|---------|----------
+Fields: | CH | RSSI | MAC_LEN | MAC_DATA | NET_LEN | NET_DATA
+
+Scan beacons have two embedded structures which contain
+information about the MAC layer and the NET layer. Their
+format depends on the MAC and NET layer currently in use.
+The format below is for an 802.15.4 MAC with Thread:
+
+* `C`: Channel
+* `c`: RSSI of the beacon
+* `t`: MAC layer properties (802.15.4 layer shown below for convenience)
+  * `E`: Long address
+  * `S`: Short address
+  * `S`: PAN-ID
+  * `c`: LQI
+* NET layer properties (Standard net layer shown below for convenience)
+  * `i`: Protocol Number
+  * `C`: Flags
+  * `U`: Network Name
+  * `d`: XPANID
+  * `d`: Steering data
+
+Extra parameters may be added to each of the structures
+in the future, so care should be taken to read the length
+that prepends each structure.
+
+### PROP 52: PROP_MAC_15_4_LADDR {#prop-mac-15-4-laddr}
+* Type: Read-Write
+* Packed-Encoding: `E`
+
+The 802.15.4 long address of this node.
+
+This property is only present on NCPs which implement 802.15.4
+
+### PROP 53: PROP_MAC_15_4_SADDR {#prop-mac-15-4-saddr}
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+The 802.15.4 short address of this node.
+
+This property is only present on NCPs which implement 802.15.4
+
+### PROP 54: PROP_MAC_15_4_PANID {#prop-mac-15-4-panid}
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+The 802.15.4 PANID this node is associated with.
+
+This property is only present on NCPs which implement 802.15.4
+
+### PROP 55: PROP_MAC_RAW_STREAM_ENABLED {#prop-mac-raw-stream-enabled}
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Set to true to enable raw MAC frames to be emitted from `PROP_STREAM_RAW`.
+See (#prop-stream-raw).
+
+### PROP 56: PROP_MAC_PROMISCUOUS_MODE {#prop-mac-promiscuous-mode}
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Possible Values:
+
+Id | Name                          | Description
+---|-------------------------------|------------------
+ 0 | `MAC_PROMISCUOUS_MODE_OFF`    | Normal MAC filtering is in place.
+ 1 | `MAC_PROMISCUOUS_MODE_NETWORK`| All MAC packets matching network are passed up the stack.
+ 2 | `MAC_PROMISCUOUS_MODE_FULL`   | All decoded MAC packets are passed up the stack.
+
+See (#prop-stream-raw).
+
+### PROP 57: PROP_MAC_ENERGY_SCAN_RESULT {#prop-mac-escan-result}
+* Type: Read-Only-Stream
+* Packed-Encoding: `Cc`
+
+This property is emitted during energy scan operation
+per scanned channel with following format:
+
+* `C`: Channel
+* `c`: RSSI (in dBm)
+
+### PROP 58: PROP_MAC_DATA_POLL_PERIOD {#prop-mac-data-poll-period
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+The (user-specified) data poll (802.15.4 MAC Data Request) period
+in milliseconds. Value zero means there is no user-specified
+poll period, and the network stack determines the maximum period
+based on the MLE Child Timeout.
+
+If the value is non-zero, it specifies the maximum period between
+data poll transmissions. Note that the network stack may send data
+request transmissions more frequently when expecting a control-message
+(e.g., when waiting for an MLE Child ID Response).
+
+This property is only present on NCPs which implement 802.15.4.
+
+### PROP 4864: PROP_MAC_WHITELIST  {#prop-mac-whitelist}
+* Type: Read-Write
+* Packed-Encoding: `A(T(Ec))`
+* Required capability: `CAP_MAC_WHITELIST`
+
+Structure Parameters:
+
+* `E`: EUI64 address of node
+* `c`: Optional RSSI-override value. The value 127 indicates
+       that the RSSI-override feature is not enabled for this
+       address. If this value is omitted when setting or
+       inserting, it is assumed to be 127. This parameter is
+       ignored when removing.
+
+### PROP 4865: PROP_MAC_WHITELIST_ENABLED  {#prop-mac-whitelist-enabled}
+* Type: Read-Write
+* Packed-Encoding: `b`
+* Required capability: `CAP_MAC_WHITELIST`
+
+### PROP 4867: SPINEL_PROP_MAC_SRC_MATCH_ENABLED  {#prop-mac-src-match-enabled}
+* Type: Write
+* Packed-Encoding: `b`
+
+Set to true to enable radio source matching or false to disable it. This property
+is only available if the `SPINEL_CAP_MAC_RAW` capability is present. The source match
+functionality is used by radios when generating ACKs. The short and extended address
+lists are used for settings the Frame Pending bit in the ACKs.
+
+### PROP 4868: SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES  {#prop-mac-src-match-short-addresses}
+* Type: Write
+* Packed-Encoding: `A(S)`
+
+Configures the list of short addresses used for source matching. This property
+is only available if the `SPINEL_CAP_MAC_RAW` capability is present.
+
+Structure Parameters:
+
+* `S`: Short address for hardware generated ACKs
+
+### PROP 4869: SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES  {#prop-mac-src-match-extended-addresses}
+* Type: Write
+* Packed-Encoding: `A(E)`
+
+Configures the list of extended addresses used for source matching. This property
+is only available if the `SPINEL_CAP_MAC_RAW` capability is present.
+
+Structure Parameters:
+
+* `E`: EUI64 address for hardware generated ACKs
+
+### PROP 4870: PROP_MAC_BLACKLIST  {#prop-mac-blacklist}
+* Type: Read-Write
+* Packed-Encoding: `A(T(E))`
+* Required capability: `CAP_MAC_WHITELIST`
+
+Structure Parameters:
+
+* `E`: EUI64 address of node
+
+### PROP 4871: PROP_MAC_BLACKLIST_ENABLED  {#prop-mac-blacklist-enabled}
+* Type: Read-Write
+* Packed-Encoding: `b`
+* Required capability: `CAP_MAC_WHITELIST`
+
diff --git a/doc/spinel-protocol-src/spinel-prop-net.md b/doc/spinel-protocol-src/spinel-prop-net.md
new file mode 100644
index 0000000..709810a
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-net.md
@@ -0,0 +1,70 @@
+## NET Properties {#prop-net}
+
+### PROP 64: PROP_NET_SAVED {#prop-net-saved}
+* Type: Read-Only
+* Packed-Encoding: `b`
+
+Returns true if there is a network state stored/saved.
+
+### PROP 65: PROP_NET_IF_UP  {#prop-net-if-up}
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Network interface up/down status. Non-zero (set to 1) indicates up,
+zero indicates down.
+
+### PROP 66: PROP_NET_STACK_UP  {#prop-net-stack-up}
+* Type: Read-Write
+* Packed-Encoding: `b`
+* Unit: Enumeration
+
+Thread stack operational status. Non-zero (set to 1) indicates up,
+zero indicates down.
+
+### PROP 67: PROP_NET_ROLE {#prop-net-role}
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Unit: Enumeration
+
+Values:
+
+* 0: `NET_ROLE_DETACHED`
+* 1: `NET_ROLE_CHILD`
+* 2: `NET_ROLE_ROUTER`
+* 3: `NET_ROLE_LEADER`
+
+### PROP 68: PROP_NET_NETWORK_NAME  {#prop-net-network-name}
+* Type: Read-Write
+* Packed-Encoding: `U`
+
+### PROP 69: PROP_NET_XPANID   {#prop-net-xpanid}
+* Type: Read-Write
+* Packed-Encoding: `D`
+
+### PROP 70: PROP_NET_MASTER_KEY   {#prop-net-master-key}
+* Type: Read-Write
+* Packed-Encoding: `D`
+
+### PROP 71: PROP_NET_KEY_SEQUENCE_COUNTER   {#prop-net-key-sequence-counter}
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+### PROP 72: PROP_NET_PARTITION_ID   {#prop-net-partition-id}
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+The partition ID of the partition that this node is a member of.
+
+### PROP 73: PROP_NET_REQUIRE_JOIN_EXISTING   {#prop-net-require-join-existing}
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+### PROP 74: PROP_NET_KEY_SWITCH_GUARDTIME   {#prop-net-key-swtich-guardtime}
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+### PROP 75: PROP_NET_PSKC   {#prop-net-pskc}
+* Type: Read-Write
+* Packed-Encoding: `D`
+
+
diff --git a/doc/spinel-protocol-src/spinel-prop-overview.md b/doc/spinel-protocol-src/spinel-prop-overview.md
new file mode 100644
index 0000000..b277a74
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-overview.md
@@ -0,0 +1,69 @@
+Spinel is largely a property-based protocol, similar to representational state transfer (REST), with a property defined for every attribute that an OS needs to create, read, update or delete in the function of an IPv6 interface. The inspiration of this approach was memory-mapped hardware registers for peripherals. The goal is to avoid, as much as possible, the use of large complicated structures and/or method argument lists. The reason for avoiding these is because they have a tendency to change, especially early in development. Adding or removing a property from a structure can render the entire protocol incompatible. By using properties, you simply extend the protocol with an additional property.
+
+Almost all features and capabilities are implemented using properties. Most new features that are initially proposed as commands can be adapted to be property-based instead. Notable exceptions include "Host Buffer Offload" ((#feature-host-buffer-offload)) and "Network Save" ((#feature-network-save)).
+
+In Spinel, properties are keyed by an unsigned integer between 0 and 2,097,151 (See (#packed-unsigned-integer)).
+
+## Property Methods ###
+
+Properties may support one or more of the following methods:
+
+*   `VALUE_GET` ((#cmd-prop-value-get))
+*   `VALUE_SET` ((#cmd-prop-value-set))
+*   `VALUE_INSERT`  ((#cmd-prop-value-insert))
+*   `VALUE_REMOVE`  ((#cmd-prop-value-remove))
+
+Additionally, the NCP can send updates to the host (either synchronously or asynchronously) that inform the host about changes to specific properties:
+
+*   `VALUE_IS`  ((#cmd-prop-value-is))
+*   `VALUE_INSERTED`  ((#cmd-prop-value-inserted))
+*   `VALUE_REMOVED`  ((#cmd-prop-value-removed))
+
+## Property Types ###
+
+Conceptually, there are three different types of properties:
+
+*   Single-value properties
+*   Multiple-value (Array) properties
+*   Stream properties
+
+### Single-Value Properties ####
+
+Single-value properties are properties that have a simple representation of a single value. Examples would be:
+
+*   Current radio channel (Represented as an unsigned 8-bit integer)
+*   Network name (Represented as a UTF-8 encoded string)
+*   802\.15.4 PAN ID (Represented as an unsigned 16-bit integer)
+
+The valid operations on these sorts of properties are `GET` and `SET`.
+
+### Multiple-Value Properties ####
+
+Multiple-Value Properties have more than one value associated with them. Examples would be:
+
+*   List of channels supported by the radio hardware.
+*   List of IPv6 addresses assigned to the interface.
+*   List of capabilities supported by the NCP.
+
+The valid operations on these sorts of properties are `VALUE_GET`, `VALUE_SET`, `VALUE_INSERT`, and `VALUE_REMOVE`.
+
+When the value is fetched using `VALUE_GET`, the returned value is the concatenation of all of the individual values in the list. If the length of the value for an individual item in the list is not defined by the type then each item returned in the list is prepended with a length (See (#arrays)). The order of the returned items, unless explicitly defined for that specific property, is undefined.
+
+`VALUE_SET` provides a way to completely replace all previous values. Calling `VALUE_SET` with an empty value effectively instructs the NCP to clear the value of that property.
+
+`VALUE_INSERT` and `VALUE_REMOVE` provide mechanisms for the insertion or removal of individual items *by value*. The payload for these commands is a plain single value.
+
+### Stream Properties ####
+
+Stream properties are special properties representing streams of data. Examples would be:
+
+*   Network packet stream ((#prop-stream-net))
+*   Raw packet stream ((#prop-stream-raw))
+*   Debug message stream ((#prop-stream-debug))
+*   Network Beacon stream ((#prop-mac-scan-beacon))
+
+All such properties emit changes asynchronously using the `VALUE_IS` command, sent from the NCP to the host. For example, as IPv6 traffic is received by the NCP, the IPv6 packets are sent to the host by way of asynchronous `VALUE_IS` notifications.
+
+Some of these properties also support the host send data back to the NCP. For example, this is how the host sends IPv6 traffic to the NCP.
+
+These types of properties generally do not support `VALUE_GET`, as it is meaningless.
diff --git a/doc/spinel-protocol-src/spinel-prop-phy.md b/doc/spinel-protocol-src/spinel-prop-phy.md
new file mode 100644
index 0000000..21d6c14
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop-phy.md
@@ -0,0 +1,72 @@
+## PHY Properties {#prop-phy}
+
+
+### PROP 32: PROP_PHY_ENABLED {#prop-phy-enabled}
+* Type: Read-Write
+* Packed-Encoding: `b` (bool8)
+
+Set to 1 if the PHY is enabled, set to 0 otherwise.
+May be directly enabled to bypass higher-level packet processing
+in order to implement things like packet sniffers. This property
+can only be written if the `SPINEL_CAP_MAC_RAW` capability is present.
+
+### PROP 33: PROP_PHY_CHAN {#prop-phy-chan}
+* Type: Read-Write
+* Packed-Encoding: `C` (uint8)
+
+Value is the current channel. Must be set to one of the
+values contained in `PROP_PHY_CHAN_SUPPORTED`.
+
+### PROP 34: PROP_PHY_CHAN_SUPPORTED {#prop-phy-chan-supported}
+* Type: Read-Only
+* Packed-Encoding: `A(C)` (array of uint8)
+* Unit: List of channels
+
+Value is a list of channel values that are supported by the
+hardware.
+
+### PROP 35: PROP_PHY_FREQ {#prop-phy-freq}
+* Type: Read-Only
+* Packed-Encoding: `L` (uint32)
+* Unit: Kilohertz
+
+Value is the radio frequency (in kilohertz) of the
+current channel.
+
+### PROP 36: PROP_PHY_CCA_THRESHOLD {#prop-phy-cca-threshold}
+* Type: Read-Write
+* Packed-Encoding: `c` (int8)
+* Unit: dBm
+
+Value is the CCA (clear-channel assessment) threshold. Set to
+-128 to disable.
+
+When setting, the value will be rounded down to a value
+that is supported by the underlying radio hardware.
+
+### PROP 37: PROP_PHY_TX_POWER {#prop-phy-tx-power}
+* Type: Read-Write
+* Packed-Encoding: `c` (int8)
+* Unit: dBm
+
+Value is the transmit power of the radio.
+
+When setting, the value will be rounded down to a value
+that is supported by the underlying radio hardware.
+
+### PROP 38: PROP_PHY_RSSI {#prop-phy-rssi}
+* Type: Read-Only
+* Packed-Encoding: `c` (int8)
+* Unit: dBm
+
+Value is the current RSSI (Received signal strength indication)
+from the radio. This value can be used in energy scans and for
+determining the ambient noise floor for the operating environment.
+
+### PROP 39: PROP_PHY_RX_SENSITIVITY {#prop-phy-rx-sensitivity}
+* Type: Read-Only
+* Packed-Encoding: `c` (int8)
+* Unit: dBm
+
+Value is the radio receive sensitivity. This value can be used as
+lower bound noise floor for link metrics computation.
diff --git a/doc/spinel-protocol-src/spinel-prop.md b/doc/spinel-protocol-src/spinel-prop.md
new file mode 100644
index 0000000..a142c66
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-prop.md
@@ -0,0 +1,58 @@
+# Properties
+
+{{spinel-prop-overview.md}}
+
+## Property Numbering
+
+While the majority of the properties that allow the configuration
+of network connectivity are network protocol specific, there are
+several properties that are required in all implementations.
+
+Future property allocations **SHALL** be made from the
+following allocation plan:
+
+Property ID Range     | Description
+:---------------------|:-----------------
+0 - 127               | Reserved for frequently-used properties
+128 - 15,359          | Unallocated
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | Unallocated
+2,000,000 - 2,097,151 | Experimental use only
+
+For an explanation of the data format encoding shorthand used
+throughout this document, see (#data-packing).
+
+## Property Sections
+
+The currently assigned properties are broken up into several
+sections, each with reserved ranges of property identifiers.
+These ranges are:
+
+Name   | Range (Inclusive)            | Documentation
+-------|------------------------------|--------------
+Core   | 0x00 - 0x1F, 0x1000 - 0x11FF | (#prop-core)
+PHY    | 0x20 - 0x2F, 0x1200 - 0x12FF | (#prop-phy)
+MAC    | 0x30 - 0x3F, 0x1300 - 0x13FF | (#prop-mac)
+NET    | 0x40 - 0x4F, 0x1400 - 0x14FF | (#prop-net)
+Tech   | 0x50 - 0x5F, 0x1500 - 0x15FF | Technology-specific
+IPv6   | 0x60 - 0x6F, 0x1600 - 0x16FF | (#prop-ipv6)
+Stream | 0x70 - 0x7F, 0x1700 - 0x17FF | (#prop-core)
+Debug  |              0x4000 - 0x4400 | (#prop-debug)
+
+Note that some of the property sections have two reserved
+ranges: a primary range (which is encoded as a single byte)
+and an extended range (which is encoded as two bytes).
+properties which are used more frequently are generally
+allocated from the former range.
+
+{{spinel-prop-core.md}}
+
+{{spinel-prop-phy.md}}
+
+{{spinel-prop-mac.md}}
+
+{{spinel-prop-net.md}}
+
+{{spinel-prop-ipv6.md}}
+
+{{spinel-prop-debug.md}}
diff --git a/doc/spinel-protocol-src/spinel-security-considerations.md b/doc/spinel-protocol-src/spinel-security-considerations.md
new file mode 100644
index 0000000..574ec1a
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-security-considerations.md
@@ -0,0 +1,9 @@
+# Security Considerations #
+
+## Raw Application Access ##
+
+Spinel **MAY** be used as an API boundary for allowing processes to configure
+the NCP. However, such a system **MUST NOT** give unprivileged processess the
+ability to send or receive arbitrary command frames to the NCP. Only the
+specific commands and properties that are required should be allowed to be
+passed, and then only after being checked for proper format.
diff --git a/doc/spinel-protocol-src/spinel-status-codes.md b/doc/spinel-protocol-src/spinel-status-codes.md
new file mode 100644
index 0000000..546b5ba
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-status-codes.md
@@ -0,0 +1,66 @@
+# Status Codes
+
+Status codes are sent from the NCP to the host via
+`PROP_LAST_STATUS` using the `CMD_VALUE_IS` command to indicate
+the return status of a previous command. As with any response,
+the TID field of the FLAG byte is used to correlate the response
+with the request.
+
+Note that most successfully executed commands do not indicate
+a last status of `STATUS_OK`. The usual way the NCP indicates a
+successful command is to mirror the property change back to the
+host. For example, if you do a `CMD_VALUE_SET` on `PROP_PHY_ENABLED`,
+the NCP would indicate success by responding with a `CMD_VALUE_IS`
+for `PROP_PHY_ENABLED`. If the command failed, `PROP_LAST_STATUS`
+would be emitted instead.
+
+See (#prop-last-status) for more information on `PROP_LAST_STATUS`.
+
+ *  0: `STATUS_OK`: Operation has completed successfully.
+ *  1: `STATUS_FAILURE`: Operation has failed for some undefined
+    reason.
+ *  2: `STATUS_UNIMPLEMENTED`: The given operation has not been implemented.
+ *  3: `STATUS_INVALID_ARGUMENT`: An argument to the given operation is invalid.
+ *  4: `STATUS_INVALID_STATE` : The given operation is invalid for the current
+    state of the device.
+ *  5: `STATUS_INVALID_COMMAND`: The given command is not recognized.
+ *  6: `STATUS_INVALID_INTERFACE`: The given Spinel interface is not supported.
+ *  7: `STATUS_INTERNAL_ERROR`: An internal runtime error has occurred.
+ *  8: `STATUS_SECURITY_ERROR`: A security or authentication error has occurred.
+ *  9: `STATUS_PARSE_ERROR`: An error has occurred while parsing the command.
+ *  10: `STATUS_IN_PROGRESS`: The operation is in progress and will be
+    completed asynchronously.
+ *  11: `STATUS_NOMEM`: The operation has been prevented due to memory
+    pressure.
+ *  12: `STATUS_BUSY`: The device is currently performing a mutually exclusive
+    operation.
+ *  13: `STATUS_PROP_NOT_FOUND`: The given property is not recognized.
+ *  14: `STATUS_PACKET_DROPPED`: The packet was dropped.
+ *  15: `STATUS_EMPTY`: The result of the operation is empty.
+ *  16: `STATUS_CMD_TOO_BIG`: The command was too large to fit in the internal
+    buffer.
+ *  17: `STATUS_NO_ACK`: The packet was not acknowledged.
+ *  18: `STATUS_CCA_FAILURE`: The packet was not sent due to a CCA failure.
+ *  19: `STATUS_ALREADY`: The operation is already in progress or
+    the property was already set to the given value.
+ *  20: `STATUS_ITEM_NOT_FOUND`: The given item could not be found in the property. 
+ *  21: `STATUS_INVALID_COMMAND_FOR_PROP`: The given command cannot be performed
+    on this property.
+ *  22-111: RESERVED
+ *  112-127: Reset Causes
+     *  112: `STATUS_RESET_POWER_ON`
+     *  113: `STATUS_RESET_EXTERNAL`
+     *  114: `STATUS_RESET_SOFTWARE`
+     *  115: `STATUS_RESET_FAULT`
+     *  116: `STATUS_RESET_CRASH`
+     *  117: `STATUS_RESET_ASSERT`
+     *  118: `STATUS_RESET_OTHER`
+     *  119: `STATUS_RESET_UNKNOWN`
+     *  120: `STATUS_RESET_WATCHDOG`
+     *  121-127: RESERVED-RESET-CODES
+ *  128 - 15,359: UNALLOCATED
+ *  15,360 - 16,383: Vendor-specific
+ *  16,384 - 1,999,999: UNALLOCATED
+ *  2,000,000 - 2,097,151: Experimental Use Only (MUST NEVER be used
+    in production!)
+
diff --git a/doc/spinel-protocol-src/spinel-tech-thread.md b/doc/spinel-protocol-src/spinel-tech-thread.md
new file mode 100644
index 0000000..f410fd0
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-tech-thread.md
@@ -0,0 +1,333 @@
+# Technology: Thread(R) {#tech-thread}
+
+This section describes all of the properties and semantics required
+for managing a Thread(R) NCP.
+
+Thread(R) NCPs have the following requirements:
+
+* The property `PROP_INTERFACE_TYPE` must be 3.
+* The non-optional properties in the following sections **MUST** be
+  implemented: CORE, PHY, MAC, NET, and IPV6.
+
+All serious implementations of an NCP **SHOULD** also support the network
+save feature (See (#feature-network-save)).
+
+## Capabilities {#thread-caps}
+
+The Thread(R) technology defines the following capabilities:
+
+* `CAP_NET_THREAD_1_0` - Indicates that the NCP implements v1.0 of the Thread(R) standard.
+* `CAP_NET_THREAD_1_1` - Indicates that the NCP implements v1.1 of the Thread(R) standard.
+
+## Properties {#thread-properties}
+
+Properties for Thread(R) are allocated out of the `Tech` property
+section (see (#property-sections)).
+
+### PROP 80: PROP_THREAD_LEADER_ADDR
+* Type: Read-Only
+* Packed-Encoding: `6`
+
+The IPv6 address of the leader. (Note: May change to long and short address of leader)
+
+### PROP 81: PROP_THREAD_PARENT
+* Type: Read-Only
+* Packed-Encoding: `ES`
+* LADDR, SADDR
+
+The long address and short address of the parent of this node.
+
+### PROP 82: PROP_THREAD_CHILD_TABLE
+* Type: Read-Only
+* Packed-Encoding: `A(t(ES))`
+
+Table containing the long and short addresses of all
+the children of this node.
+
+### PROP 83: PROP_THREAD_LEADER_RID
+* Type: Read-Only
+* Packed-Encoding: `C`
+
+The router-id of the current leader.
+
+### PROP 84: PROP_THREAD_LEADER_WEIGHT
+* Type: Read-Only
+* Packed-Encoding: `C`
+
+The leader weight of the current leader.
+
+### PROP 85: PROP_THREAD_LOCAL_LEADER_WEIGHT
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+The leader weight for this node.
+
+### PROP 86: PROP_THREAD_NETWORK_DATA
+* Type: Read-Only
+* Packed-Encoding: `D`
+
+The local network data.
+
+### PROP 87: PROP_THREAD_NETWORK_DATA_VERSION
+* Type: Read-Only
+* Packed-Encoding: `S`
+
+### PROP 88: PROP_THREAD_STABLE_NETWORK_DATA
+* Type: Read-Only
+* Packed-Encoding: `D`
+
+The local stable network data.
+
+### PROP 89: PROP_THREAD_STABLE_NETWORK_DATA_VERSION
+* Type: Read-Only
+* Packed-Encoding: `S`
+
+### PROP 90: PROP_THREAD_ON_MESH_NETS
+* Type: Read-Write
+* Packed-Encoding: `A(t(6CbCb))`
+
+Data per item is:
+
+* `6`: IPv6 Prefix
+* `C`: Prefix length in bits
+* `b`: Stable flag
+* `C`: TLV flags
+* `b`: "Is defined locally" flag. Set if this network was locally
+  defined. Assumed to be true for set, insert and replace. Clear if
+  the on mesh network was defined by another node.
+
+### PROP 91: PROP_THREAD_OFF_MESH_ROUTES
+* Type: Read-Write
+* Packed-Encoding: `A(t(6CbCbb))`
+
+Data per item is:
+
+* `6`: Route Prefix
+* `C`: Prefix length in bits
+* `b`: Stable flag
+* `C`: Route preference flags
+* `b`: "Is defined locally" flag. Set if this route info was locally
+  defined as part of local network data. Assumed to be true for set,
+  insert and replace. Clear if the route is part of partition's network
+  data.
+* `b`: "Next hop is this device" flag. Set if the next hop for the
+  route is this device itself (i.e., route was added by this device)
+  This value is ignored when adding an external route. For any added
+  route the next hop is this device.
+
+### PROP 92: PROP_THREAD_ASSISTING_PORTS
+* Type: Read-Write
+* Packed-Encoding: `A(S)`
+
+### PROP 93: PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Set to true before changing local net data. Set to false when finished.
+This allows changes to be aggregated into single events.
+
+### PROP 94: PROP_THREAD_MODE
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+This property contains the value of the mode
+TLV for this node. The meaning of the bits in this
+bitfield are defined by section 4.5.2 of the Thread(R)
+specification.
+
+### PROP 5376: PROP_THREAD_CHILD_TIMEOUT
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+Used when operating in the Child role.
+
+### PROP 5377: PROP_THREAD_RLOC16
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+### PROP 5378: PROP_THREAD_ROUTER_UPGRADE_THRESHOLD
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+### PROP 5379: PROP_THREAD_CONTEXT_REUSE_DELAY
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+### PROP 5380: PROP_THREAD_NETWORK_ID_TIMEOUT
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Allows you to get or set the Thread(R) `NETWORK_ID_TIMEOUT` constant, as
+defined by the Thread(R) specification.
+
+### PROP 5381: PROP_THREAD_ACTIVE_ROUTER_IDS
+* Type: Read-Write/Write-Only
+* Packed-Encoding: `A(C)` (List of active thread router ids)
+
+Note that some implementations may not support `CMD_GET_VALUE`
+router ids, but may support `CMD_REMOVE_VALUE` when the node is
+a leader.
+
+### PROP 5382: PROP_THREAD_RLOC16_DEBUG_PASSTHRU
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Allow the HOST to directly observe all IPv6 packets received by the NCP,
+including ones sent to the RLOC16 address.
+
+Default value is `false`.
+
+### PROP 5383: PROP_THREAD_ROUTER_ROLE_ENABLED
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Allow the HOST to indicate whether or not the router role is enabled.
+If current role is a router, setting this property to `false` starts
+a re-attach process as an end-device.
+
+### PROP 5384: PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+### PROP 5385: PROP_THREAD_ROUTER_SELECTION_JITTER
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Specifies the self imposed random delay in seconds a REED waits before
+registering to become an Active Router.
+
+### PROP 5386: PROP_THREAD_PREFERRED_ROUTER_ID
+* Type: Write-Only
+* Packed-Encoding: `C`
+
+Specifies the preferred Router Id. Upon becoming a router/leader the node
+attempts to use this Router Id. If the preferred Router Id is not set or
+if it can not be used, a randomly generated router id is picked. This
+property can be set only when the device role is either detached or
+disabled.
+
+### PROP 5387: PROP_THREAD_NEIGHBOR_TABLE
+* Type: Read-Only
+* Packed-Encoding: `A(t(ESLCcCbLL))`
+
+Data per item is:
+
+* `E`: Extended/long address
+* `S`: RLOC16
+* `L`: Age
+* `C`: Link Quality In
+* `c`: Average RSS
+* `C`: Mode (bit-flags)
+* `b`: `true` if neighbor is a child, `false` otherwise.
+* `L`: Link Frame Counter
+* `L`: MLE Frame Counter
+
+### PROP 5388: PROP_THREAD_CHILD_COUNT_MAX
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Specifies the maximum number of children currently allowed.
+This parameter can only be set when Thread(R) protocol operation
+has been stopped.
+
+### PROP 5389: PROP_THREAD_LEADER_NETWORK_DATA
+* Type: Read-Only
+* Packed-Encoding: `D`
+
+The leader network data.
+
+### PROP 5390: PROP_THREAD_STABLE_LEADER_NETWORK_DATA
+* Type: Read-Only
+* Packed-Encoding: `D`
+
+The stable leader network data.
+
+### PROP 5391: PROP_THREAD_JOINERS {#prop-thread-joiners}
+
+* Type: Insert/Remove Only (optionally Read-Write)
+* Packed-Encoding: `A(t(ULE))`
+* Required capability: `CAP_THREAD_COMMISSIONER`
+
+Data per item is:
+
+* `U`: PSKd
+* `L`: Timeout in seconds
+* `E`: Extended/long address (optional)
+
+Passess Pre-Shared Key for the Device to the NCP in the commissioning process.
+When the Extended address is ommited all Devices which provided a valid PSKd
+are allowed to join the Thread(R) Network.
+
+### PROP 5392: PROP_THREAD_COMMISSIONER_ENABLED {#prop-thread-commissioner-enabled}
+
+* Type: Write only (optionally Read-Write)
+* Packed-Encoding: `b`
+* Required capability: `CAP_THREAD_COMMISSIONER`
+
+Set to true to enable the native commissioner. It is mandatory before adding the joiner to the network.
+
+### PROP 5393: PROP_THREAD_TMF_PROXY_ENABLED {#prop-thread-tmf-proxy-enabled}
+
+* Type: Read-Write
+* Packed-Encoding: `b`
+* Required capability: `CAP_THREAD_TMF_PROXY`
+
+Set to true to enable the TMF proxy.
+
+### PROP 5394: PROP_THREAD_TMF_PROXY_STREAM {#prop-thread-tmf-proxy-stream}
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `dSS`
+* Required capability: `CAP_THREAD_TMF_PROXY`
+
+Data per item is:
+
+* `d`: CoAP frame
+* `S`: source/destination RLOC/ALOC
+* `S`: source/destination port
+
+Octects: | 2      | *n*  |    2    |  2
+---------|--------|------|---------|-------
+Fields:  | Length | CoAP | locator | port
+
+This property allows the host to send and receive TMF messages from
+the NCP's RLOC address and support Thread-specific border router functions.
+
+
+### PROP 5395: PROP_THREAD_DISOVERY_SCAN_JOINER_FLAG {#prop-thread-discovery-scan-joiner-flag}
+
+* Type: Read-Write
+* Packed-Encoding:: `b`
+
+This property specifies the value used in Thread(R) MLE Discovery Request
+TLV during discovery scan operation. Default value is `false`.
+
+### PROP 5396: PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING {#prop-thread-discovery-scan-enable-filtering}
+
+* Type: Read-Write
+* Packed-Encoding:: `b`
+
+This property is used to enable/disable EUI64 filtering during discovery
+scan operation. Default value is `false`.
+
+### PROP 5397: PROP_THREAD_DISCOVERY_SCAN_PANID {#prop-thread-discovery-scan-panid}
+
+* Type: Read-write
+* Packed-Encoding:: `S`
+
+This property specifies the PANID used for filtering during discovery
+scan operation. Default value is `0xffff` (broadcast PANID) which disables
+PANID filtering.
+
+### PROP 5398: PROP_THREAD_STEERING_DATA {#prop-thread-steering-data}
+
+* Type: Write-Only
+* Packed-Encoding: `E`
+* Required capability: `CAP_OOB_STEERING_DATA`
+
+This property can be used to set the steering data for MLE Discovery
+Response messages.
+
+* All zeros to clear the steering data (indicating no steering data).
+* All 0xFFs to set the steering data (bloom filter) to accept/allow all.
+* A specific EUI64 which is then added to steering data/bloom filter.
diff --git a/doc/spinel-protocol-src/spinel-test-vectors.md b/doc/spinel-protocol-src/spinel-test-vectors.md
new file mode 100644
index 0000000..a2b9e06
--- /dev/null
+++ b/doc/spinel-protocol-src/spinel-test-vectors.md
@@ -0,0 +1,174 @@
+# Test Vectors
+
+## Test Vector: Packed Unsigned Integer
+
+Decimal Value | Packet Octet Encoding
+-------------:|:----------------------
+            0 | `00`
+            1 | `01`
+          127 | `7F`
+          128 | `80 01`
+          129 | `81 01`
+        1,337 | `B9 0A`
+       16,383 | `FF 7F`
+       16,384 | `80 80 01`
+       16,385 | `81 80 01`
+    2,097,151 | `FF FF 7F`
+
+<!-- RQ -- The PUI test-vector encodings need to be verified. -->
+
+## Test Vector: Reset Command
+
+* NLI: 0
+* TID: 0
+* CMD: 1 (`CMD_RESET`)
+
+Frame:
+
+    80 01
+
+## Test Vector: Reset Notification
+
+* NLI: 0
+* TID: 0
+* CMD: 6 (`CMD_VALUE_IS`)
+* PROP: 0 (`PROP_LAST_STATUS`)
+* VALUE: 114 (`STATUS_RESET_SOFTWARE`)
+
+Frame:
+
+    80 06 00 72
+
+## Test Vector: Scan Beacon
+
+* NLI: 0
+* TID: 0
+* CMD: 7 (`CMD_VALUE_INSERTED`)
+* PROP: 51 (`PROP_MAC_SCAN_BEACON`)
+* VALUE: Structure, encoded as `Cct(ESSc)t(iCUd)`
+    * CHAN: 15
+    * RSSI: -60dBm
+    * MAC_DATA: (0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00)
+        * Long address: B6:40:D4:8C:E9:38:F9:52
+        * Short address: 0xFFFF
+        * PAN-ID: 0x04D2
+        * LQI: 0
+    * NET_DATA: (13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE EF 00 CA FE)
+        * Protocol Number: 3
+        * Flags: 0x20
+        * Network Name: `spinel`
+        * XPANID: `DE AD 00 BE EF 00 CA FE`
+
+Frame:
+
+    80 07 33 0F C4 0D 00 B6 40 D4 8C E9 38 F9 52 FF FF D2 04 00
+    13 00 03 20 73 70 69 6E 65 6C 00 08 00 DE AD 00 BE EF 00 CA
+    FE
+
+## Test Vector: Inbound IPv6 Packet
+
+CMD_VALUE_IS(PROP_STREAM_NET)
+
+<!-- RQ -- FIXME: This test vector is incomplete. -->
+
+## Test Vector: Outbound IPv6 Packet
+
+CMD_VALUE_SET(PROP_STREAM_NET)
+
+<!-- RQ -- FIXME: This test vector is incomplete. -->
+
+## Test Vector: Fetch list of on-mesh networks
+
+* NLI: 0
+* TID: 4
+* CMD: 2 (`CMD_VALUE_GET`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+
+Frame:
+
+    84 02 5A
+
+## Test Vector: Returned list of on-mesh networks
+
+* NLI: 0
+* TID: 4
+* CMD: 6 (`CMD_VALUE_IS`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+* VALUE: Array of structures, encoded as `A(t(6CbC))`
+
+IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags
+-------------|---------------|-------------|--------------
+2001:DB8:1:: | 64            | True        | ??
+2001:DB8:2:: | 64            | False       | ??
+
+Frame:
+
+    84 06 5A 13 00 20 01 0D B8 00 01 00 00 00 00 00 00 00 00 00
+    00 40 01 ?? 13 00 20 01 0D B8 00 02 00 00 00 00 00 00 00 00
+    00 00 40 00 ??
+
+<!-- TODO: This test vector is incomplete. -->
+
+## Test Vector: Adding an on-mesh network
+
+* NLI: 0
+* TID: 5
+* CMD: 4 (`CMD_VALUE_INSERT`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+* VALUE: Structure, encoded as `6CbCb`
+
+IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags
+-------------|---------------|-------------|--------------
+2001:DB8:3:: | 64            | True        | ??
+
+Frame:
+
+    85 03 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+    01 ?? 01
+
+<!-- RQ -- FIXME: This test vector is incomplete. -->
+
+## Test Vector: Insertion notification of an on-mesh network
+
+* NLI: 0
+* TID: 5
+* CMD: 7 (`CMD_VALUE_INSERTED`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+* VALUE: Structure, encoded as `6CbCb`
+
+IPv6 Prefix  | Prefix Length | Stable Flag | Other Flags
+-------------|---------------|-------------|--------------
+2001:DB8:3:: | 64            | True        | ??
+
+Frame:
+
+    85 07 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00 40
+    01 ?? 01
+
+<!-- RQ -- FIXME: This test vector is incomplete. -->
+
+## Test Vector: Removing a local on-mesh network
+
+* NLI: 0
+* TID: 6
+* CMD: 5 (`CMD_VALUE_REMOVE`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+* VALUE: IPv6 Prefix `2001:DB8:3::`
+
+Frame:
+
+    86 05 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+
+## Test Vector: Removal notification of an on-mesh network
+
+* NLI: 0
+* TID: 6
+* CMD: 8 (`CMD_VALUE_REMOVED`)
+* PROP: 90 (`PROP_THREAD_ON_MESH_NETS`)
+* VALUE: IPv6 Prefix `2001:DB8:3::`
+
+Frame:
+
+    86 08 5A 20 01 0D B8 00 03 00 00 00 00 00 00 00 00 00 00
+
+
diff --git a/etc/vagrant/Vagrantfile b/etc/vagrant/Vagrantfile
new file mode 100644
index 0000000..5ad0433
--- /dev/null
+++ b/etc/vagrant/Vagrantfile
@@ -0,0 +1,86 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# cribbed from https://github.com/adafruit/esp8266-micropython-vagrant
+Vagrant.configure("2") do |config|
+  config.vm.box = "ubuntu/trusty64"
+
+  # Virtualbox VM configuration.
+  config.vm.provider "virtualbox" do |v|
+    # extra memory for compilation
+    v.memory = 2048
+  end
+
+  # downloads and configuration dependencies
+  config.vm.provision "shell", privileged: false, inline: <<-SHELL
+    echo "Installing dependencies..."
+
+    # quiets some stdin errors
+    export DEBIAN_FRONTEND=noninteractive
+
+    sudo apt-get install -y python-software-properties
+    sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded
+    sudo apt-get update -qq
+
+    # wpandtund runtime & build requirements
+    sudo apt-get install -y build-essential git make autoconf autoconf-archive \
+                            automake dbus libtool gcc g++ gperf flex bison texinfo \
+                            ncurses-dev libexpat-dev python sed python-pip gawk \
+                            libreadline6-dev libreadline6 libdbus-1-dev libboost-dev
+    sudo apt-get install -y --force-yes gcc-arm-none-eabi
+
+    sudo pip install pexpect
+
+    echo "Installing OpenThread & wpandtund..."
+    mkdir -p ~/src
+
+    # install wpantund
+    cd ~/src
+    echo "installing wpantund"
+    git clone --recursive https://github.com/openthread/wpantund.git
+    cd wpantund
+    sudo git checkout full/master
+    ./configure --sysconfdir=/etc
+    make
+    sudo make install
+    # dbus sometimes is wonky; forcing restart
+    sudo service dbus restart
+
+    # install OpenThread
+    cd ~/src
+    git clone --recursive https://github.com/openthread/openthread.git
+    cd openthread
+    ./bootstrap
+
+    echo "OpenThread and wpantund setup complete! Examples can be found in ~/src/openthread/examples"
+  SHELL
+
+end
diff --git a/etc/visual-studio/Assets/LockScreenLogo.scale-200.png b/etc/visual-studio/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000..2dedc71
--- /dev/null
+++ b/etc/visual-studio/Assets/LockScreenLogo.scale-200.png
Binary files differ
diff --git a/etc/visual-studio/Assets/SplashScreen.scale-200.png b/etc/visual-studio/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000..af038b0
--- /dev/null
+++ b/etc/visual-studio/Assets/SplashScreen.scale-200.png
Binary files differ
diff --git a/etc/visual-studio/Assets/Square150x150Logo.scale-200.png b/etc/visual-studio/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..5ae239d
--- /dev/null
+++ b/etc/visual-studio/Assets/Square150x150Logo.scale-200.png
Binary files differ
diff --git a/etc/visual-studio/Assets/Square44x44Logo.scale-200.png b/etc/visual-studio/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..4e020df
--- /dev/null
+++ b/etc/visual-studio/Assets/Square44x44Logo.scale-200.png
Binary files differ
diff --git a/etc/visual-studio/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/etc/visual-studio/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..8b6f2df
--- /dev/null
+++ b/etc/visual-studio/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
Binary files differ
diff --git a/etc/visual-studio/Assets/StoreLogo.png b/etc/visual-studio/Assets/StoreLogo.png
new file mode 100644
index 0000000..7fa37c2
--- /dev/null
+++ b/etc/visual-studio/Assets/StoreLogo.png
Binary files differ
diff --git a/etc/visual-studio/Assets/Wide310x150Logo.scale-200.png b/etc/visual-studio/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..25b80bb
--- /dev/null
+++ b/etc/visual-studio/Assets/Wide310x150Logo.scale-200.png
Binary files differ
diff --git a/etc/visual-studio/OpenThread.vcxproj b/etc/visual-studio/OpenThread.vcxproj
new file mode 100644
index 0000000..021279d
--- /dev/null
+++ b/etc/visual-studio/OpenThread.vcxproj
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{f8c22844-9b93-4978-80df-8af2b37a7abb}</ProjectGuid>
+    <RootNamespace>ot</RootNamespace>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <AppContainerApplication>true</AppContainerApplication>
+    <ApplicationType>Windows Store</ApplicationType>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <PlatformToolset>v140</PlatformToolset>
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <PackageCertificateKeyFile>..\..\examples\apps\windows\OpenThread_TemporaryKey.pfx</PackageCertificateKeyFile>
+  </PropertyGroup>
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\app\</OutDir>
+    <AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
+    <AppxPackageDir>..\..\build\bin\AppPackages\</AppxPackageDir>
+    <AppxBundle>Never</AppxBundle>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\apps\windows;
+        ..\..\include;
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>
+        %(AdditionalDependencies);
+        ..\..\build\bin\$(Platform)\$(Configuration)\dll\otApi.lib;
+      </AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\apps\windows\ClientArgs.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ClientControl.xaml.h">
+      <DependentUpon>..\..\examples\apps\windows\ClientControl.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\apps\windows\DatagramClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\DatagramListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\Factory.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\Protocol.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IAsyncThreadNotify.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IMainPageUIElements.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ListenerArgs.h" />
+    <ClInclude Include="..\..\examples\apps\windows\otAdapter.h" />
+    <ClInclude Include="..\..\examples\apps\windows\otApi.h" />
+    <ClInclude Include="..\..\examples\apps\windows\pch.h" />
+    <ClInclude Include="..\..\examples\apps\windows\App.xaml.h">
+      <DependentUpon>..\..\examples\apps\windows\App.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\apps\windows\MainPage.xaml.h">
+      <DependentUpon>..\..\examples\apps\windows\MainPage.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\apps\windows\ServerControl.xaml.h">
+      <DependentUpon>..\..\examples\apps\windows\ServerControl.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\apps\windows\StreamClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\StreamListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\TalkConsts.h" />
+    <ClInclude Include="..\..\examples\apps\windows\TalkGrid.xaml.h">
+      <DependentUpon>..\..\examples\apps\windows\TalkGrid.xaml</DependentUpon>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\apps\windows\TalkHelper.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="..\..\examples\apps\windows\App.xaml">
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Page Include="..\..\examples\apps\windows\ClientControl.xaml">
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\..\examples\apps\windows\MainPage.xaml">
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\..\examples\apps\windows\ServerControl.xaml">
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="..\..\examples\apps\windows\TalkGrid.xaml">
+      <SubType>Designer</SubType>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <AppxManifest Include="..\..\examples\apps\windows\Package.appxmanifest">
+      <SubType>Designer</SubType>
+    </AppxManifest>
+    <None Include="..\..\examples\apps\windows\OpenThread_TemporaryKey.pfx" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="Assets\LockScreenLogo.scale-200.png" />
+    <Image Include="Assets\SplashScreen.scale-200.png" />
+    <Image Include="Assets\Square150x150Logo.scale-200.png" />
+    <Image Include="Assets\Square44x44Logo.scale-200.png" />
+    <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
+    <Image Include="Assets\StoreLogo.png" />
+    <Image Include="Assets\Wide310x150Logo.scale-200.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\windows\App.xaml.cpp">
+      <DependentUpon>..\..\examples\apps\windows\App.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\apps\windows\ClientControl.xaml.cpp">
+      <DependentUpon>..\..\examples\apps\windows\ClientControl.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\apps\windows\DatagramClientContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\DatagramListenerContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\Factory.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\MainPage.xaml.cpp">
+      <DependentUpon>..\..\examples\apps\windows\MainPage.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\apps\windows\pch.cpp">
+      <PrecompiledHeader>Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\apps\windows\ServerControl.xaml.cpp">
+      <DependentUpon>..\..\examples\apps\windows\ServerControl.xaml</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\apps\windows\StreamClientContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\StreamListenerContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\TalkGrid.xaml.cpp">
+      <DependentUpon>..\..\examples\apps\windows\TalkGrid.xaml</DependentUpon>
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/OpenThread.vcxproj.filters b/etc/visual-studio/OpenThread.vcxproj.filters
new file mode 100644
index 0000000..5fb4315
--- /dev/null
+++ b/etc/visual-studio/OpenThread.vcxproj.filters
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Common">
+      <UniqueIdentifier>f8c22844-9b93-4978-80df-8af2b37a7abb</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Assets">
+      <UniqueIdentifier>cf539bf1-44af-4a06-be13-5941f6ac2623</UniqueIdentifier>
+      <Extensions>bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="..\..\examples\apps\windows\App.xaml" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\windows\App.xaml.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\MainPage.xaml.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\pch.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\Factory.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\ClientControl.xaml.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\ServerControl.xaml.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\TalkGrid.xaml.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\DatagramClientContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\DatagramListenerContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\StreamClientContext.cpp" />
+    <ClCompile Include="..\..\examples\apps\windows\StreamListenerContext.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\apps\windows\pch.h" />
+    <ClInclude Include="..\..\examples\apps\windows\App.xaml.h" />
+    <ClInclude Include="..\..\examples\apps\windows\MainPage.xaml.h" />
+    <ClInclude Include="..\..\examples\apps\windows\otAdapter.h" />
+    <ClInclude Include="..\..\examples\apps\windows\otApi.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IAsyncThreadNotify.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IMainPageUIElements.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ListenerArgs.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ClientArgs.h" />
+    <ClInclude Include="..\..\examples\apps\windows\Protocol.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\Factory.h" />
+    <ClInclude Include="..\..\examples\apps\windows\IClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ClientControl.xaml.h" />
+    <ClInclude Include="..\..\examples\apps\windows\ServerControl.xaml.h" />
+    <ClInclude Include="..\..\examples\apps\windows\TalkGrid.xaml.h" />
+    <ClInclude Include="..\..\examples\apps\windows\DatagramClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\DatagramListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\StreamClientContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\StreamListenerContext.h" />
+    <ClInclude Include="..\..\examples\apps\windows\TalkConsts.h" />
+    <ClInclude Include="..\..\examples\apps\windows\TalkHelper.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="Assets\LockScreenLogo.scale-200.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\SplashScreen.scale-200.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\Square150x150Logo.scale-200.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\Square44x44Logo.scale-200.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\StoreLogo.png">
+      <Filter>Assets</Filter>
+    </Image>
+    <Image Include="Assets\Wide310x150Logo.scale-200.png">
+      <Filter>Assets</Filter>
+    </Image>
+  </ItemGroup>
+  <ItemGroup>
+    <AppxManifest Include="..\..\examples\apps\windows\Package.appxmanifest" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\examples\apps\windows\OpenThread_TemporaryKey.pfx" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="..\..\examples\apps\windows\MainPage.xaml" />
+    <Page Include="..\..\examples\apps\windows\TalkGrid.xaml" />
+    <Page Include="..\..\examples\apps\windows\ClientControl.xaml" />
+    <Page Include="..\..\examples\apps\windows\ServerControl.xaml" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj
new file mode 100644
index 0000000..f8a6448
--- /dev/null
+++ b/etc/visual-studio/UnitTests.vcxproj
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{FD64BF17-8D36-4578-8D13-77B123BE30D3}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\dll\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        $(VCInstallDir)UnitTest\include;
+        ..\..\include;
+        ..\..\src;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+      </AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions)
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+      </PreprocessorDefinitions>
+      <WarningLevel>Level3</WarningLevel>
+      <UseFullPaths>true</UseFullPaths>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <AdditionalLibraryDirectories>
+        %(AdditionalLibraryDirectories);
+        $(VCInstallDir)UnitTest\lib;
+        </AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tests\unit\test_aes.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_fuzz.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_hmac_sha256.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_link_quality.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_lowpan.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_mac_frame.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_message.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_message_queue.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_ncp_buffer.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_platform.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_priority_queue.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_timer.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_toolchain_c.c" />
+    <ClCompile Include="..\..\tests\unit\test_toolchain.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_util.cpp" />
+    <ClCompile Include="..\..\tests\unit\test_windows.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tests\unit\test_platform.h" />
+    <ClInclude Include="..\..\tests\unit\test_lowpan.hpp" />
+    <ClInclude Include="..\..\tests\unit\test_util.h" />
+    <ClInclude Include="..\..\tests\unit\test_util.hpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread-ncp-uart.vcxproj">
+      <Project>{d94867d2-6dae-47e2-962a-5e8e658134d1}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread.vcxproj">
+      <Project>{dd5018be-54c6-4fd4-9f8d-08d52fc0cd40}</Project>
+    </ProjectReference>
+    <ProjectReference Include="mbedtls.vcxproj">
+      <Project>{4111c8bb-d354-4348-ad3c-eb6832e84831}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/etc/visual-studio/UnitTests.vcxproj.filters b/etc/visual-studio/UnitTests.vcxproj.filters
new file mode 100644
index 0000000..042dda0
--- /dev/null
+++ b/etc/visual-studio/UnitTests.vcxproj.filters
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tests\unit\test_aes.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_fuzz.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_hmac_sha256.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_link_quality.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_lowpan.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_mac_frame.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_message.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_message_queue.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_priority_queue.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_timer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_toolchain.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_util.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_windows.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_platform.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\unit\test_ncp_buffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tests\unit\test_platform.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tests\unit\test_lowpan.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tests\unit\test_util.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tests\unit\test_util.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread-cli-windows.vcxproj b/etc/visual-studio/libopenthread-cli-windows.vcxproj
new file mode 100644
index 0000000..818e285
--- /dev/null
+++ b/etc/visual-studio/libopenthread-cli-windows.vcxproj
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{28834498-3837-44A5-8F67-249ABAB7B97D}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OTDLL;
+        OTBUILD;
+        OPENTHREAD_FTD=1;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\cli\cli.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_dataset.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_instance.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_uart.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters b/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters
new file mode 100644
index 0000000..d0ba0e9
--- /dev/null
+++ b/etc/visual-studio/libopenthread-cli-windows.vcxproj.filters
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\cli\cli.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_uart.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_dataset.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_instance.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-cli.vcxproj b/etc/visual-studio/libopenthread-cli.vcxproj
new file mode 100644
index 0000000..8120e9f
--- /dev/null
+++ b/etc/visual-studio/libopenthread-cli.vcxproj
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{41B32069-632E-4578-855B-A36EBFA80B56}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\cli\cli.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_dataset.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_uart.cpp" />
+    <ClCompile Include="..\..\src\cli\cli_udp.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-cli.vcxproj.filters b/etc/visual-studio/libopenthread-cli.vcxproj.filters
new file mode 100644
index 0000000..cf6c8a3
--- /dev/null
+++ b/etc/visual-studio/libopenthread-cli.vcxproj.filters
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\cli\cli.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_udp.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_uart.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\cli\cli_dataset.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-ncp-spi.vcxproj b/etc/visual-studio/libopenthread-ncp-spi.vcxproj
new file mode 100644
index 0000000..2b01424
--- /dev/null
+++ b/etc/visual-studio/libopenthread-ncp-spi.vcxproj
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OPENTHREAD_ENABLE_NCP_SPI=1;
+        OPENTHREAD_ENABLE_NCP_UART=0;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\ncp_base.cpp" />
+    <ClCompile Include="..\..\src\ncp\ncp_buffer.cpp" />
+    <ClCompile Include="..\..\src\ncp\ncp_spi.cpp" />
+    <ClCompile Include="..\..\src\ncp\spinel.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\ncp_base.hpp" />
+    <ClInclude Include="..\..\src\ncp\ncp_buffer.hpp" />
+    <ClInclude Include="..\..\src\ncp\ncp_spi.hpp" />
+    <ClInclude Include="..\..\src\ncp\spinel.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-ncp-spi.vcxproj.filters b/etc/visual-studio/libopenthread-ncp-spi.vcxproj.filters
new file mode 100644
index 0000000..a2f8547
--- /dev/null
+++ b/etc/visual-studio/libopenthread-ncp-spi.vcxproj.filters
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\ncp_base.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\ncp_buffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\spinel.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\ncp_spi.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\ncp_base.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\ncp_buffer.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\ncp_spi.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\spinel.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-ncp-uart.vcxproj b/etc/visual-studio/libopenthread-ncp-uart.vcxproj
new file mode 100644
index 0000000..e7c5974
--- /dev/null
+++ b/etc/visual-studio/libopenthread-ncp-uart.vcxproj
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D94867D2-6DAE-47E2-962A-5E8E658134D1}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OPENTHREAD_ENABLE_NCP_SPI=0;
+        OPENTHREAD_ENABLE_NCP_UART=1;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\hdlc.cpp" />
+    <ClCompile Include="..\..\src\ncp\ncp_base.cpp" />
+    <ClCompile Include="..\..\src\ncp\ncp_buffer.cpp" />
+    <ClCompile Include="..\..\src\ncp\ncp_uart.cpp" />
+    <ClCompile Include="..\..\src\ncp\spinel.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\hdlc.hpp" />
+    <ClInclude Include="..\..\src\ncp\ncp_base.hpp" />
+    <ClInclude Include="..\..\src\ncp\ncp_buffer.hpp" />
+    <ClInclude Include="..\..\src\ncp\ncp_uart.hpp" />
+    <ClInclude Include="..\..\src\ncp\spinel.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread-ncp-uart.vcxproj.filters b/etc/visual-studio/libopenthread-ncp-uart.vcxproj.filters
new file mode 100644
index 0000000..ff54433
--- /dev/null
+++ b/etc/visual-studio/libopenthread-ncp-uart.vcxproj.filters
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\hdlc.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\ncp_base.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\ncp_buffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\ncp_uart.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ncp\spinel.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\hdlc.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\ncp_base.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\ncp_buffer.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\ncp_uart.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ncp\spinel.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread-windows.vcxproj b/etc/visual-studio/libopenthread-windows.vcxproj
new file mode 100644
index 0000000..07a1c0a
--- /dev/null
+++ b/etc/visual-studio/libopenthread-windows.vcxproj
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" /> 
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{30723C38-BA3B-44C9-8D64-C5861A26934F}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        _CRT_SECURE_NO_WARNINGS;
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\examples\platforms;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\platforms\posix\alarm.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\flash-windows-stubs.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\logging.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\misc.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\platform.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\radio.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\random.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\spi-stubs.c" />
+    <ClCompile Include="..\..\examples\platforms\posix\uart-windows.c" />
+    <ClCompile Include="..\..\examples\platforms\utils\settings.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\platforms\posix\platform-posix.h" />
+    <ClInclude Include="..\..\examples\platforms\utils\flash.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/libopenthread-windows.vcxproj.filters b/etc/visual-studio/libopenthread-windows.vcxproj.filters
new file mode 100644
index 0000000..440eca8
--- /dev/null
+++ b/etc/visual-studio/libopenthread-windows.vcxproj.filters
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{af2a0e04-134d-40b3-84d4-82c8c622f9ae}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\platforms\posix\alarm.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\flash-windows-stubs.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\logging.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\misc.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\platform.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\random.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\radio.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\spi-stubs.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\posix\uart-windows.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\platforms\utils\settings.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\platforms\posix\platform-posix.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\platforms\utils\flash.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj
new file mode 100644
index 0000000..e1e702a
--- /dev/null
+++ b/etc/visual-studio/libopenthread.vcxproj
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}</ProjectGuid>
+    <Keyword>StaticLibrary</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\core\api\commissioner_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\border_router_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dataset_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dataset_ftd_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dhcp6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\icmp6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\ip6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\instance_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\joiner_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\link_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\link_raw_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\message_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\netdata_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\tasklet_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\thread_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\thread_ftd_api.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap_header.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap_secure.cpp" />
+    <ClCompile Include="..\..\src\core\common\crc16.cpp" />
+    <ClCompile Include="..\..\src\core\common\locator.cpp" />
+    <ClCompile Include="..\..\src\core\common\logging.cpp" />
+    <ClCompile Include="..\..\src\core\common\message.cpp" />
+    <ClCompile Include="..\..\src\core\common\tasklet.cpp" />
+    <ClCompile Include="..\..\src\core\common\timer.cpp" />
+    <ClCompile Include="..\..\src\core\common\tlvs.cpp" />
+    <ClCompile Include="..\..\src\core\common\trickle_timer.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\aes_ccm.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\aes_ecb.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\hmac_sha256.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\mbedtls.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\pbkdf2_cmac.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\sha256.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_blacklist.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_frame.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_whitelist.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\announce_begin_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\commissioner.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_local.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager_ftd.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dtls.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\energy_scan_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\joiner.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\joiner_router.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\leader.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\panid_query_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\timestamp.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\meshcop_tlvs.cpp" />
+    <ClCompile Include="..\..\src\core\net\dhcp6_client.cpp" />
+    <ClCompile Include="..\..\src\core\net\dhcp6_server.cpp" />
+    <ClCompile Include="..\..\src\core\net\icmp6.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_address.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_filter.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_mpl.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_routes.cpp" />
+    <ClCompile Include="..\..\src\core\net\netif.cpp" />
+    <ClCompile Include="..\..\src\core\net\udp6.cpp" />
+    <ClCompile Include="..\..\src\core\thread\address_resolver.cpp" />
+    <ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp" />
+    <ClCompile Include="..\..\src\core\thread\key_manager.cpp" />
+    <ClCompile Include="..\..\src\core\thread\link_quality.cpp" />
+    <ClCompile Include="..\..\src\core\thread\lowpan.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mesh_forwarder.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mle.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mle_router.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_leader.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_leader_ftd.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_local.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_diagnostic.cpp" />
+    <ClCompile Include="..\..\src\core\thread\panid_query_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\src_match_controller.cpp" />
+    <ClCompile Include="..\..\src\core\thread\thread_netif.cpp" />
+    <ClCompile Include="..\..\src\core\thread\topology.cpp" />
+    <ClCompile Include="..\..\src\core\utils\child_supervision.cpp" />
+    <ClCompile Include="..\..\src\core\utils\slaac_address.cpp" />
+    <ClCompile Include="..\..\src\core\utils\jam_detector.cpp" />
+    <ClCompile Include="..\..\src\core\utils\missing_strnlen.c" />
+    <ClCompile Include="..\..\src\core\utils\missing_strlcat.c" />
+    <ClCompile Include="..\..\src\core\utils\missing_strlcpy.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\include\openthread\link_raw.h" />
+    <ClInclude Include="..\..\src\core\coap\coap.hpp" />
+    <ClInclude Include="..\..\src\core\coap\coap_header.hpp" />
+    <ClInclude Include="..\..\src\core\coap\coap_secure.hpp" />
+    <ClInclude Include="..\..\src\core\common\code_utils.hpp" />
+    <ClInclude Include="..\..\src\core\common\crc16.hpp" />
+    <ClInclude Include="..\..\src\core\common\context.hpp" />
+    <ClInclude Include="..\..\src\core\common\debug.hpp" />
+    <ClInclude Include="..\..\src\core\common\encoding.hpp" />
+    <ClInclude Include="..\..\src\core\common\locator.hpp" />
+    <ClInclude Include="..\..\src\core\common\logging.hpp" />
+    <ClInclude Include="..\..\src\core\common\message.hpp" />
+    <ClInclude Include="..\..\src\core\common\new.hpp" />
+    <ClInclude Include="..\..\src\core\common\tasklet.hpp" />
+    <ClInclude Include="..\..\src\core\common\timer.hpp" />
+    <ClInclude Include="..\..\src\core\common\tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\common\trickle_timer.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\aes_ccm.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\aes_ecb.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\hmac_sha256.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\mbedtls.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\pbkdf2_cmac.h" />
+    <ClInclude Include="..\..\src\core\crypto\sha256.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_impl.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_stub.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_frame.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_impl.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_stub.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\commissioner.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset_local.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset_manager.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dtls.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\energy_scan_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\joiner.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\joiner_router.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\leader.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\panid_query_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\timestamp.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\net\icmp6.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_address.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_filter.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_mpl.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_routes.hpp" />
+    <ClInclude Include="..\..\src\core\net\netif.hpp" />
+    <ClInclude Include="..\..\src\core\net\socket.hpp" />
+    <ClInclude Include="..\..\src\core\net\udp6.hpp" />
+    <ClInclude Include="..\..\src\core\thread\address_resolver.hpp" />
+    <ClInclude Include="..\..\src\core\thread\announce_begin_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\energy_scan_server.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6_client.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp" />
+    <ClInclude Include="..\..\src\core\thread\key_manager.hpp" />
+    <ClInclude Include="..\..\src\core\thread\link_quality.hpp" />
+    <ClInclude Include="..\..\src\core\thread\lowpan.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mesh_forwarder.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_constants.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_router.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_leader.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_local.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\panid_query_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\src_match_controller.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_netif.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_uri_paths.hpp" />
+    <ClInclude Include="..\..\src\core\thread\topology.hpp" />
+    <ClInclude Include="..\..\src\core\utils\child_supervision.hpp" />
+    <ClInclude Include="..\..\src\core\utils\slaac_address.hpp" />
+    <ClInclude Include="..\..\src\core\utils\jam_detector.hpp" />
+    <ClInclude Include="..\..\src\core\utils\wrap_string.h" />
+    <ClInclude Include="..\..\src\core\utils\wrap_stdbool.h" />
+    <ClInclude Include="..\..\src\core\utils\wrap_stdint.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters
new file mode 100644
index 0000000..7a7335c
--- /dev/null
+++ b/etc/visual-studio/libopenthread.vcxproj.filters
@@ -0,0 +1,591 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{c09c2d1c-85fb-4081-8235-925b45ed3c4b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\api">
+      <UniqueIdentifier>{CE77AA98-FC98-4FC6-BB52-8A886378AB5E}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\common">
+      <UniqueIdentifier>{a59636b8-d046-46b7-992b-76572ca72bd1}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\coap">
+      <UniqueIdentifier>{7c989f53-480b-42d4-a2dc-454adafda818}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\crypto">
+      <UniqueIdentifier>{444ab8c6-6b83-4a5b-9059-67f469da1331}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\mac">
+      <UniqueIdentifier>{a49a9cc3-6a37-48a6-8049-b10c9cecd193}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\meshcop">
+      <UniqueIdentifier>{0e7f4f58-a115-4ce8-8e3b-1094fb79af52}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\net">
+      <UniqueIdentifier>{9a61353a-8613-4496-9ee8-c3d2800c5ac6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\thread">
+      <UniqueIdentifier>{c14a7a3f-089f-47ee-80db-1caf1b47ab6b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\utils">
+      <UniqueIdentifier>{A0D2E990-DC5E-4B56-A552-888F97214A17}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\missing">
+      <UniqueIdentifier>{F0DDA90-D13F-4B56-A552-328F97124A81}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{b4905c15-9702-4ff0-8d57-e7926337d50f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\coap">
+      <UniqueIdentifier>{c2f72132-0df1-4dba-b24f-0ab683874538}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\common">
+      <UniqueIdentifier>{92f20ce7-8f87-4a1c-bc0b-d7fa3dfd3b60}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\crypto">
+      <UniqueIdentifier>{c618ddc9-5c66-485f-a0ff-ebabb3508568}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\mac">
+      <UniqueIdentifier>{90294598-e94b-4c07-a531-93335a8955d9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\net">
+      <UniqueIdentifier>{0c76d116-0b4d-4fec-a0d8-5ca127913124}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\thread">
+      <UniqueIdentifier>{702145f9-0e66-4e45-b3d4-d43f2cf17e10}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\meshcop">
+      <UniqueIdentifier>{635aaabf-80f0-40e2-9218-22ff75bc0e32}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\utils">
+      <UniqueIdentifier>{5ac96cb9-6257-4387-9238-27649915463a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\api">
+      <UniqueIdentifier>{25a5662d-0709-4698-8fd9-3abfde8f0fc6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\missing">
+      <UniqueIdentifier>{971969f2-f6ec-50b4-806a-97965c934cef}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\core\api\commissioner_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\border_router_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dataset_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dataset_ftd_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dhcp6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\icmp6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\ip6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\instance_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\joiner_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\link_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\link_raw_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\message_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\netdata_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\tasklet_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\thread_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\thread_ftd_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap_header.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap_secure.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\locator.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\logging.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\message.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\tasklet.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\timer.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\tlvs.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\trickle_timer.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\aes_ccm.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_frame.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_whitelist.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\dhcp6_client.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\dhcp6_server.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\icmp6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_address.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_filter.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_mpl.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_routes.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\netif.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\address_resolver.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\key_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\link_quality.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\lowpan.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mesh_forwarder.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mle.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mle_router.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_leader.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_leader_ftd.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_local.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\panid_query_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\src_match_controller.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\thread_netif.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\topology.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\udp6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_blacklist.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\commissioner.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_local.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager_ftd.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dtls.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\energy_scan_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\joiner.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\joiner_router.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\leader.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\panid_query_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\timestamp.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\meshcop_tlvs.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\aes_ecb.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\hmac_sha256.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\mbedtls.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\pbkdf2_cmac.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\sha256.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\child_supervision.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\slaac_address.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\jam_detector.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\wrap_strnlen.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\wrap_strlcpy.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\wrap_strlcat.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\announce_begin_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\crc16.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_diagnostic.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\core\coap\coap.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\coap\coap_header.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\coap\coap_secure.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\code_utils.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\context.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\debug.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\encoding.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\locator.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\logging.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\message.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\new.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\tasklet.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\timer.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\tlvs.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\trickle_timer.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\aes_ccm.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_frame.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_impl.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_stub.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6_client.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6_server.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\icmp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_address.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_filter.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_mpl.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_routes.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\netif.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\socket.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\udp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\address_resolver.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\announce_begin_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\energy_scan_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\key_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\link_quality.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\lowpan.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mesh_forwarder.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_constants.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_router.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_leader.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_local.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\panid_query_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\src_match_controller.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_netif.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_uri_paths.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\topology.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_impl.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_stub.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\commissioner.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset_local.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dtls.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\energy_scan_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\joiner.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\joiner_router.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\leader.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\panid_query_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\timestamp.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\aes_ecb.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\hmac_sha256.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\mbedtls.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\pbkdf2_cmac.h">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\sha256.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\child_supervision.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\slaac_address.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\jam_detector.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrap_string.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrap_stdbool.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrap_stdint.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\crc16.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\link_raw.h">
+      <Filter>Header Files\api</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj
new file mode 100644
index 0000000..78cb04e
--- /dev/null
+++ b/etc/visual-studio/libopenthread_k.vcxproj
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{9B33C190-5D07-40BF-9536-68843DC5D7AF}</ProjectGuid>
+    <TemplateGuid>{8c0e3d8b-df43-455b-815a-4a0e72973bc6}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <Configuration>Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <RootNamespace>ot</RootNamespace>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_PROJECT_CORE_CONFIG_FILE="openthread-core-windows-config.h";
+        WINDOWS_LOGGING;
+        OPENTHREAD_FTD=1;
+        HAVE_STDBOOL_H=1;
+        HAVE_STDINT_H=1;
+        OTBUILD;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\examples\drivers\windows\include;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+        ..\..\examples\drivers\windows\include_c99;
+      </AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4100;4706;4748;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppAdditionalOptions>-km %(WppAdditionalOptions)</WppAdditionalOptions>
+      <WppModuleName>otCore</WppModuleName>
+      <WppSearchString>WPP_INIT_TRACING</WppSearchString>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\core\api\commissioner_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\border_router_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dataset_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dataset_ftd_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\dhcp6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\icmp6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\ip6_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\instance_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\joiner_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\link_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\message_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\netdata_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\tasklet_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\thread_api.cpp" />
+    <ClCompile Include="..\..\src\core\api\thread_ftd_api.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap_header.cpp" />
+    <ClCompile Include="..\..\src\core\coap\coap_secure.cpp" />
+    <ClCompile Include="..\..\src\core\common\crc16.cpp" />
+    <ClCompile Include="..\..\src\core\common\locator.cpp" />
+    <ClCompile Include="..\..\src\core\common\logging.cpp" />
+    <ClCompile Include="..\..\src\core\common\message.cpp" />
+    <ClCompile Include="..\..\src\core\common\tasklet.cpp" />
+    <ClCompile Include="..\..\src\core\common\timer.cpp" />
+    <ClCompile Include="..\..\src\core\common\tlvs.cpp" />
+    <ClCompile Include="..\..\src\core\common\trickle_timer.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\aes_ccm.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\aes_ecb.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\hmac_sha256.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\pbkdf2_cmac.cpp" />
+    <ClCompile Include="..\..\src\core\crypto\sha256.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_blacklist.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_frame.cpp" />
+    <ClCompile Include="..\..\src\core\mac\mac_whitelist.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\commissioner.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_local.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager_ftd.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\dtls.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\announce_begin_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\energy_scan_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\joiner.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\joiner_router.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\leader.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\panid_query_client.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\timestamp.cpp" />
+    <ClCompile Include="..\..\src\core\meshcop\meshcop_tlvs.cpp" />
+    <ClCompile Include="..\..\src\core\net\dhcp6_client.cpp" />
+    <ClCompile Include="..\..\src\core\net\dhcp6_server.cpp" />
+    <ClCompile Include="..\..\src\core\net\icmp6.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_address.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_filter.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_mpl.cpp" />
+    <ClCompile Include="..\..\src\core\net\ip6_routes.cpp" />
+    <ClCompile Include="..\..\src\core\net\netif.cpp" />
+    <ClCompile Include="..\..\src\core\net\udp6.cpp" />
+    <ClCompile Include="..\..\src\core\thread\address_resolver.cpp" />
+    <ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp" />
+    <ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\key_manager.cpp" />
+    <ClCompile Include="..\..\src\core\thread\link_quality.cpp" />
+    <ClCompile Include="..\..\src\core\thread\lowpan.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mesh_forwarder.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mle.cpp" />
+    <ClCompile Include="..\..\src\core\thread\mle_router.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_leader.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_leader_ftd.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_data_local.cpp" />
+    <ClCompile Include="..\..\src\core\thread\network_diagnostic.cpp" />
+    <ClCompile Include="..\..\src\core\thread\panid_query_server.cpp" />
+    <ClCompile Include="..\..\src\core\thread\src_match_controller.cpp" />
+    <ClCompile Include="..\..\src\core\thread\thread_netif.cpp" />
+    <ClCompile Include="..\..\src\core\thread\topology.cpp" />
+    <ClCompile Include="..\..\src\core\utils\child_supervision.cpp" />
+    <ClCompile Include="..\..\src\core\utils\slaac_address.cpp" />
+    <ClCompile Include="..\..\src\core\utils\jam_detector.cpp" />
+    <ClCompile Include="..\..\src\core\utils\missing_strlcat.c" />
+    <ClCompile Include="..\..\src\core\utils\missing_strlcpy.c" />
+    <ClCompile Include="..\..\src\core\utils\missing_strnlen.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\include\openthread-core-windows-config.h" />
+    <ClInclude Include="..\..\include\openthread\coap.h" />
+    <ClInclude Include="..\..\include\openthread\commissioner.h" />
+    <ClInclude Include="..\..\include\openthread\crypto.h" />
+    <ClInclude Include="..\..\include\openthread\border_router.h" />
+    <ClInclude Include="..\..\include\openthread\dataset.h" />
+    <ClInclude Include="..\..\include\openthread\dataset_ftd.h" />
+    <ClInclude Include="..\..\include\openthread\dhcp6_client.h" />
+    <ClInclude Include="..\..\include\openthread\dhcp6_server.h" />
+    <ClInclude Include="..\..\include\openthread\icmp6.h" />
+    <ClInclude Include="..\..\include\openthread\instance.h" />
+    <ClInclude Include="..\..\include\openthread\ip6.h" />
+    <ClInclude Include="..\..\include\openthread\jam_detection.h" />
+    <ClInclude Include="..\..\include\openthread\joiner.h" />
+    <ClInclude Include="..\..\include\openthread\link.h" />
+    <ClInclude Include="..\..\include\openthread\message.h" />
+    <ClInclude Include="..\..\include\openthread\netdata.h" />
+    <ClInclude Include="..\..\include\openthread\openthread.h" />
+    <ClInclude Include="..\..\include\openthread\tasklet.h" />
+    <ClInclude Include="..\..\include\openthread\thread.h" />
+    <ClInclude Include="..\..\include\openthread\thread_ftd.h" />
+    <ClInclude Include="..\..\include\openthread\types.h" />
+    <ClInclude Include="..\..\include\openthread-windows-config.h" />
+    <ClInclude Include="..\..\include\openthread\udp.h" />
+    <ClInclude Include="..\..\src\core\api\link_raw.hpp" />
+    <ClInclude Include="..\..\src\core\coap\coap.hpp" />
+    <ClInclude Include="..\..\src\core\coap\coap_header.hpp" />
+    <ClInclude Include="..\..\src\core\coap\coap_secure.hpp" />
+    <ClInclude Include="..\..\src\core\common\code_utils.hpp" />
+    <ClInclude Include="..\..\src\core\common\context.hpp" />
+    <ClInclude Include="..\..\src\core\common\crc16.hpp" />
+    <ClInclude Include="..\..\src\core\common\debug.hpp" />
+    <ClInclude Include="..\..\src\core\common\encoding.hpp" />
+    <ClInclude Include="..\..\src\core\common\locator.hpp" />
+    <ClInclude Include="..\..\src\core\common\logging.hpp" />
+    <ClInclude Include="..\..\src\core\common\message.hpp" />
+    <ClInclude Include="..\..\src\core\common\new.hpp" />
+    <ClInclude Include="..\..\src\core\common\tasklet.hpp" />
+    <ClInclude Include="..\..\src\core\common\timer.hpp" />
+    <ClInclude Include="..\..\src\core\common\tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\common\trickle_timer.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\aes_ccm.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\aes_ecb.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\hmac_sha256.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\mbedtls.hpp" />
+    <ClInclude Include="..\..\src\core\crypto\pbkdf2_cmac.h" />
+    <ClInclude Include="..\..\src\core\crypto\sha256.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_impl.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_stub.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_frame.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_impl.hpp" />
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_stub.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\commissioner.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset_local.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dataset_manager.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\dtls.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\energy_scan_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\joiner.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\joiner_router.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\leader.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\panid_query_client.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\timestamp.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6_client.hpp" />
+    <ClInclude Include="..\..\src\core\net\dhcp6_server.hpp" />
+    <ClInclude Include="..\..\src\core\net\icmp6.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_address.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_filter.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_mpl.hpp" />
+    <ClInclude Include="..\..\src\core\net\ip6_routes.hpp" />
+    <ClInclude Include="..\..\src\core\net\netif.hpp" />
+    <ClInclude Include="..\..\src\core\net\socket.hpp" />
+    <ClInclude Include="..\..\src\core\net\udp6.hpp" />
+    <ClInclude Include="..\..\src\core\openthread-core-config.h" />
+    <ClInclude Include="..\..\src\core\openthread-core-default-config.h" />
+    <ClInclude Include="..\..\src\core\openthread-instance.h" />
+    <ClInclude Include="..\..\src\core\thread\address_resolver.hpp" />
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp" />
+    <ClInclude Include="..\..\src\core\thread\energy_scan_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\key_manager.hpp" />
+    <ClInclude Include="..\..\src\core\thread\link_quality.hpp" />
+    <ClInclude Include="..\..\src\core\thread\lowpan.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mesh_forwarder.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_constants.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_router.hpp" />
+    <ClInclude Include="..\..\src\core\thread\mle_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_leader.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_local.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_data_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic.hpp" />
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\panid_query_server.hpp" />
+    <ClInclude Include="..\..\src\core\thread\src_match_controller.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_netif.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp" />
+    <ClInclude Include="..\..\src\core\thread\thread_uri_paths.hpp" />
+    <ClInclude Include="..\..\src\core\thread\topology.hpp" />
+    <ClInclude Include="..\..\src\core\utils\child_supervision.hpp" />
+    <ClInclude Include="..\..\src\core\utils\slaac_address.hpp" />
+    <ClInclude Include="..\..\src\core\utils\jam_detector.hpp" />
+    <ClInclude Include="..\..\src\core\utils\wrap_string.h" />
+    <ClInclude Include="..\..\src\core\utils\wrap_stdbool.h" />
+    <ClInclude Include="..\..\src\core\utils\wrap_stdint.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters
new file mode 100644
index 0000000..d3a6d1c
--- /dev/null
+++ b/etc/visual-studio/libopenthread_k.vcxproj.filters
@@ -0,0 +1,669 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{c09c2d1c-85fb-4081-8235-925b45ed3c4b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\api">
+      <UniqueIdentifier>{B78747A9-11C2-4784-B825-D48FD388FE2F}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\common">
+      <UniqueIdentifier>{a59636b8-d046-46b7-992b-76572ca72bd1}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\coap">
+      <UniqueIdentifier>{7c989f53-480b-42d4-a2dc-454adafda818}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\crypto">
+      <UniqueIdentifier>{444ab8c6-6b83-4a5b-9059-67f469da1331}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\mac">
+      <UniqueIdentifier>{a49a9cc3-6a37-48a6-8049-b10c9cecd193}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\meshcop">
+      <UniqueIdentifier>{50a39acc-fab6-4e2a-85b7-d764be5299f5}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\net">
+      <UniqueIdentifier>{9a61353a-8613-4496-9ee8-c3d2800c5ac6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\thread">
+      <UniqueIdentifier>{c14a7a3f-089f-47ee-80db-1caf1b47ab6b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\utils">
+      <UniqueIdentifier>{A0D2E990-DC5E-4B56-A552-888F97214A17}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\missing">
+      <UniqueIdentifier>{F0DDA90-D13F-4B56-A552-328F97124A81}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{b4905c15-9702-4ff0-8d57-e7926337d50f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\coap">
+      <UniqueIdentifier>{c2f72132-0df1-4dba-b24f-0ab683874538}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\common">
+      <UniqueIdentifier>{92f20ce7-8f87-4a1c-bc0b-d7fa3dfd3b60}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\crypto">
+      <UniqueIdentifier>{c618ddc9-5c66-485f-a0ff-ebabb3508568}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\mac">
+      <UniqueIdentifier>{90294598-e94b-4c07-a531-93335a8955d9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\meshcop">
+      <UniqueIdentifier>{8f54e8b3-d8fb-4fed-88ce-42b39f171d0b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\net">
+      <UniqueIdentifier>{0c76d116-0b4d-4fec-a0d8-5ca127913124}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\thread">
+      <UniqueIdentifier>{702145f9-0e66-4e45-b3d4-d43f2cf17e10}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\utils">
+      <UniqueIdentifier>{5ac96cb9-6257-4387-9238-27649915463a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\api">
+      <UniqueIdentifier>{63a27d20-b039-46b4-aff9-63ace2010b9b}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\openthread">
+      <UniqueIdentifier>{2b66635d-fb09-421b-9942-8c43df5c8c77}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\missing">
+      <UniqueIdentifier>{971969f2-f6ec-50b4-806a-97965c934cef}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\core\api\commissioner_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\border_router_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dataset_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dataset_ftd_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\dhcp6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\icmp6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\ip6_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\instance_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\joiner_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\link_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\message_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\netdata_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\tasklet_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\thread_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\api\thread_ftd_api.cpp">
+      <Filter>Source Files\api</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap_header.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\coap\coap_secure.cpp">
+      <Filter>Source Files\coap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\locator.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\logging.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\message.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\tasklet.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\timer.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\tlvs.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\trickle_timer.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\aes_ccm.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_frame.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_whitelist.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\dhcp6_client.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\dhcp6_server.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\icmp6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_address.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_filter.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_mpl.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\ip6_routes.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\netif.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\address_resolver.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\key_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\link_quality.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\lowpan.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mesh_forwarder.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mle.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\mle_router.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_leader.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_leader_ftd.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_data_local.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\network_diagnostic.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\panid_query_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\src_match_controller.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\thread_netif.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\topology.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\net\udp6.cpp">
+      <Filter>Source Files\net</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\mac\mac_blacklist.cpp">
+      <Filter>Source Files\mac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\commissioner.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_local.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dataset_manager_ftd.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\dtls.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\energy_scan_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\joiner.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\joiner_router.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\leader.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\panid_query_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\timestamp.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\meshcop_tlvs.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\aes_ecb.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\hmac_sha256.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\pbkdf2_cmac.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\crypto\sha256.cpp">
+      <Filter>Source Files\crypto</Filter>
+    </ClCompile>
+   <ClCompile Include="..\..\src\core\utils\child_supervision.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\slaac_address.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\jam_detector.cpp">
+      <Filter>Source Files\utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp">
+      <Filter>Source Files\thread</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\meshcop\announce_begin_client.cpp">
+      <Filter>Source Files\meshcop</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\common\crc16.cpp">
+      <Filter>Source Files\common</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\missing_strlcpy.c">
+      <Filter>Source Files\missing</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\missing_strlcat.c">
+      <Filter>Source Files\missing</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\core\utils\missing_strnlen.c">
+      <Filter>Source Files\missing</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\core\openthread-core-config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\coap\coap.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\coap\coap_header.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\coap\coap_secure.hpp">
+      <Filter>Header Files\coap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\code_utils.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\context.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\debug.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\encoding.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\locator.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\logging.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\message.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\new.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\tasklet.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\timer.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\tlvs.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\trickle_timer.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\aes_ccm.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_frame.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_impl.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_whitelist_stub.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6_client.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\dhcp6_server.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\icmp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_address.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_filter.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_mpl.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\ip6_routes.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\netif.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\socket.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\net\udp6.hpp">
+      <Filter>Header Files\net</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\address_resolver.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\energy_scan_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\key_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\link_quality.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\lowpan.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mesh_forwarder.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_constants.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_router.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\mle_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_leader.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_local.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_data_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\network_diagnostic_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\panid_query_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\src_match_controller.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_netif.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\thread_uri_paths.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\thread\topology.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_impl.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\mac\mac_blacklist_stub.hpp">
+      <Filter>Header Files\mac</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\commissioner.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset_local.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dataset_manager.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\dtls.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\energy_scan_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\joiner.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\joiner_router.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\leader.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\panid_query_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\timestamp.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\tlvs.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\aes_ecb.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\hmac_sha256.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\mbedtls.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\pbkdf2_cmac.h">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\crypto\sha256.hpp">
+      <Filter>Header Files\crypto</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\child_supervision.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\slaac_address.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\jam_detector.hpp">
+      <Filter>Header Files\utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_server.hpp">
+      <Filter>Header Files\thread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\meshcop\announce_begin_client.hpp">
+      <Filter>Header Files\meshcop</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\common\crc16.hpp">
+      <Filter>Header Files\common</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread-windows-config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\openthread-core-default-config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\openthread-instance.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\types.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\include\openthread-core-windows-config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\coap.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\commissioner.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\crypto.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\border_router.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\dataset.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\dataset_ftd.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\dhcp6_client.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\dhcp6_server.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\icmp6.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\instance.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\ip6.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\jam_detection.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\joiner.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\link.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\message.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\netdata.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\openthread.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\tasklet.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\thread.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\thread_ftd.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\openthread\udp.h">
+      <Filter>Header Files\openthread</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\api\link_raw.hpp">
+      <Filter>Header Files\api</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrapper_string.h">
+      <Filter>Header Files\missing</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrapper_stdbool.h">
+      <Filter>Header Files\missing</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\core\utils\wrapper_stdint.h">
+      <Filter>Header Files\missing</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/mbedtls.vcxproj b/etc/visual-studio/mbedtls.vcxproj
new file mode 100644
index 0000000..c81832e
--- /dev/null
+++ b/etc/visual-studio/mbedtls.vcxproj
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{4111C8BB-D354-4348-AD3C-EB6832E84831}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>mbedtls</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        ;%(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        ..\..\include;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+        ..\..\third_party\mbedtls\repo\include\mbedtls
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\third_party\mbedtls\hardware_entropy.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\aes.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\bignum.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ccm.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher_wrap.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cmac.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ctr_drbg.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\debug.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecjpake.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp_curves.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy_poll.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md_wrap.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\memory_buffer_alloc.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\platform.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\sha256.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ciphersuites.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cli.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cookie.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_srv.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ticket.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_tls.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/etc/visual-studio/mbedtls.vcxproj.filters b/etc/visual-studio/mbedtls.vcxproj.filters
new file mode 100644
index 0000000..7939941
--- /dev/null
+++ b/etc/visual-studio/mbedtls.vcxproj.filters
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Source Files\repo">
+      <UniqueIdentifier>{0a2ce77e-dccf-408c-aea0-7a781284a936}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\repo\library">
+      <UniqueIdentifier>{859819dc-e84e-4854-ad32-2819d60da38b}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\third_party\mbedtls\hardware_entropy.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\aes.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\bignum.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ccm.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher_wrap.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cmac.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ctr_drbg.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\debug.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecjpake.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp_curves.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy_poll.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md_wrap.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\memory_buffer_alloc.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\platform.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\sha256.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ciphersuites.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cli.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cookie.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_srv.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ticket.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_tls.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/mbedtls_k.vcxproj b/etc/visual-studio/mbedtls_k.vcxproj
new file mode 100644
index 0000000..51fd42d
--- /dev/null
+++ b/etc/visual-studio/mbedtls_k.vcxproj
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{69BE8E8C-CF1E-46D6-932B-DB435F47059B}</ProjectGuid>
+    <TemplateGuid>{8c0e3d8b-df43-455b-815a-4a0e72973bc6}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <Configuration>Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <RootNamespace>ot</RootNamespace>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalIncludeDirectories>
+        ..\..\include;
+        ..\..\src\core;
+        ..\..\third_party\mbedtls;
+        ..\..\third_party\mbedtls\repo\include;
+        ..\..\third_party\mbedtls\repo\include\mbedtls;
+        ..\..\examples\drivers\windows\include_c99;
+      </AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        MBEDTLS_CONFIG_FILE="mbedtls-config.h";
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        HAVE_STDBOOL_H=1;
+        HAVE_STDINT_H=1;
+      </PreprocessorDefinitions>
+      <DisableSpecificWarnings>4132;4242;4245;4603;4627;4986;4987;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <WarningLevel>Level4</WarningLevel>
+      <TreatWarningAsError>true</TreatWarningAsError>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\third_party\mbedtls\hardware_entropy.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\aes.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\bignum.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ccm.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher_wrap.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cmac.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ctr_drbg.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\debug.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecjpake.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp_curves.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy_poll.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md_wrap.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\memory_buffer_alloc.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\platform.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\sha256.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ciphersuites.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cli.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cookie.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_srv.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ticket.c" />
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_tls.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
diff --git a/etc/visual-studio/mbedtls_k.vcxproj.filters b/etc/visual-studio/mbedtls_k.vcxproj.filters
new file mode 100644
index 0000000..691803f
--- /dev/null
+++ b/etc/visual-studio/mbedtls_k.vcxproj.filters
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Source Files\repo">
+      <UniqueIdentifier>{0a2ce77e-dccf-408c-aea0-7a781284a936}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\repo\library">
+      <UniqueIdentifier>{87ec68b3-5bec-4c60-b764-c2daf3ffcfb2}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\third_party\mbedtls\hardware_entropy.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\aes.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\bignum.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ccm.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cipher_wrap.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\cmac.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ctr_drbg.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\debug.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecjpake.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ecp_curves.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\entropy_poll.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\md_wrap.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\memory_buffer_alloc.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\platform.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\sha256.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ciphersuites.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cli.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_cookie.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_srv.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_ticket.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\third_party\mbedtls\repo\library\ssl_tls.c">
+      <Filter>Source Files\repo\library</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
diff --git a/etc/visual-studio/openthread.configuration.props b/etc/visual-studio/openthread.configuration.props
new file mode 100644
index 0000000..12a29dc
--- /dev/null
+++ b/etc/visual-studio/openthread.configuration.props
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>  
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">  
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|ARM">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM">
+      <Configuration>Release</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/openthread.sln b/etc/visual-studio/openthread.sln
new file mode 100644
index 0000000..8de9f53
--- /dev/null
+++ b/etc/visual-studio/openthread.sln
@@ -0,0 +1,389 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config files", "config files", "{53F4EEF6-B96D-4887-B9CF-CD21E8BECD15}"
+	ProjectSection(SolutionItems) = preProject
+		..\..\.appveyor.yml = ..\..\.appveyor.yml
+		..\..\.codecov.yml = ..\..\.codecov.yml
+		..\..\.travis.yml = ..\..\.travis.yml
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{95BD0669-04C8-4EEB-B3CC-0535B03F4468}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{35F6AB71-5BD2-4D53-8A51-D75CCD4CCC3D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "third_party", "third_party", "{873E9D16-2A08-41FC-B301-79C95B3A8F98}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread", "libopenthread.vcxproj", "{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread_k", "libopenthread_k.vcxproj", "{9B33C190-5D07-40BF-9536-68843DC5D7AF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbedtls", "mbedtls.vcxproj", "{4111C8BB-D354-4348-AD3C-EB6832E84831}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mbedtls_k", "mbedtls_k.vcxproj", "{69BE8E8C-CF1E-46D6-932B-DB435F47059B}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests.vcxproj", "{FD64BF17-8D36-4578-8D13-77B123BE30D3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread-windows", "libopenthread-windows.vcxproj", "{30723C38-BA3B-44C9-8D64-C5861A26934F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread-cli", "libopenthread-cli.vcxproj", "{41B32069-632E-4578-855B-A36EBFA80B56}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ot-cli", "ot-cli.vcxproj", "{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread-ncp-spi", "libopenthread-ncp-spi.vcxproj", "{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread-ncp-uart", "libopenthread-ncp-uart.vcxproj", "{D94867D2-6DAE-47E2-962A-5E8E658134D1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ot-ncp-spi", "ot-ncp-spi.vcxproj", "{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ot-ncp-uart", "ot-ncp-uart.vcxproj", "{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "drivers", "drivers", "{61E8A4A0-8138-49DB-97B4-3BEC87C8E133}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otLwf", "otLwf.vcxproj", "{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otApi", "otApi.vcxproj", "{ED0EA262-C222-42C7-98D3-E70C72978ED2}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenthread-cli-windows", "libopenthread-cli-windows.vcxproj", "{28834498-3837-44A5-8F67-249ABAB7B97D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otCli", "otCli.vcxproj", "{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otNodeApi", "otNodeApi.vcxproj", "{B7C6F344-7287-4930-AF38-223622BD8CBB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "otTestRunner", "otTestRunner.csproj", "{D5577E51-FA31-4802-8669-1DB32805935E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenThread", "OpenThread.vcxproj", "{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}"
+	ProjectSection(ProjectDependencies) = postProject
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2} = {ED0EA262-C222-42C7-98D3-E70C72978ED2}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spinel_k", "spinel_k.vcxproj", "{A55766B5-58B6-4519-835E-5A4B7C164B5A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ottmp", "ottmp.vcxproj", "{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|ARM = Debug|ARM
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|ARM = Release|ARM
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|ARM.ActiveCfg = Debug|ARM
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|ARM.Build.0 = Debug|ARM
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|x64.ActiveCfg = Debug|x64
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|x64.Build.0 = Debug|x64
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|x86.ActiveCfg = Debug|Win32
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Debug|x86.Build.0 = Debug|Win32
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|ARM.ActiveCfg = Release|ARM
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|ARM.Build.0 = Release|ARM
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|x64.ActiveCfg = Release|x64
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|x64.Build.0 = Release|x64
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|x86.ActiveCfg = Release|Win32
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40}.Release|x86.Build.0 = Release|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|ARM.ActiveCfg = Debug|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|ARM.Build.0 = Debug|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|ARM.Deploy.0 = Debug|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|x64.ActiveCfg = Debug|x64
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|x64.Build.0 = Debug|x64
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|x86.ActiveCfg = Debug|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|x86.Build.0 = Debug|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Debug|x86.Deploy.0 = Debug|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|ARM.ActiveCfg = Release|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|ARM.Build.0 = Release|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|ARM.Deploy.0 = Release|ARM
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|x64.ActiveCfg = Release|x64
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|x64.Build.0 = Release|x64
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|x86.ActiveCfg = Release|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|x86.Build.0 = Release|Win32
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF}.Release|x86.Deploy.0 = Release|Win32
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|ARM.ActiveCfg = Debug|ARM
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|ARM.Build.0 = Debug|ARM
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|x64.ActiveCfg = Debug|x64
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|x64.Build.0 = Debug|x64
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|x86.ActiveCfg = Debug|Win32
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Debug|x86.Build.0 = Debug|Win32
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|ARM.ActiveCfg = Release|ARM
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|ARM.Build.0 = Release|ARM
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|x64.ActiveCfg = Release|x64
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|x64.Build.0 = Release|x64
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|x86.ActiveCfg = Release|Win32
+		{4111C8BB-D354-4348-AD3C-EB6832E84831}.Release|x86.Build.0 = Release|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|ARM.ActiveCfg = Debug|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|ARM.Build.0 = Debug|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|ARM.Deploy.0 = Debug|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|x64.ActiveCfg = Debug|x64
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|x64.Build.0 = Debug|x64
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|x86.ActiveCfg = Debug|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|x86.Build.0 = Debug|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Debug|x86.Deploy.0 = Debug|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|ARM.ActiveCfg = Release|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|ARM.Build.0 = Release|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|ARM.Deploy.0 = Release|ARM
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|x64.ActiveCfg = Release|x64
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|x64.Build.0 = Release|x64
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|x86.ActiveCfg = Release|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|x86.Build.0 = Release|Win32
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B}.Release|x86.Deploy.0 = Release|Win32
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|ARM.ActiveCfg = Debug|ARM
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|ARM.Build.0 = Debug|ARM
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|x64.ActiveCfg = Debug|x64
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|x64.Build.0 = Debug|x64
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|x86.ActiveCfg = Debug|Win32
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Debug|x86.Build.0 = Debug|Win32
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|ARM.ActiveCfg = Release|ARM
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|ARM.Build.0 = Release|ARM
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|x64.ActiveCfg = Release|x64
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|x64.Build.0 = Release|x64
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|x86.ActiveCfg = Release|Win32
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3}.Release|x86.Build.0 = Release|Win32
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|ARM.ActiveCfg = Debug|ARM
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|ARM.Build.0 = Debug|ARM
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|x64.ActiveCfg = Debug|x64
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|x64.Build.0 = Debug|x64
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|x86.ActiveCfg = Debug|Win32
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Debug|x86.Build.0 = Debug|Win32
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|ARM.ActiveCfg = Release|ARM
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|ARM.Build.0 = Release|ARM
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|x64.ActiveCfg = Release|x64
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|x64.Build.0 = Release|x64
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|x86.ActiveCfg = Release|Win32
+		{30723C38-BA3B-44C9-8D64-C5861A26934F}.Release|x86.Build.0 = Release|Win32
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|ARM.ActiveCfg = Debug|ARM
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|ARM.Build.0 = Debug|ARM
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|x64.ActiveCfg = Debug|x64
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|x64.Build.0 = Debug|x64
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|x86.ActiveCfg = Debug|Win32
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Debug|x86.Build.0 = Debug|Win32
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|ARM.ActiveCfg = Release|ARM
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|ARM.Build.0 = Release|ARM
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|x64.ActiveCfg = Release|x64
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|x64.Build.0 = Release|x64
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|x86.ActiveCfg = Release|Win32
+		{41B32069-632E-4578-855B-A36EBFA80B56}.Release|x86.Build.0 = Release|Win32
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|ARM.ActiveCfg = Debug|ARM
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|ARM.Build.0 = Debug|ARM
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|x64.ActiveCfg = Debug|x64
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|x64.Build.0 = Debug|x64
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|x86.ActiveCfg = Debug|Win32
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Debug|x86.Build.0 = Debug|Win32
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|ARM.ActiveCfg = Release|ARM
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|ARM.Build.0 = Release|ARM
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|x64.ActiveCfg = Release|x64
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|x64.Build.0 = Release|x64
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|x86.ActiveCfg = Release|Win32
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}.Release|x86.Build.0 = Release|Win32
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|ARM.ActiveCfg = Debug|ARM
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|ARM.Build.0 = Debug|ARM
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|x64.ActiveCfg = Debug|x64
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|x64.Build.0 = Debug|x64
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|x86.ActiveCfg = Debug|Win32
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Debug|x86.Build.0 = Debug|Win32
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|ARM.ActiveCfg = Release|ARM
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|ARM.Build.0 = Release|ARM
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|x64.ActiveCfg = Release|x64
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|x64.Build.0 = Release|x64
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|x86.ActiveCfg = Release|Win32
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2}.Release|x86.Build.0 = Release|Win32
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|ARM.ActiveCfg = Debug|ARM
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|ARM.Build.0 = Debug|ARM
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|x64.ActiveCfg = Debug|x64
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|x64.Build.0 = Debug|x64
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|x86.ActiveCfg = Debug|Win32
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Debug|x86.Build.0 = Debug|Win32
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|ARM.ActiveCfg = Release|ARM
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|ARM.Build.0 = Release|ARM
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|x64.ActiveCfg = Release|x64
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|x64.Build.0 = Release|x64
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|x86.ActiveCfg = Release|Win32
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1}.Release|x86.Build.0 = Release|Win32
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|ARM.ActiveCfg = Debug|ARM
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|ARM.Build.0 = Debug|ARM
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|x64.ActiveCfg = Debug|x64
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|x64.Build.0 = Debug|x64
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|x86.ActiveCfg = Debug|Win32
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Debug|x86.Build.0 = Debug|Win32
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|ARM.ActiveCfg = Release|ARM
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|ARM.Build.0 = Release|ARM
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|x64.ActiveCfg = Release|x64
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|x64.Build.0 = Release|x64
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|x86.ActiveCfg = Release|Win32
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}.Release|x86.Build.0 = Release|Win32
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|ARM.ActiveCfg = Debug|ARM
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|ARM.Build.0 = Debug|ARM
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|x64.ActiveCfg = Debug|x64
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|x64.Build.0 = Debug|x64
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|x86.ActiveCfg = Debug|Win32
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Debug|x86.Build.0 = Debug|Win32
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|ARM.ActiveCfg = Release|ARM
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|ARM.Build.0 = Release|ARM
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|x64.ActiveCfg = Release|x64
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|x64.Build.0 = Release|x64
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|x86.ActiveCfg = Release|Win32
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}.Release|x86.Build.0 = Release|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|ARM.ActiveCfg = Debug|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|ARM.Build.0 = Debug|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|ARM.Deploy.0 = Debug|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x64.ActiveCfg = Debug|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x64.Build.0 = Debug|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x64.Deploy.0 = Debug|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x86.ActiveCfg = Debug|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x86.Build.0 = Debug|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Debug|x86.Deploy.0 = Debug|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|ARM.ActiveCfg = Release|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|ARM.Build.0 = Release|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|ARM.Deploy.0 = Release|ARM
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x64.ActiveCfg = Release|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x64.Build.0 = Release|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x64.Deploy.0 = Release|x64
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x86.ActiveCfg = Release|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x86.Build.0 = Release|Win32
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}.Release|x86.Deploy.0 = Release|Win32
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|ARM.ActiveCfg = Debug|ARM
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|ARM.Build.0 = Debug|ARM
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|x64.ActiveCfg = Debug|x64
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|x64.Build.0 = Debug|x64
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|x86.ActiveCfg = Debug|Win32
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Debug|x86.Build.0 = Debug|Win32
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|ARM.ActiveCfg = Release|ARM
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|ARM.Build.0 = Release|ARM
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x64.ActiveCfg = Release|x64
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x64.Build.0 = Release|x64
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x86.ActiveCfg = Release|Win32
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x86.Build.0 = Release|Win32
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|ARM.ActiveCfg = Debug|ARM
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|ARM.Build.0 = Debug|ARM
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|x64.ActiveCfg = Debug|x64
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|x64.Build.0 = Debug|x64
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|x86.ActiveCfg = Debug|Win32
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Debug|x86.Build.0 = Debug|Win32
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|ARM.ActiveCfg = Release|ARM
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|ARM.Build.0 = Release|ARM
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|x64.ActiveCfg = Release|x64
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|x64.Build.0 = Release|x64
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|x86.ActiveCfg = Release|Win32
+		{28834498-3837-44A5-8F67-249ABAB7B97D}.Release|x86.Build.0 = Release|Win32
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|ARM.ActiveCfg = Debug|ARM
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|ARM.Build.0 = Debug|ARM
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|x64.ActiveCfg = Debug|x64
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|x64.Build.0 = Debug|x64
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|x86.ActiveCfg = Debug|Win32
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Debug|x86.Build.0 = Debug|Win32
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|ARM.ActiveCfg = Release|ARM
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|ARM.Build.0 = Release|ARM
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|x64.ActiveCfg = Release|x64
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|x64.Build.0 = Release|x64
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|x86.ActiveCfg = Release|Win32
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}.Release|x86.Build.0 = Release|Win32
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|ARM.ActiveCfg = Debug|ARM
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|ARM.Build.0 = Debug|ARM
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x64.ActiveCfg = Debug|x64
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x64.Build.0 = Debug|x64
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x86.ActiveCfg = Debug|Win32
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x86.Build.0 = Debug|Win32
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|ARM.ActiveCfg = Release|ARM
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|ARM.Build.0 = Release|ARM
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x64.ActiveCfg = Release|x64
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x64.Build.0 = Release|x64
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x86.ActiveCfg = Release|Win32
+		{B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x86.Build.0 = Release|Win32
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|ARM.Build.0 = Debug|Any CPU
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x64.ActiveCfg = Debug|x64
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x64.Build.0 = Debug|x64
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x86.ActiveCfg = Debug|Win32
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x86.Build.0 = Debug|Win32
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|ARM.ActiveCfg = Release|Any CPU
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|ARM.Build.0 = Release|Any CPU
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|x64.ActiveCfg = Release|x64
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|x64.Build.0 = Release|x64
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|x86.ActiveCfg = Release|Win32
+		{D5577E51-FA31-4802-8669-1DB32805935E}.Release|x86.Build.0 = Release|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.ActiveCfg = Debug|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.Build.0 = Debug|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.Deploy.0 = Debug|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x64.ActiveCfg = Debug|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x64.Build.0 = Debug|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x64.Deploy.0 = Debug|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x86.ActiveCfg = Debug|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x86.Build.0 = Debug|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|x86.Deploy.0 = Debug|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|ARM.ActiveCfg = Release|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|ARM.Build.0 = Release|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|ARM.Deploy.0 = Release|ARM
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x64.ActiveCfg = Release|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x64.Build.0 = Release|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x64.Deploy.0 = Release|x64
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x86.ActiveCfg = Release|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x86.Build.0 = Release|Win32
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Release|x86.Deploy.0 = Release|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|ARM.ActiveCfg = Debug|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|ARM.Build.0 = Debug|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|ARM.Deploy.0 = Debug|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x64.ActiveCfg = Debug|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x64.Build.0 = Debug|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x64.Deploy.0 = Debug|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x86.ActiveCfg = Debug|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x86.Build.0 = Debug|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Debug|x86.Deploy.0 = Debug|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|ARM.ActiveCfg = Release|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|ARM.Build.0 = Release|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|ARM.Deploy.0 = Release|ARM
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x64.ActiveCfg = Release|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x64.Build.0 = Release|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x64.Deploy.0 = Release|x64
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.ActiveCfg = Release|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.Build.0 = Release|Win32
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A}.Release|x86.Deploy.0 = Release|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.ActiveCfg = Debug|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.Build.0 = Debug|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|ARM.Deploy.0 = Debug|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.ActiveCfg = Debug|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.Build.0 = Debug|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x64.Deploy.0 = Debug|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.ActiveCfg = Debug|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.Build.0 = Debug|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Debug|x86.Deploy.0 = Debug|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.ActiveCfg = Release|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.Build.0 = Release|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|ARM.Deploy.0 = Release|ARM
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.ActiveCfg = Release|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.Build.0 = Release|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x64.Deploy.0 = Release|x64
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.ActiveCfg = Release|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.Build.0 = Release|Win32
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}.Release|x86.Deploy.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{DD5018BE-54C6-4FD4-9F8D-08D52FC0CD40} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{9B33C190-5D07-40BF-9536-68843DC5D7AF} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{4111C8BB-D354-4348-AD3C-EB6832E84831} = {873E9D16-2A08-41FC-B301-79C95B3A8F98}
+		{69BE8E8C-CF1E-46D6-932B-DB435F47059B} = {873E9D16-2A08-41FC-B301-79C95B3A8F98}
+		{FD64BF17-8D36-4578-8D13-77B123BE30D3} = {35F6AB71-5BD2-4D53-8A51-D75CCD4CCC3D}
+		{30723C38-BA3B-44C9-8D64-C5861A26934F} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}
+		{41B32069-632E-4578-855B-A36EBFA80B56} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}
+		{B92F449E-0FD9-44FC-ACFA-6521A3240CA2} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{D94867D2-6DAE-47E2-962A-5E8E658134D1} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{B4C744EC-B662-46C6-A076-FB58FA8FDF1B} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}
+		{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}
+		{3F1F7F6C-2A33-4635-9880-08FC5BC4E435} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133}
+		{ED0EA262-C222-42C7-98D3-E70C72978ED2} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133}
+		{28834498-3837-44A5-8F67-249ABAB7B97D} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133}
+		{B7C6F344-7287-4930-AF38-223622BD8CBB} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133}
+		{D5577E51-FA31-4802-8669-1DB32805935E} = {35F6AB71-5BD2-4D53-8A51-D75CCD4CCC3D}
+		{F8C22844-9B93-4978-80DF-8AF2B37A7ABB} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD}
+		{A55766B5-58B6-4519-835E-5A4B7C164B5A} = {95BD0669-04C8-4EEB-B3CC-0535B03F4468}
+		{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133}
+	EndGlobalSection
+EndGlobal
diff --git a/etc/visual-studio/ot-cli.vcxproj b/etc/visual-studio/ot-cli.vcxproj
new file mode 100644
index 0000000..6d669f4
--- /dev/null
+++ b/etc/visual-studio/ot-cli.vcxproj
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{91D3ADEA-F1FE-4433-95B6-F8F6A7CF7BAF}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\platforms;
+        ..\..\include;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>
+        mincore.lib;
+      </AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread-cli.vcxproj">
+      <Project>{41b32069-632e-4578-855b-a36ebfa80b56}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread.vcxproj">
+      <Project>{dd5018be-54c6-4fd4-9f8d-08d52fc0cd40}</Project>
+    </ProjectReference>
+    <ProjectReference Include="mbedtls.vcxproj">
+      <Project>{4111c8bb-d354-4348-ad3c-eb6832e84831}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread-windows.vcxproj">
+      <Project>{30723c38-ba3b-44c9-8d64-c5861a26934f}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\cli\main.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ot-cli.vcxproj.filters b/etc/visual-studio/ot-cli.vcxproj.filters
new file mode 100644
index 0000000..5d735b3
--- /dev/null
+++ b/etc/visual-studio/ot-cli.vcxproj.filters
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\cli\main.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ot-ncp-spi.vcxproj b/etc/visual-studio/ot-ncp-spi.vcxproj
new file mode 100644
index 0000000..af117a4
--- /dev/null
+++ b/etc/visual-studio/ot-ncp-spi.vcxproj
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B4C744EC-B662-46C6-A076-FB58FA8FDF1B}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OPENTHREAD_ENABLE_NCP_SPI=1;
+        OPENTHREAD_ENABLE_NCP_UART=0;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\platforms;
+        ..\..\include;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <SDLCheck>true</SDLCheck>
+      <WarningLevel>Level3</WarningLevel>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>
+        mincore.lib;
+      </AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\ncp\main.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread.vcxproj">
+      <Project>{dd5018be-54c6-4fd4-9f8d-08d52fc0cd40}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread-ncp-spi.vcxproj">
+      <Project>{b92f449e-0fd9-44fc-acfa-6521a3240ca2}</Project>
+    </ProjectReference>
+    <ProjectReference Include="mbedtls.vcxproj">
+      <Project>{4111c8bb-d354-4348-ad3c-eb6832e84831}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread-windows.vcxproj">
+      <Project>{30723c38-ba3b-44c9-8d64-c5861a26934f}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ot-ncp-spi.vcxproj.filters b/etc/visual-studio/ot-ncp-spi.vcxproj.filters
new file mode 100644
index 0000000..3cfd287
--- /dev/null
+++ b/etc/visual-studio/ot-ncp-spi.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\ncp\main.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ot-ncp-uart.vcxproj b/etc/visual-studio/ot-ncp-uart.vcxproj
new file mode 100644
index 0000000..e000631
--- /dev/null
+++ b/etc/visual-studio/ot-ncp-uart.vcxproj
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_FTD=1;
+        OPENTHREAD_ENABLE_NCP_SPI=0;
+        OPENTHREAD_ENABLE_NCP_UART=1;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\platforms;
+        ..\..\include;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <SDLCheck>true</SDLCheck>
+      <WarningLevel>Level3</WarningLevel>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>
+        mincore.lib;
+      </AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\ncp\main.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread.vcxproj">
+      <Project>{dd5018be-54c6-4fd4-9f8d-08d52fc0cd40}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread-ncp-uart.vcxproj">
+      <Project>{d94867d2-6dae-47e2-962a-5e8e658134d1}</Project>
+    </ProjectReference>
+    <ProjectReference Include="mbedtls.vcxproj">
+      <Project>{4111c8bb-d354-4348-ad3c-eb6832e84831}</Project>
+    </ProjectReference>
+    <ProjectReference Include="libopenthread-windows.vcxproj">
+      <Project>{30723c38-ba3b-44c9-8d64-c5861a26934f}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/etc/visual-studio/ot-ncp-uart.vcxproj.filters b/etc/visual-studio/ot-ncp-uart.vcxproj.filters
new file mode 100644
index 0000000..3cfd287
--- /dev/null
+++ b/etc/visual-studio/ot-ncp-uart.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\apps\ncp\main.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otApi.vcxproj b/etc/visual-studio/otApi.vcxproj
new file mode 100644
index 0000000..38acfef
--- /dev/null
+++ b/etc/visual-studio/otApi.vcxproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" /> 
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{ED0EA262-C222-42C7-98D3-E70C72978ED2}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\dll\</OutDir>
+    <CodeAnalysisRuleSet>C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets\NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <ApiValidator_Enable>false</ApiValidator_Enable>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_FTD=1;
+        OTAPI_EXPORTS;
+      </PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\drivers\windows\include;
+        ..\..\examples\drivers\windows\otApi;
+        ..\..\include;
+      </AdditionalIncludeDirectories>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppModuleName>otApi</WppModuleName>
+      <EnablePREfast>true</EnablePREfast>
+      <ExceptionHandling Condition="'$(Platform)'=='ARM'">Sync</ExceptionHandling>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\otApi\precomp.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otApi\dllmain.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\otApi\otApi.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otApi.vcxproj.filters b/etc/visual-studio/otApi.vcxproj.filters
new file mode 100644
index 0000000..ce2de06
--- /dev/null
+++ b/etc/visual-studio/otApi.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otApi\otApi.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otApi\dllmain.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\otApi\precomp.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otCli.vcxproj b/etc/visual-studio/otCli.vcxproj
new file mode 100644
index 0000000..5291045
--- /dev/null
+++ b/etc/visual-studio/otCli.vcxproj
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" /> 
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{CAC8A00E-C6C8-4CF0-BA5A-C9A9A601C6DB}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <ApplicationType>Windows Store</ApplicationType>
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+    <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_FTD=1;
+        OTDLL;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otCli\main.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread-cli-windows.vcxproj">
+      <Project>{28834498-3837-44a5-8f67-249abab7b97d}</Project>
+    </ProjectReference>
+    <ProjectReference Include="otApi.vcxproj">
+      <Project>{ed0ea262-c222-42c7-98d3-e70c72978ed2}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otCli.vcxproj.filters b/etc/visual-studio/otCli.vcxproj.filters
new file mode 100644
index 0000000..203d16a
--- /dev/null
+++ b/etc/visual-studio/otCli.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otCli\main.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otLwf.vcxproj b/etc/visual-studio/otLwf.vcxproj
new file mode 100644
index 0000000..46af170
--- /dev/null
+++ b/etc/visual-studio/otLwf.vcxproj
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}</ProjectGuid>
+    <TemplateGuid>{8b1800b9-d017-4029-9785-13ef5e5b328e}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <RootNamespace>ot</RootNamespace>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\sys\</OutDir>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreProcessorDefinitions>
+        %(PreProcessorDefinitions);
+        NDIS_WDM=1;
+        NDIS630=1;
+        OPENTHREAD_FTD=1;
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_PROJECT_CORE_CONFIG_FILE="openthread-core-windows-config.h";
+      </PreProcessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\examples\drivers\windows\include;
+        ..\..\examples\drivers\windows\include_c99;
+        ..\..\examples\drivers\windows\otLwf;
+        ..\..\src;
+        ..\..\src\core;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level4</WarningLevel>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DisableSpecificWarnings>%(DisableSpecificWarnings);4201;4214</DisableSpecificWarnings>
+      <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+      <PreCompiledHeader>Use</PreCompiledHeader>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppAdditionalOptions>-km %(WppAdditionalOptions)</WppAdditionalOptions>
+      <WppModuleName>otLwf</WppModuleName>
+      <WppSearchString>WPP_INIT_TRACING</WppSearchString>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..;.;</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Link>
+      <AdditionalDependencies>
+        %(AdditionalDependencies);
+        ndis.lib;
+        wdmsec.lib;
+        netio.lib;
+        uuid.lib;
+        cng.lib;
+      </AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command Condition="'$(Platform)'=='ARM'">inf2cat /driver:$(TargetDir) /os:8_ARM</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\address.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\alarm.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\command.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\datapath.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\driver.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\eventprocessing.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\iocontrol.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\settings.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\thread.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\tunnel.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\precomp.c">
+      <AdditionalIncludeDirectories>..;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreProcessorDefinitions>%(PreProcessorDefinitions);NDIS_WDM=1</PreProcessorDefinitions>
+      <PreCompiledHeaderFile>precomp.h</PreCompiledHeaderFile>
+      <PreCompiledHeader>Create</PreCompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\filter.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\device.c" />
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\radio.c" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\command.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\device.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\driver.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\iocontrol.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\nsihelper.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\radio.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\thread.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\tunnel.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\precomp.h" />
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\filter.h" />
+    <ResourceCompile Include="..\..\examples\drivers\windows\otLwf\filter.rc" />
+    <Inf Include="..\..\examples\drivers\windows\otLwf\otLwf.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="libopenthread_k.vcxproj">
+      <Project>{9b33c190-5d07-40bf-9536-68843dc5d7af}</Project>
+    </ProjectReference>
+    <ProjectReference Include="mbedtls_k.vcxproj">
+      <Project>{69be8e8c-cf1e-46d6-932b-db435f47059b}</Project>
+    </ProjectReference>
+    <ProjectReference Include="spinel_k.vcxproj">
+      <Project>{a55766b5-58b6-4519-835e-5a4b7c164b5a}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otLwf.vcxproj.filters b/etc/visual-studio/otLwf.vcxproj.filters
new file mode 100644
index 0000000..8b540e3
--- /dev/null
+++ b/etc/visual-studio/otLwf.vcxproj.filters
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="Driver Files">
+      <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
+      <Extensions>inf;inv;inx;mof;mc;</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\..\examples\drivers\windows\otLwf\otLwf.inf">
+      <Filter>Driver Files</Filter>
+    </Inf>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\precomp.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\filter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\driver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\device.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\nsihelper.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\iocontrol.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\radio.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\thread.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\tunnel.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\examples\drivers\windows\otLwf\command.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\filter.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\device.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\precomp.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\alarm.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\radio.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\driver.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\iocontrol.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\eventprocessing.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\datapath.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\address.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\tunnel.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\thread.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\settings.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otLwf\command.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\examples\drivers\windows\otLwf\filter.rc">
+      <Filter>Resource Files</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otNodeApi.vcxproj b/etc/visual-studio/otNodeApi.vcxproj
new file mode 100644
index 0000000..06d6876
--- /dev/null
+++ b/etc/visual-studio/otNodeApi.vcxproj
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" /> 
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{B7C6F344-7287-4930-AF38-223622BD8CBB}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ot</RootNamespace>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\dll\</OutDir>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <ApiValidator_Enable>false</ApiValidator_Enable>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        OPENTHREAD_FTD=1;
+        OTAPI_EXPORTS;
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\examples\drivers\windows\include;
+        ..\..\examples\drivers\windows\otApi;
+        ..\..\include;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppModuleName>otNodeApi</WppModuleName>
+      <EnablePREfast>true</EnablePREfast>
+      <ExceptionHandling Condition="'$(Platform)'=='ARM'">Sync</ExceptionHandling>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>
+        ntdll.lib;
+      </AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\otNodeApi\precomp.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otNodeApi\dllmain.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\otNodeApi\otNodeApi.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="otApi.vcxproj">
+      <Project>{ed0ea262-c222-42c7-98d3-e70c72978ed2}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otNodeApi.vcxproj.filters b/etc/visual-studio/otNodeApi.vcxproj.filters
new file mode 100644
index 0000000..8e527ae
--- /dev/null
+++ b/etc/visual-studio/otNodeApi.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\otNodeApi\otNodeApi.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\examples\drivers\windows\otNodeApi\dllmain.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\examples\drivers\windows\otNodeApi\precomp.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/otTestRunner.csproj b/etc/visual-studio/otTestRunner.csproj
new file mode 100644
index 0000000..9584037
--- /dev/null
+++ b/etc/visual-studio/otTestRunner.csproj
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{D5577E51-FA31-4802-8669-1DB32805935E}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>otTestRunner</RootNamespace>
+    <AssemblyName>otTestRunner</AssemblyName>
+    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">
+    <DebugSymbols>true</DebugSymbols>
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">
+    <IntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</IntermediateOutputPath>
+    <BaseIntermediateOutputPath>..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\</BaseIntermediateOutputPath>
+    <OutputPath>..\..\build\bin\$(Platform)\$(Configuration)\exe\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\..\tests\otTestRunner\Program.cs" />
+    <Compile Include="..\..\tests\otTestRunner\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\tests\otTestRunner\App.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ottmp.vcxproj b/etc/visual-studio/ottmp.vcxproj
new file mode 100644
index 0000000..96a5e55
--- /dev/null
+++ b/etc/visual-studio/ottmp.vcxproj
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{1EAFF7C8-8215-4EDA-83B2-EEB56CECE84D}</ProjectGuid>
+    <TemplateGuid>{497e31cb-056b-4f31-abb8-447fd55ee5a5}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <RootNamespace>ot</RootNamespace>
+    <TargetVersion>Windows10</TargetVersion>
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\sys\</OutDir>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreProcessorDefinitions>
+        %(PreProcessorDefinitions);
+        NDIS_MINIPORT_DRIVER=1;
+        NDIS_WDM=1;
+        NDIS650_MINIPORT=1;
+        OTTMP_LEGACY=1;
+      </PreProcessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\examples\drivers\windows\include;
+        ..\..\examples\drivers\windows\ottmp;
+      </AdditionalIncludeDirectories>
+      <WarningLevel>Level4</WarningLevel>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <DisableSpecificWarnings>%(DisableSpecificWarnings);4200;4201;4214</DisableSpecificWarnings>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppAdditionalOptions>-km %(WppAdditionalOptions)</WppAdditionalOptions>
+      <WppModuleName>ottmp</WppModuleName>
+      <WppSearchString>WPP_INIT_TRACING</WppSearchString>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <ResourceCompile>
+      <PreProcessorDefinitions>
+        %(PreProcessorDefinitions);
+        NDIS_MINIPORT_DRIVER=1;
+        NDIS_WDM=1;
+        NDIS650_MINIPORT=1;
+        OTTMP_LEGACY=1;
+      </PreProcessorDefinitions>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..;.;</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Link>
+      <AdditionalDependencies>
+        %(AdditionalDependencies);
+        ndis.lib;
+      </AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command Condition="'$(Platform)'=='ARM'">inf2cat /driver:$(TargetDir) /os:8_ARM</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\adapter.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\device.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\driver.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\hdlc.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\oid.cpp" />
+    <ClCompile Include="..\..\examples\drivers\windows\ottmp\serial.cpp" />
+    <ResourceCompile Include="..\..\examples\drivers\windows\ottmp\ottmp.rc" />
+    <Inf Include="..\..\examples\drivers\windows\ottmp\ottmp.inf" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\adapter.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\device.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\driver.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\hardware.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\hdlc.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\oid.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\pch.hpp" />
+    <ClInclude Include="..\..\examples\drivers\windows\ottmp\serial.hpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/ottmp.vcxproj.filters b/etc/visual-studio/ottmp.vcxproj.filters
new file mode 100644
index 0000000..64ba1d0
--- /dev/null
+++ b/etc/visual-studio/ottmp.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{BED10704-BFDA-427C-BB01-B08F20AB1718}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{A6C61B28-961C-4B63-892B-A7E5EB2A4B3E}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{0CBFC63C-11CB-4E5F-95F1-187BE3F6DA44}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="Driver Files">
+      <UniqueIdentifier>{6C2BAFD4-E244-4FF2-A002-FBCCF14AFA9D}</UniqueIdentifier>
+      <Extensions>inf;inv;inx;mof;mc;</Extensions>
+    </Filter>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/spinel_k.vcxproj b/etc/visual-studio/spinel_k.vcxproj
new file mode 100644
index 0000000..ed09929
--- /dev/null
+++ b/etc/visual-studio/spinel_k.vcxproj
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="openthread.configuration.props" /> 
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{A55766B5-58B6-4519-835E-5A4B7C164B5A}</ProjectGuid>
+    <TemplateGuid>{8c0e3d8b-df43-455b-815a-4a0e72973bc6}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <Configuration>Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <RootNamespace>ot</RootNamespace>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+    <TargetVersion>Windows10</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup>
+    <OutDir>..\..\build\bin\$(Platform)\$(Configuration)\lib\</OutDir>
+    <IntDir>..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>
+        %(PreprocessorDefinitions);
+        HAVE_STRNLEN=1;
+        OPENTHREAD_FTD=1;
+        HAVE_STDBOOL_H=1;
+        HAVE_STDINT_H=1;
+        SPINEL_PLATFORM_DOESNT_IMPLEMENT_ERRNO_VAR=1;
+        OPENTHREAD_CONFIG_FILE="openthread-windows-config.h";
+        OPENTHREAD_PROJECT_CORE_CONFIG_FILE="openthread-core-windows-config.h";
+      </PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>
+        %(AdditionalIncludeDirectories);
+        ..\..\include;
+        ..\..\src\core;
+        ..\..\examples\drivers\windows\include_c99;
+      </AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4100;4706;4748;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <TreatWarningAsError>true</TreatWarningAsError>
+      <WppEnabled>true</WppEnabled>
+      <WppScanConfigurationData>..\..\include\openthread\platform\logging-windows.h</WppScanConfigurationData>
+      <WppAdditionalOptions>-km %(WppAdditionalOptions)</WppAdditionalOptions>
+      <WppModuleName>otCore</WppModuleName>
+      <WppSearchString>WPP_INIT_TRACING</WppSearchString>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\spinel.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\spinel.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>
\ No newline at end of file
diff --git a/etc/visual-studio/spinel_k.vcxproj.filters b/etc/visual-studio/spinel_k.vcxproj.filters
new file mode 100644
index 0000000..24312b9
--- /dev/null
+++ b/etc/visual-studio/spinel_k.vcxproj.filters
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{C034F7F4-3CBD-4B9A-A033-0D76A195B581}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{7A95885E-AD23-439A-B283-9BDE0E39D84E}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{1E1DF93E-9746-4688-A1F0-7B59378CF28F}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\ncp\spinel.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\ncp\spinel.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/examples/Makefile-cc2538 b/examples/Makefile-cc2538
new file mode 100644
index 0000000..c354d10
--- /dev/null
+++ b/examples/Makefile-cc2538
@@ -0,0 +1,262 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+CCAS                            = arm-none-eabi-as
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=all          \
+    --enable-ncp-app=all          \
+    --with-ncp-bus=uart           \
+    --enable-diag                 \
+    --with-examples=cc2538        \
+    --with-platform-info=CC2538   \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+CONFIG_FILE      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-cc2538-config.h\"'
+CONFIG_FILE_PATH = $(AbsTopSourceDir)/examples/platforms/cc2538/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -nostartfiles                 \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = cc2538
+
+ARCHS                           = cortex-m3
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# cortex-m3
+#
+
+cortex-m3_target_ABI                  = cortex-m3
+cortex-m3_target_CPPFLAGS             = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_CFLAGS               = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_CXXFLAGS             = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_LDFLAGS              = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-cc2650 b/examples/Makefile-cc2650
new file mode 100644
index 0000000..090b5f2
--- /dev/null
+++ b/examples/Makefile-cc2650
@@ -0,0 +1,267 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+AS                              = arm-none-eabi-as
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=mtd          \
+    --enable-ncp-app=mtd          \
+    --with-ncp-bus=uart           \
+    --with-examples=cc2650        \
+    --with-platform-info=cc2650   \
+    MBEDTLS_CPPFLAGS="$(CC2650_MBEDTLS_CPPFLAGS)" \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+CC2650_MBEDTLS_CPPFLAGS  = -DMBEDTLS_CONFIG_FILE='\"cc2650-mbedtls-config.h\"'
+CC2650_MBEDTLS_CPPFLAGS += -I$(PWD)/examples/platforms/cc2650/crypto
+CC2650_MBEDTLS_CPPFLAGS += -I$(PWD)/third_party/ti/cc26xxware
+CC2650_MBEDTLS_CPPFLAGS += -I$(PWD)/third_party/mbedtls
+CC2650_MBEDTLS_CPPFLAGS += -I$(PWD)/third_party/mbedtls/repo/include
+
+CC2650_CONFIG_FILE_CPPFLAGS  = -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-cc2650-config.h\"'
+CC2650_CONFIG_FILE_CPPFLAGS += -I$(PWD)/examples/platforms/cc2650/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    $(CC2650_CONFIG_FILE_CPPFLAGS)\
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -nostartfiles                 \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = cc2650
+
+ARCHS                           = cortex-m3
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# cortex-m3
+#
+
+cortex-m3_target_ABI                  = cortex-m3
+cortex-m3_target_CPPFLAGS             = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_CFLAGS               = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_CXXFLAGS             = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+cortex-m3_target_LDFLAGS              = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-da15000 b/examples/Makefile-da15000
new file mode 100644
index 0000000..e349c1a
--- /dev/null
+++ b/examples/Makefile-da15000
@@ -0,0 +1,261 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+AS                              = arm-none-eabi-as
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=all          \
+    --enable-ncp-app=all          \
+    --with-ncp-bus=uart           \
+    --with-examples=da15000       \
+    --with-platform-info=DA15000  \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+CONFIG_FILE                      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-da15000-config.h\"'
+CONFIG_FILE_PATH                 = $(AbsTopSourceDir)/examples/platforms/da15000/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -nostartfiles                 \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = da15000
+
+ARCHS                           = cortex-m0
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# cortex-m0
+#
+
+cortex-m0_target_ABI                  = cortex-m0
+cortex-m0_target_CPPFLAGS             = -mcpu=cortex-m0 -mfloat-abi=soft -mthumb
+cortex-m0_target_CFLAGS               = -mcpu=cortex-m0 -mfloat-abi=soft -mthumb
+cortex-m0_target_CXXFLAGS             = -mcpu=cortex-m0 -mfloat-abi=soft -mthumb
+cortex-m0_target_LDFLAGS              = -mcpu=cortex-m0 -mfloat-abi=soft -mthumb
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-efr32 b/examples/Makefile-efr32
new file mode 100644
index 0000000..103a144
--- /dev/null
+++ b/examples/Makefile-efr32
@@ -0,0 +1,276 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+CCAS                            = arm-none-eabi-as
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=all          \
+    --enable-ncp-app=all          \
+    --with-ncp-bus=uart           \
+    --enable-diag                 \
+    --with-examples=efr32         \
+    --with-platform-info=EFR32    \
+    MBEDTLS_CPPFLAGS="$(EFR32_MBEDTLS_CPPFLAGS)" \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+EFR32_MBEDTLS_CPPFLAGS  = -DMBEDTLS_CONFIG_FILE='\"mbedtls-config.h\"'
+EFR32_MBEDTLS_CPPFLAGS += -DMBEDTLS_USER_CONFIG_FILE='\"efr32-mbedtls-config.h\"'
+EFR32_MBEDTLS_CPPFLAGS += -DEFR32MG12P432F1024GL125
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/examples/platforms/efr32/crypto
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/silabs/gecko_sdk_suite/v1.1/util/third_party/mbedtls/configs
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/CMSIS/Include
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/silabs/gecko_sdk_suite/v1.1/util/third_party/mbedtls/sl_crypto/include
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/Device/SiliconLabs/EFR32MG12P/Include
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/emlib/inc
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls/repo/include
+EFR32_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls/repo/include/mbedtls
+
+CONFIG_FILE      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-efr32-config.h\"'
+CONFIG_FILE_PATH = $(AbsTopSourceDir)/examples/platforms/efr32/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -nostartfiles                 \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = efr32
+
+ARCHS                           = cortex-m4
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# cortex-m4
+#
+
+cortex-m4_target_ABI                  = cortex-m4
+cortex-m4_target_CPPFLAGS             = -mcpu=cortex-m4 -mfloat-abi=soft -mthumb
+cortex-m4_target_CFLAGS               = -mcpu=cortex-m4 -mfloat-abi=soft -mthumb
+cortex-m4_target_CXXFLAGS             = -mcpu=cortex-m4 -mfloat-abi=soft -mthumb
+cortex-m4_target_LDFLAGS              = -mcpu=cortex-m4 -mfloat-abi=soft -mthumb
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-emsk b/examples/Makefile-emsk
new file mode 100644
index 0000000..01a4f07
--- /dev/null
+++ b/examples/Makefile-emsk
@@ -0,0 +1,327 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arc-elf32-ar
+CCAS                            = arc-elf32-gcc
+CPP                             = arc-elf32-cpp
+CC                              = arc-elf32-gcc
+CXX                             = arc-elf32-g++
+LD                              = arc-elf32-gcc
+STRIP                           = arc-elf32-strip
+NM                              = arc-elf32-nm
+RANLIB                          = arc-elf32-ranlib
+OBJCOPY                         = arc-elf32-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=all          \
+    --enable-ncp-app=all          \
+    --with-ncp-bus=uart           \
+    --with-examples=emsk          \
+    --with-platform-info=EMSK     \
+    $(NULL)
+
+ifeq ($(CERT_LOG),1)
+configure_OPTIONS              += --enable-cert-log
+endif
+
+ifeq ($(COMMISSIONER),1)
+configure_OPTIONS              += --enable-commissioner
+endif
+
+ifeq ($(JOINER),1)
+configure_OPTIONS              += --enable-joiner
+endif
+
+ifeq ($(DHCP6_SERVER),1)
+configure_OPTIONS              += --enable-dhcp6-server
+endif
+
+ifeq ($(DHCP6_CLIENT),1)
+configure_OPTIONS              += --enable-dhcp6-client
+endif
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+CONFIG_FILE                      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-emsk-config.h\"'
+CONFIG_FILE_PATH                 = $(AbsTopSourceDir)/examples/platforms/emsk/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+CCASFLAGS                      += \
+    $(COMMONCCASFLAGS)            \
+    $(target_CCASFLAGS)           \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -nostartfiles                 \
+    -specs=nano.specs             \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    -Wl,--defsym=_STACKSIZE=524288 \
+    -Wl,--defsym=_HEAPSIZE=524288 \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = emsk
+
+ARCHS                           = arcem11d
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" CCAS="$(CCAS)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CCASFLAGS="$(CCASFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arc-elf32 \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+configure-$(1): target_CCASFLAGS=$($(1)_target_CCASFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# EMSK2.3 arcem11d
+#
+
+# Common options
+MKDEP_OPT                             = -MMD -MT $@ -MF $@.d
+OPT_OLEVEL                            = -O0
+
+# Include EMSK BSP folder
+ALL_INCLUDES                          = -I$(top_srcdir)/third_party/synopsys/embarc_emsk_bsp \
+                                        -I$(top_srcdir)/examples/platforms/emsk \
+                                        -I$(top_srcdir)/third_party/synopsys/embarc_emsk_bsp/library/clib
+
+# COMMON_COMPILE_OPT                  = -mno-sdata $(OPT_OLEVEL) \
+                                        $(ALL_INCLUDES) $(MKDEP_OPT)
+
+# Core options of arcem11d in EMSK2.3 from ARC TCF file
+COMMON_CORE_OPT_GNU                   = -mcpu=em4_fpuda \
+                                        -mlittle-endian \
+                                        -mcode-density \
+                                        -mdiv-rem \
+                                        -mswap \
+                                        -mnorm \
+                                        -mmpy-option=6 \
+                                        -mbarrel-shifter \
+                                        -mfpu=fpuda_all \
+                                        --param l1-cache-size=16384 \
+                                        --param l1-cache-line-size=32
+
+CCORE_OPT_GNU                        += $(COMMON_CORE_OPT_GNU)
+CXXCORE_OPT_GNU                      += $(COMMON_CORE_OPT_GNU)
+ACORE_OPT_GNU                        += $(COMMON_CORE_OPT_GNU)
+LCORE_OPT_GNU                        += $(COMMON_CORE_OPT_GNU)
+
+# C/CPP/ASM/Linker Options
+COMPILE_OPT                          += $(CCORE_OPT_GNU) \
+                                        $(COMMON_COMPILE_OPT) -std=gnu99
+CXX_COMPILE_OPT                      += $(CXXCORE_OPT_GNU) \
+                                        $(COMMON_COMPILE_OPT)
+ASM_OPT                              += $(ACORE_OPT_GNU) \
+                                        $(COMMON_COMPILE_OPT) -x assembler-with-cpp
+LINK_OPT                             += $(LCORE_OPT_GNU) \
+                                        -mno-sdata -nostartfiles --verbose
+
+arcem11d_target_ABI                   = arcem11d
+arcem11d_target_CPPFLAGS              = $(CXX_COMPILE_OPT)
+arcem11d_target_CFLAGS                = $(COMPILE_OPT)
+arcem11d_target_CXXFLAGS              = $(CXX_COMPILE_OPT)
+arcem11d_target_CCASFLAGS             = $(ASM_OPT)
+arcem11d_target_LDFLAGS               = $(LINK_OPT)
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-kw41z b/examples/Makefile-kw41z
new file mode 100644
index 0000000..3f22803
--- /dev/null
+++ b/examples/Makefile-kw41z
@@ -0,0 +1,261 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+CCAS                            = arm-none-eabi-as
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS               = \
+    --enable-cli-app=all          \
+    --enable-ncp-app=all          \
+    --with-ncp-bus=uart           \
+    --enable-diag                 \
+    --with-examples=kw41z         \
+    --with-platform-info=KW41Z    \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+CONFIG_FILE      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-kw41z-config.h\"'
+CONFIG_FILE_PATH = $(AbsTopSourceDir)/examples/platforms/kw41z/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = kw41z
+
+ARCHS                           = cortex-m0plus
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# cortex-m0plus
+#
+
+cortex-m0plus_target_ABI                  = cortex-m0plus
+cortex-m0plus_target_CPPFLAGS             = -mcpu=cortex-m0plus -mfloat-abi=soft -mthumb
+cortex-m0plus_target_CFLAGS               = -mcpu=cortex-m0plus -mfloat-abi=soft -mthumb
+cortex-m0plus_target_CXXFLAGS             = -mcpu=cortex-m0plus -mfloat-abi=soft -mthumb
+cortex-m0plus_target_LDFLAGS              = -mcpu=cortex-m0plus -mfloat-abi=soft -mthumb
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-nrf52840 b/examples/Makefile-nrf52840
new file mode 100644
index 0000000..3c7a0b4
--- /dev/null
+++ b/examples/Makefile-nrf52840
@@ -0,0 +1,285 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+.NOTPARALLEL:
+
+AR                              = arm-none-eabi-ar
+CCAS                            = arm-none-eabi-gcc
+CPP                             = arm-none-eabi-cpp
+CC                              = arm-none-eabi-gcc
+CXX                             = arm-none-eabi-g++
+LD                              = arm-none-eabi-ld
+STRIP                           = arm-none-eabi-strip
+NM                              = arm-none-eabi-nm
+RANLIB                          = arm-none-eabi-ranlib
+OBJCOPY                         = arm-none-eabi-objcopy
+
+BuildJobs                      ?= 10
+
+configure_OPTIONS                                 = \
+    --enable-cli-app=all                            \
+    --enable-ncp-app=all                            \
+    --with-ncp-bus=uart                             \
+    --enable-diag                                   \
+    --with-examples=nrf52840                        \
+    --with-platform-info=NRF52840                   \
+    MBEDTLS_CPPFLAGS="$(NRF52840_MBEDTLS_CPPFLAGS)" \
+    $(NULL)
+
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk
+
+ifdef SRC_PATH
+configure_OPTIONS              += --srcdir="$(SRC_PATH)"
+endif
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+NRF52840_MBEDTLS_CPPFLAGS  = -DMBEDTLS_CONFIG_FILE='\"mbedtls-config.h\"'
+NRF52840_MBEDTLS_CPPFLAGS += -DMBEDTLS_USER_CONFIG_FILE='\"nrf52840-mbedtls-config.h\"'
+NRF52840_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/examples/platforms/nrf52840/crypto
+NRF52840_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls
+NRF52840_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls/repo/include
+NRF52840_MBEDTLS_CPPFLAGS += -I$(AbsTopSourceDir)/third_party/mbedtls/repo/include/mbedtls
+
+CONFIG_FILE      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-nrf52840-config.h\"'
+CONFIG_FILE_PATH = $(AbsTopSourceDir)/examples/platforms/nrf52840/
+
+COMMONCFLAGS                   := \
+    -fdata-sections               \
+    -ffunction-sections           \
+    -Os                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+ifeq ($(CERT_LOG),1)
+COMMONCFLAGS += -DOPENTHREAD_CONFIG_ENABLE_DEFAULT_LOG_OUTPUT=1
+endif
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CPPFLAGS)            \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(target_CFLAGS)              \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(target_CXXFLAGS)            \
+    -fno-exceptions               \
+    -fno-rtti                     \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(target_LDFLAGS)             \
+    -specs=nano.specs             \
+    -specs=nosys.specs            \
+    -Wl,--gc-sections             \
+    -Wl,-Map=map.map              \
+    $(NULL)
+
+CCASFLAGS                       += \
+    $(COMMONCCASFLAGS)             \
+    $(target_CCASFLAGS)            \
+    -x                             \
+    assembler-with-cpp             \
+    $(NULL)
+
+ECHO                            := @echo
+MAKE                            := make
+MKDIR_P                         := mkdir -p
+LN_S                            := ln -s
+RM_F                            := rm -f
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = nrf52840
+
+ARCHS                           = cortex-m4
+
+TopTargetLibDir                 = $(TopResultDir)/$(TargetTuple)/lib
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <arch>
+#
+# Configure OpenThread for the specified architecture.
+#
+#   arch - The architecture to configure.
+#
+define configure-arch
+$(ECHO) "  CONFIG   $(TargetTuple)..."
+(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" CCAS="$(CCAS)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CCASFLAGS="$(CCASFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--host=arm-none-eabi \
+--prefix=/ \
+--exec-prefix=/$(TargetTuple) \
+$(configure_OPTIONS))
+endef # configure-arch
+
+#
+# build-arch <arch>
+#
+# Build the OpenThread intermediate build products for the specified
+# architecture.
+#
+#   arch - The architecture to build.
+#
+define build-arch
+$(ECHO) "  BUILD    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+all
+endef # build-arch
+
+#
+# stage-arch <arch>
+#
+# Stage (install) the OpenThread final build products for the specified
+# architecture.
+#
+#   arch - The architecture to stage.
+#
+define stage-arch
+$(ECHO) "  STAGE    $(TargetTuple)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-arch
+
+#
+# ARCH_template <arch>
+#
+# Define macros, targets and rules to configure, build, and stage the
+# OpenThread for a single architecture.
+#
+#   arch - The architecture to instantiate the template for.
+#
+define ARCH_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(TargetTuple)
+DIRECTORIES       += $(BuildPath)/$(TargetTuple)
+
+configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS)
+configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS)
+configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS)
+configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS)
+configure-$(1): target_CCASFLAGS=$($(1)_target_CCASFLAGS)
+
+configure-$(1): $(BuildPath)/$(TargetTuple)/config.status
+
+$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple)
+	$$(call configure-arch,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-arch,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-arch,$(1))
+
+$(1): stage-$(1)
+endef # ARCH_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+#
+# Cortex-m4
+#
+
+cortex-m4_target_ABI                  = cortex-m4
+cortex-m4_target_CPPFLAGS             = -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -mabi=aapcs
+cortex-m4_target_CFLAGS               = -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -mabi=aapcs
+cortex-m4_target_CXXFLAGS             = -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -mabi=aapcs
+cortex-m4_target_LDFLAGS              = -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mthumb -mabi=aapcs
+
+# Instantiate an architecture-specific build template for each target
+# architecture.
+
+$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch))))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "architectures: "
+	$(ECHO) ""
+	$(ECHO) "    $(ARCHS)"
+	$(ECHO) ""
+	$(ECHO) "To build only a particular architecture, specify: "
+	$(ECHO) ""
+	$(ECHO) "    make -f $(firstword $(MAKEFILE_LIST)) <architecture>"
+	$(ECHO) ""
diff --git a/examples/Makefile-posix b/examples/Makefile-posix
new file mode 100644
index 0000000..7b37b33
--- /dev/null
+++ b/examples/Makefile-posix
@@ -0,0 +1,300 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+# Don't allow this top-level makefile's targets to be built in parallel.
+
+.NOTPARALLEL:
+
+COVERAGE                       ?= 0
+DEBUG                          ?= 0
+
+ECHO                           := @echo
+MAKE                           := make
+MKDIR_P                        := mkdir -p
+LN_S                           := ln -s
+RM_F                           := rm -f
+
+BuildJobs                      ?= 10
+
+TopSourceDir                   := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+CONFIG_FILE      = OPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-posix-config.h\"'
+CONFIG_FILE_PATH = $(AbsTopSourceDir)/examples/platforms/posix/
+
+COMMONCFLAGS                   := \
+    -O1                           \
+    -g                            \
+    -D$(CONFIG_FILE)              \
+    -I$(CONFIG_FILE_PATH)         \
+    $(NULL)
+
+CPPFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(NULL)
+
+CFLAGS                         += \
+    $(COMMONCFLAGS)               \
+    $(NULL)
+
+CXXFLAGS                       += \
+    $(COMMONCFLAGS)               \
+    $(NULL)
+
+LDFLAGS                        += \
+    $(COMMONCFLAGS)               \
+    $(NULL)
+
+INSTALL                         := /usr/bin/install
+INSTALLFLAGS                    := -p
+
+TopSourceDir                    := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))..
+AbsTopSourceDir                 := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))..
+
+BuildPath                       = build
+TopBuildDir                     = $(BuildPath)
+AbsTopBuildDir                  = $(PWD)/$(TopBuildDir)
+
+ResultPath                      = output
+TopResultDir                    = $(ResultPath)
+AbsTopResultDir                 = $(PWD)/$(TopResultDir)
+
+TargetTuple                     = $(shell ${AbsTopSourceDir}/third_party/nlbuild-autotools/repo/autoconf/config.guess | sed -e 's/[[:digit:].]*$$//g')
+
+# If the user has asserted COVERAGE, alter the configuration options
+# accordingly.
+
+configure_OPTIONS                   = \
+    --enable-cli-app=all              \
+    --enable-ncp-app=all              \
+    --with-ncp-bus=uart               \
+    --enable-diag                     \
+    --enable-raw-link-api=yes         \
+    --with-examples=posix             \
+    --with-platform-info=POSIX        \
+    --enable-application-coap         \
+    --enable-tmf-proxy                \
+    --enable-cert-log                 \
+    --enable-commissioner             \
+    --enable-dhcp6-client             \
+    --enable-dhcp6-server             \
+    --enable-dns-client               \
+    --enable-jam-detection            \
+    --enable-joiner                   \
+    --enable-legacy                   \
+    --enable-mac-whitelist            \
+    --enable-mtd-network-diagnostic   \
+    --enable-border-router            \
+    $(NULL)
+
+ifndef BuildJobs
+BuildJobs := $(shell getconf _NPROCESSORS_ONLN)
+endif
+JOBSFLAG := -j$(BuildJobs)
+
+#
+# configure-arch <target>
+#
+# Configure OpenThread for the specified target.
+#
+#   target - The target to configure.
+#
+define configure-target
+$(ECHO) "  CONFIG   $(1)..."
+(cd $(BuildPath)/$(1) && $(AbsTopSourceDir)/configure \
+INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \
+--prefix=/ \
+--exec-prefix=/$(1) \
+$(configure_OPTIONS))
+endef # configure-target
+
+#
+# build-target <target>
+#
+# Build the OpenThread intermediate build products for the specified
+# target.
+#
+#   target - The target to build.
+#
+define build-target
+$(ECHO) "  BUILD    $(1)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \
+all
+endef # build-target
+
+#
+# check-target <target>
+#
+# Check (run unit tests) OpenThread for the specified target.
+#
+#   target - The target to check.
+#
+define check-target
+$(ECHO) "  CHECK    $(1)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \
+check
+endef # check-target
+
+#
+# distcheck-target <target>
+#
+# Check (run unit tests) OpenThread for the specified target.
+#
+#   target - The target to distcheck.
+#
+define distcheck-target
+$(ECHO) "  DISTCHECK    $(1)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \
+distcheck
+endef # distcheck-target
+
+#
+# coverage-target <target>
+#
+# Generate code coverage from unit tests for OpenThread for the
+# specified target.
+#
+#   target - The target to generate code coverage for.
+#
+define coverage-target
+$(ECHO) "  COVERAGE $(1)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \
+coverage
+endef # coverage-target
+
+#
+# stage-target <target>
+#
+# Stage (install) the OpenThread final build products for the specified
+# target.
+#
+#   target - The target to stage.
+#
+define stage-target
+$(ECHO) "  STAGE    $(1)"
+$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(1) --no-print-directory \
+DESTDIR=$(AbsTopResultDir) \
+install
+endef # stage-target
+
+#
+# TARGET_template <target>
+#
+# Define macros, targets and rules to configure, build, and stage
+# OpenThread for a single target.
+#
+#   target - The target to instantiate the template for.
+#
+define TARGET_template
+CONFIGURE_TARGETS += configure-$(1)
+BUILD_TARGETS     += do-build-$(1)
+CHECK_TARGETS     += check-$(1)
+DISTCHECK_TARGETS += distcheck-$(1)
+COVERAGE_TARGETS  += coverage-$(1)
+STAGE_TARGETS     += stage-$(1)
+BUILD_DIRS        += $(BuildPath)/$(1)
+DIRECTORIES       += $(BuildPath)/$(1)
+
+configure-$(1): $(BuildPath)/$(1)/config.status
+
+$(BuildPath)/$(1)/config.status: | $(BuildPath)/$(1)
+	$$(call configure-target,$(1))
+
+do-build-$(1): configure-$(1)
+
+do-build-$(1):
+	+$$(call build-target,$(1))
+
+check-$(1): do-build-$(1)
+
+check-$(1):
+	+$$(call check-target,$(1))
+
+distcheck-$(1): do-build-$(1)
+
+distcheck-$(1):
+	+$$(call distcheck-target,$(1))
+
+coverage-$(1): do-build-$(1)
+
+coverage-$(1):
+	+$$(call coverage-target,$(1))
+
+stage-$(1): do-build-$(1)
+
+stage-$(1): | $(TopResultDir)
+	$$(call stage-target,$(1))
+
+$(1): stage-$(1)
+endef # TARGET_template
+
+.DEFAULT_GOAL := all
+
+all: stage
+
+# Instantiate an target-specific build template for the target.
+
+$(eval $(call TARGET_template,$(TargetTuple)))
+
+#
+# Common / Finalization
+#
+
+configure: $(CONFIGURE_TARGETS)
+
+build: $(BUILD_TARGETS)
+
+check: $(CHECK_TARGETS)
+
+distcheck: $(DISTCHECK_TARGETS)
+
+coverage: $(COVERAGE_TARGETS)
+
+stage: $(STAGE_TARGETS)
+
+DIRECTORIES     = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS)
+
+CLEAN_DIRS      = $(TopResultDir) $(BUILD_DIRS)
+
+all: stage
+
+$(DIRECTORIES):
+	$(ECHO) "  MKDIR    $@"
+	@$(MKDIR_P) "$@"
+
+clean:
+	$(ECHO) "  CLEAN"
+	@$(RM_F) -r $(CLEAN_DIRS)
+
+help:
+	$(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following "
+	$(ECHO) "target:"
+	$(ECHO) ""
+	$(ECHO) "    $(TargetTuple)"
+	$(ECHO) ""
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..e0b1051
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,61 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+# Always package (e.g. for 'make dist') these subdirectories.
+
+DIST_SUBDIRS                            = \
+    platforms                             \
+    apps                                  \
+    $(NULL)
+
+EXTRA_DIST                              = \
+    drivers                               \
+    $(NULL)
+
+# Always build (e.g. for 'make all') these subdirectories.
+
+SUBDIRS                                 = \
+    platforms                             \
+    $(NULL)
+    
+if OPENTHREAD_EXAMPLES
+SUBDIRS                                += \
+    apps                                  \
+    $(NULL)
+endif
+
+# Always pretty (e.g. for 'make pretty') these subdirectories.
+
+PRETTY_SUBDIRS                          = \
+    platforms                             \
+    apps                                  \
+    $(NULL)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/apps/Makefile.am b/examples/apps/Makefile.am
new file mode 100644
index 0000000..60345f6
--- /dev/null
+++ b/examples/apps/Makefile.am
@@ -0,0 +1,58 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+# Always package (e.g. for 'make dist') these subdirectories.
+
+DIST_SUBDIRS                            = \
+    cli                                   \
+    ncp                                   \
+    $(NULL)
+
+# Always build (e.g. for 'make all') these subdirectories.
+
+SUBDIRS                                 = \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_CLI
+SUBDIRS                                += cli
+endif
+
+if OPENTHREAD_ENABLE_NCP
+SUBDIRS                                += ncp
+endif
+
+# Always pretty (e.g. for 'make pretty') these subdirectories.
+
+PRETTY_SUBDIRS                          = \
+    cli                                   \
+    ncp                                   \
+    $(NULL)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/apps/cli/Makefile.am b/examples/apps/cli/Makefile.am
new file mode 100644
index 0000000..63c26a0
--- /dev/null
+++ b/examples/apps/cli/Makefile.am
@@ -0,0 +1,198 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+bin_PROGRAMS                                                           = \
+    $(NULL)
+
+CPPFLAGS_COMMON                                                        = \
+    -I$(top_srcdir)/include                                              \
+    -I$(top_srcdir)/src/core                                             \
+    -I$(top_srcdir)/examples/platforms                                   \
+    $(NULL)
+
+LDADD_COMMON                                                           = \
+    $(NULL)
+
+LDFLAGS_COMMON                                                         = \
+    $(NULL)
+
+SOURCES_COMMON                                                         = \
+    main.c                                                               \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
+LDADD_COMMON                                                          += \
+    $(top_builddir)/third_party/mbedtls/libmbedcrypto.a                  \
+    $(NULL)
+endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
+
+if OPENTHREAD_ENABLE_DIAG
+LDADD_COMMON                                                          += \
+    $(top_builddir)/src/diag/libopenthread-diag.a                        \
+    $(NULL)
+endif # OPENTHREAD_ENABLE_DIAG
+
+if OPENTHREAD_EXAMPLES_POSIX
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/posix/libopenthread-posix.a       \
+    -lstdc++                                                             \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_POSIX
+
+if OPENTHREAD_EXAMPLES_CC2538
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/cc2538/libopenthread-cc2538.a     \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/cc2538/cc2538.ld                 \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_CC2538
+
+if OPENTHREAD_EXAMPLES_CC2650
+CPPFLAGS_COMMON                                                       += \
+    -I$(top_srcdir)/third_party/ti/cc26xxware                            \
+    $(NULL)
+
+LDADD_COMMON                                                          +=  \
+    $(top_builddir)/examples/platforms/cc2650/libopenthread-cc2650.a      \
+    $(top_srcdir)/third_party/ti/cc26xxware/driverlib/bin/gcc/driverlib.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        +=   \
+    -T $(top_srcdir)/third_party/ti/cc26xxware/linker_files/cc26x0f128.lds \
+    $(NULL)
+
+endif #OPENTHREAD_EXAMPLES_CC2650
+
+if OPENTHREAD_EXAMPLES_DA15000
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/da15000/libopenthread-da15000.a   \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/da15000/da15000.ld               \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_DA15000
+
+if OPENTHREAD_EXAMPLES_EFR32
+    LDADD_COMMON                                                      += \
+    $(top_builddir)/examples/platforms/efr32/libopenthread-efr32.a       \
+    $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/radio/rail_lib/autogen/librail_release/librail_efr32xg12_gcc_release.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/Device/SiliconLabs/EFR32MG12P/Source/GCC/efr32mg12p.ld \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_EFR32
+
+if OPENTHREAD_EXAMPLES_EMSK
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/emsk/libopenthread-emsk.a         \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/emsk/emsk.ld                     \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_EMSK
+
+if OPENTHREAD_EXAMPLES_NRF52840
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/nrf52840/libopenthread-nrf52840.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/nrf52840/nrf52840.ld             \
+    $(NULL)
+endif
+
+if OPENTHREAD_EXAMPLES_KW41Z
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/kw41z/libopenthread-kw41z.a       \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/kw41z/MKW41Z512xxx4.ld           \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_KW41Z
+
+if OPENTHREAD_ENABLE_CLI_FTD
+bin_PROGRAMS                                                          += \
+    ot-cli-ftd                                                           \
+    $(NULL)
+endif
+
+ot_cli_ftd_CPPFLAGS                                                    = \
+    $(CPPFLAGS_COMMON)                                                   \
+    $(NULL)
+
+ot_cli_ftd_LDADD                                                       = \
+    $(top_builddir)/src/cli/libopenthread-cli-ftd.a                      \
+    $(top_builddir)/src/core/libopenthread-ftd.a                         \
+    $(LDADD_COMMON)                                                      \
+    $(NULL)
+
+ot_cli_ftd_LDFLAGS                                                     = \
+    $(LDFLAGS_COMMON)                                                    \
+    $(NULL)
+
+ot_cli_ftd_SOURCES                                                     = \
+    $(SOURCES_COMMON)                                                    \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_CLI_MTD
+bin_PROGRAMS                                                          += \
+    ot-cli-mtd                                                           \
+    $(NULL)
+endif
+
+ot_cli_mtd_CPPFLAGS                                                    = \
+    $(CPPFLAGS_COMMON)                                                   \
+    $(NULL)
+
+ot_cli_mtd_LDADD                                                       = \
+    $(top_builddir)/src/cli/libopenthread-cli-mtd.a                      \
+    $(top_builddir)/src/core/libopenthread-mtd.a                         \
+    $(LDADD_COMMON)                                                      \
+    $(NULL)
+
+ot_cli_mtd_LDFLAGS                                                     = \
+    $(LDFLAGS_COMMON)                                                    \
+    $(NULL)
+
+ot_cli_mtd_SOURCES                                                     = \
+    $(SOURCES_COMMON)                                                    \
+    $(NULL)
+
+if OPENTHREAD_BUILD_COVERAGE
+CLEANFILES                                                             = $(wildcard *.gcda *.gcno)
+endif # OPENTHREAD_BUILD_COVERAGE
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/apps/cli/README.md b/examples/apps/cli/README.md
new file mode 100644
index 0000000..9dd19be
--- /dev/null
+++ b/examples/apps/cli/README.md
@@ -0,0 +1,112 @@
+# OpenThread CLI Example
+
+This example application demonstrates a minimal OpenThread application
+that exposes the OpenThread configuration and management interfaces
+via a basic command-line interface. The steps below take you through
+the minimal steps required to ping one emulated Thread device from
+another emulated Thread device.
+
+## 1. Build
+
+```bash
+$ cd <path-to-openthread>
+$ ./bootstrap
+$ make -f examples/Makefile-posix
+```
+
+## 2. Start node 1
+
+Spawn the process:
+
+```bash
+$ cd <path-to-openthread>/output/<platform>/bin
+$ ./ot-cli-ftd 1
+```
+
+Set the PAN ID:
+
+```bash
+> panid 0x1234
+```
+
+Bring up the IPv6 interface:
+
+```bash
+> ifconfig up
+Done
+```
+
+Start Thread protocol operation:
+
+```bash
+> thread start
+Done
+```
+
+Wait a few seconds and verify that the device has become a Thread Leader:
+
+```bash
+> state
+leader
+Done
+```
+
+View IPv6 addresses assigned to Node 1's Thread interface:
+
+```bash
+> ipaddr
+fdde:ad00:beef:0:0:ff:fe00:0
+fdde:ad00:beef:0:558:f56b:d688:799
+fe80:0:0:0:f3d9:2a82:c8d8:fe43
+Done
+```
+
+## 2. Start node 2
+
+Spawn the process:
+
+```bash
+$ cd <path-to-openthread>/output/<platform>/bin
+$ ./ot-cli-ftd 2
+```
+
+Set the PAN ID:
+
+```bash
+> panid 0x1234
+```
+
+Bring up the IPv6 interface:
+
+```bash
+> ifconfig up
+Done
+```
+
+Start Thread protocol operation:
+
+```bash
+> thread start
+Done
+```
+
+Wait a few seconds and verify that the device has become a Thread Router:
+
+```bash
+> state
+router
+Done
+```
+
+## 3. Ping Node 1 from Node 2
+
+```bash
+> ping fdde:ad00:beef:0:558:f56b:d688:799
+16 bytes from fdde:ad00:beef:0:558:f56b:d688:799: icmp_seq=1 hlim=64
+```
+
+## 4. Want more?
+
+You may note that the example above did not include any network parameter configuration, such as the IEEE 802.15.4 PAN ID or the Thread Master Key. OpenThread currently implements default values for network parameters, however, you may use the CLI to change network parameters, other configurations, and perform other operations.
+
+See the [OpenThread CLI Reference README.md](../../../src/cli/README.md) to explore more.
diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c
new file mode 100644
index 0000000..17d4893
--- /dev/null
+++ b/examples/apps/cli/main.c
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/config.h>
+
+#include <assert.h>
+
+#include <openthread/cli.h>
+#include <openthread/diag.h>
+#include <openthread/openthread.h>
+#include <openthread/platform/platform.h>
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+void *otPlatCAlloc(size_t aNum, size_t aSize)
+{
+    return calloc(aNum, aSize);
+}
+
+void otPlatFree(void *aPtr)
+{
+    free(aPtr);
+}
+#endif
+
+void otTaskletsSignalPending(otInstance *aInstance)
+{
+    (void)aInstance;
+}
+
+int main(int argc, char *argv[])
+{
+    otInstance *sInstance;
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    size_t otInstanceBufferLength = 0;
+    uint8_t *otInstanceBuffer = NULL;
+#endif
+
+    PlatformInit(argc, argv);
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    // Call to query the buffer size
+    (void)otInstanceInit(NULL, &otInstanceBufferLength);
+
+    // Call to allocate the buffer
+    otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength);
+    assert(otInstanceBuffer);
+
+    // Initialize OpenThread with the buffer
+    sInstance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength);
+#else
+    sInstance = otInstanceInitSingle();
+#endif
+    assert(sInstance);
+
+    otCliUartInit(sInstance);
+
+#if OPENTHREAD_ENABLE_DIAG
+    otDiagInit(sInstance);
+#endif
+
+    while (1)
+    {
+        otTaskletsProcess(sInstance);
+        PlatformProcessDrivers(sInstance);
+    }
+
+    // otInstanceFinalize(sInstance);
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    // free(otInstanceBuffer);
+#endif
+
+    return 0;
+}
diff --git a/examples/apps/ncp/Makefile.am b/examples/apps/ncp/Makefile.am
new file mode 100644
index 0000000..7ffe392
--- /dev/null
+++ b/examples/apps/ncp/Makefile.am
@@ -0,0 +1,198 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+bin_PROGRAMS                                                           = \
+    $(NULL)
+
+CPPFLAGS_COMMON                                                        = \
+    -I$(top_srcdir)/include                                              \
+    -I$(top_srcdir)/src/core                                             \
+    -I$(top_srcdir)/examples/platforms                                   \
+    $(NULL)
+
+LDADD_COMMON                                                           = \
+    $(NULL)
+
+LDFLAGS_COMMON                                                         = \
+    $(NULL)
+
+SOURCES_COMMON                                                         = \
+    main.c                                                               \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
+LDADD_COMMON                                                          += \
+    $(top_builddir)/third_party/mbedtls/libmbedcrypto.a                  \
+    $(NULL)
+endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
+
+if OPENTHREAD_ENABLE_DIAG
+LDADD_COMMON                                                          += \
+    $(top_builddir)/src/diag/libopenthread-diag.a                        \
+    $(NULL)
+endif
+
+if OPENTHREAD_EXAMPLES_POSIX
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/posix/libopenthread-posix.a       \
+    -lstdc++                                                             \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_POSIX
+
+if OPENTHREAD_EXAMPLES_CC2538
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/cc2538/libopenthread-cc2538.a     \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/cc2538/cc2538.ld                 \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_CC2538
+
+if OPENTHREAD_EXAMPLES_CC2650
+CPPFLAGS_COMMON                                                       += \
+    -I$(top_srcdir)/third_party/ti/cc26xxware                            \
+    $(NULL)
+
+LDADD_COMMON                                                          +=  \
+    $(top_builddir)/examples/platforms/cc2650/libopenthread-cc2650.a      \
+    $(top_srcdir)/third_party/ti/cc26xxware/driverlib/bin/gcc/driverlib.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        +=   \
+    -T $(top_srcdir)/third_party/ti/cc26xxware/linker_files/cc26x0f128.lds \
+    $(NULL)
+
+endif # OPENTHREAD_EXAMPLES_CC2650
+
+if OPENTHREAD_EXAMPLES_DA15000
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/da15000/libopenthread-da15000.a   \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/da15000/da15000.ld               \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_DA15000
+
+if OPENTHREAD_EXAMPLES_EFR32
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/efr32/libopenthread-efr32.a       \
+    $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/radio/rail_lib/autogen/librail_release/librail_efr32xg12_gcc_release.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v1.1/platform/Device/SiliconLabs/EFR32MG12P/Source/GCC/efr32mg12p.ld \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_EFR32
+
+if OPENTHREAD_EXAMPLES_EMSK
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/emsk/libopenthread-emsk.a         \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/emsk/emsk.ld                     \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_EMSK
+
+if OPENTHREAD_EXAMPLES_NRF52840
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/nrf52840/libopenthread-nrf52840.a \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/nrf52840/nrf52840.ld             \
+    $(NULL)
+endif
+
+if OPENTHREAD_EXAMPLES_KW41Z
+LDADD_COMMON                                                          += \
+    $(top_builddir)/examples/platforms/kw41z/libopenthread-kw41z.a       \
+    $(NULL)
+
+LDFLAGS_COMMON                                                        += \
+    -T $(top_srcdir)/examples/platforms/kw41z/MKW41Z512xxx4.ld           \
+    $(NULL)
+endif # OPENTHREAD_EXAMPLES_KW41Z
+
+if OPENTHREAD_ENABLE_NCP_FTD
+bin_PROGRAMS                                                          += \
+    ot-ncp-ftd                                                           \
+    $(NULL)
+endif
+
+ot_ncp_ftd_CPPFLAGS                                                    = \
+    $(CPPFLAGS_COMMON)                                                   \
+    $(NULL)
+
+ot_ncp_ftd_LDADD                                                       = \
+    $(top_builddir)/src/ncp/libopenthread-ncp-ftd.a                      \
+    $(top_builddir)/src/core/libopenthread-ftd.a                         \
+    $(LDADD_COMMON)                                                      \
+    $(NULL)
+
+ot_ncp_ftd_LDFLAGS                                                     = \
+    $(LDFLAGS_COMMON)                                                    \
+    $(NULL)
+
+ot_ncp_ftd_SOURCES                                                     = \
+    $(SOURCES_COMMON)                                                    \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_NCP_MTD
+bin_PROGRAMS                                                          += \
+    ot-ncp-mtd                                                           \
+    $(NULL)
+endif
+
+ot_ncp_mtd_CPPFLAGS                                                    = \
+    $(CPPFLAGS_COMMON)                                                   \
+    $(NULL)
+
+ot_ncp_mtd_LDADD                                                       = \
+    $(top_builddir)/src/ncp/libopenthread-ncp-mtd.a                      \
+    $(top_builddir)/src/core/libopenthread-mtd.a                         \
+    $(LDADD_COMMON)                                                      \
+    $(NULL)
+
+ot_ncp_mtd_LDFLAGS                                                     = \
+    $(LDFLAGS_COMMON)                                                    \
+    $(NULL)
+
+ot_ncp_mtd_SOURCES                                                     = \
+    $(SOURCES_COMMON)                                                    \
+    $(NULL)
+
+if OPENTHREAD_BUILD_COVERAGE
+CLEANFILES                                                             = $(wildcard *.gcda *.gcno)
+endif # OPENTHREAD_BUILD_COVERAGE
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/apps/ncp/main.c b/examples/apps/ncp/main.c
new file mode 100644
index 0000000..5f53218
--- /dev/null
+++ b/examples/apps/ncp/main.c
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/config.h>
+
+#include <assert.h>
+
+#include <openthread/diag.h>
+#include <openthread/ncp.h>
+#include <openthread/openthread.h>
+#include <openthread/platform/platform.h>
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+void *otPlatCAlloc(size_t aNum, size_t aSize)
+{
+    return calloc(aNum, aSize);
+}
+
+void otPlatFree(void *aPtr)
+{
+    free(aPtr);
+}
+#endif
+
+void otTaskletsSignalPending(otInstance *aInstance)
+{
+    (void)aInstance;
+}
+
+int main(int argc, char *argv[])
+{
+    otInstance *sInstance;
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    size_t otInstanceBufferLength = 0;
+    uint8_t *otInstanceBuffer = NULL;
+#endif
+
+    PlatformInit(argc, argv);
+
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    // Call to query the buffer size
+    (void)otInstanceInit(NULL, &otInstanceBufferLength);
+
+    // Call to allocate the buffer
+    otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength);
+    assert(otInstanceBuffer);
+
+    // Initialize OpenThread with the buffer
+    sInstance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength);
+#else
+    sInstance = otInstanceInitSingle();
+#endif
+    assert(sInstance);
+
+    otNcpInit(sInstance);
+
+#if OPENTHREAD_ENABLE_DIAG
+    otDiagInit(sInstance);
+#endif
+
+    while (1)
+    {
+        otTaskletsProcess(sInstance);
+        PlatformProcessDrivers(sInstance);
+    }
+
+    // otInstanceFinalize(sInstance);
+#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
+    // free(otInstanceBuffer);
+#endif
+
+    return 0;
+}
diff --git a/examples/apps/windows/App.xaml b/examples/apps/windows/App.xaml
new file mode 100644
index 0000000..10b02c4
--- /dev/null
+++ b/examples/apps/windows/App.xaml
@@ -0,0 +1,35 @@
+<!--
+  Copyright (c) 2016, The OpenThread Authors.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the copyright holder nor the
+     names of its contributors may be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+-->
+<Application
+    x:Class="ot.App"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:ot"
+    RequestedTheme="Dark">
+
+</Application>
diff --git a/examples/apps/windows/App.xaml.cpp b/examples/apps/windows/App.xaml.cpp
new file mode 100644
index 0000000..cf33b54
--- /dev/null
+++ b/examples/apps/windows/App.xaml.cpp
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include "MainPage.xaml.h"
+
+using namespace ot;
+
+using namespace Platform;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Interop;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+/// <summary>
+/// Initializes the singleton application object.  This is the first line of authored code
+/// executed, and as such is the logical equivalent of main() or WinMain().
+/// </summary>
+App::App()
+{
+    InitializeComponent();
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
+}
+
+/// <summary>
+/// Invoked when the application is launched normally by the end user.  Other entry points
+/// will be used such as when the application is launched to open a specific file.
+/// </summary>
+/// <param name="e">Details about the launch request and process.</param>
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e)
+{
+#if _DEBUG
+    // Show graphics profiling information while debugging.
+    if (IsDebuggerPresent())
+    {
+        // Display the current frame rate counters
+         DebugSettings->EnableFrameRateCounter = true;
+    }
+#endif
+    auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
+
+    // Do not repeat app initialization when the Window already has content,
+    // just ensure that the window is active
+    if (rootFrame == nullptr)
+    {
+        // Create a Frame to act as the navigation context and associate it with
+        // a SuspensionManager key
+        rootFrame = ref new Frame();
+
+        rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed);
+
+        if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
+        {
+            // TODO: Restore the saved session state only when appropriate, scheduling the
+            // final launch steps after the restore is complete
+        }
+
+        if (e->PrelaunchActivated == false)
+        {
+            if (rootFrame->Content == nullptr)
+            {
+                // When the navigation stack isn't restored navigate to the first page,
+                // configuring the new page by passing required information as a navigation
+                // parameter
+                rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);
+            }
+            // Place the frame in the current Window
+            Window::Current->Content = rootFrame;
+            // Ensure the current window is active
+            Window::Current->Activate();
+        }
+    }
+    else
+    {
+        if (e->PrelaunchActivated == false)
+        {
+            if (rootFrame->Content == nullptr)
+            {
+                // When the navigation stack isn't restored navigate to the first page,
+                // configuring the new page by passing required information as a navigation
+                // parameter
+                rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);
+            }
+            // Ensure the current window is active
+            Window::Current->Activate();
+        }
+    }
+}
+
+/// <summary>
+/// Invoked when application execution is being suspended.  Application state is saved
+/// without knowing whether the application will be terminated or resumed with the contents
+/// of memory still intact.
+/// </summary>
+/// <param name="sender">The source of the suspend request.</param>
+/// <param name="e">Details about the suspend request.</param>
+void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
+{
+    (void) sender;  // Unused parameter
+    (void) e;   // Unused parameter
+
+    //TODO: Save application state and stop any background activity
+}
+
+/// <summary>
+/// Invoked when Navigation to a certain page fails
+/// </summary>
+/// <param name="sender">The Frame which failed navigation</param>
+/// <param name="e">Details about the navigation failure</param>
+void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e)
+{
+    throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name);
+}
diff --git a/examples/apps/windows/App.xaml.h b/examples/apps/windows/App.xaml.h
new file mode 100644
index 0000000..0449c89
--- /dev/null
+++ b/examples/apps/windows/App.xaml.h
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "App.g.h"
+
+namespace ot
+{
+    /// <summary>
+    /// Provides application-specific behavior to supplement the default Application class.
+    /// </summary>
+    ref class App sealed
+    {
+    protected:
+        virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override;
+
+    internal:
+        App();
+
+    private:
+        void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);
+        void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e);
+    };
+}
diff --git a/examples/apps/windows/ClientArgs.h b/examples/apps/windows/ClientArgs.h
new file mode 100644
index 0000000..3943a11
--- /dev/null
+++ b/examples/apps/windows/ClientArgs.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public ref class ClientArgs sealed
+{
+public:
+    property Windows::Networking::HostName^ ServerHostName;
+    property Platform::String^              ServerPort;
+    property Windows::Networking::HostName^ ClientHostName;
+    property Platform::String^              ClientPort;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/ClientControl.xaml b/examples/apps/windows/ClientControl.xaml
new file mode 100644
index 0000000..4f00fbb
--- /dev/null
+++ b/examples/apps/windows/ClientControl.xaml
@@ -0,0 +1,142 @@
+<!--
+  Copyright (c) 2016, The OpenThread Authors.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the copyright holder nor the
+     names of its contributors may be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+-->
+<UserControl
+    x:Class="ot.ClientControl"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:ot"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="Auto"/>
+            <ColumnDefinition Width="20"/>
+            <ColumnDefinition Width="*"/>
+        </Grid.ColumnDefinitions>
+        <TextBlock
+            Grid.Row="0"
+            Grid.Column="0"
+            Text="Server IP :"
+            />
+        <TextBox
+            Grid.Row="0"
+            Grid.Column="2"
+            x:Name="ServerIP"
+            MinWidth="500"
+            />
+        <TextBlock
+            Grid.Row="2"
+            Grid.Column="0"
+            Text="Server Port :"
+            />
+        <TextBox
+            Grid.Row="2"
+            Grid.Column="2"
+            x:Name="ServerPort"
+            Width="100"
+            HorizontalAlignment="Left"
+            />
+        <TextBlock
+            Grid.Row="4"
+            Grid.Column="0"
+            Text="Client IP :"
+            />
+        <TextBox
+            Grid.Row="4"
+            Grid.Column="2"
+            x:Name="ClientIP"
+            MinWidth="500"
+            />
+        <TextBlock
+            Grid.Row="6"
+            Grid.Column="0"
+            Text="Client Port :"
+            />
+        <TextBox
+            Grid.Row="6"
+            Grid.Column="2"
+            x:Name="ClientPort"
+            Width="100"
+            HorizontalAlignment="Left"
+            />
+        <Button
+            Grid.Row="8"
+            Grid.Column="0"
+            Grid.ColumnSpan="3"
+            Width="75"
+            Content="Connect"
+            Click="Connect_Click"
+            />
+        <TextBlock
+            Grid.Row="10"
+            Grid.Column="0"
+            Text="Input :"
+            />
+        <TextBox
+            Grid.Row="10"
+            Grid.Column="2"
+            x:Name="Input"
+            MinWidth="300"
+            />
+        <StackPanel
+            Orientation="Horizontal"
+            VerticalAlignment="Top"
+            Grid.Row="12"
+            Grid.Column="0"
+            Grid.ColumnSpan="3"
+            >
+            <Button
+                Width="75"
+                Content="Send"
+                Click="Send_Click"
+                Margin="0,0,75,0"
+                />
+            <Button
+                Width="75"
+                Content="Exit"
+                Click="Exit_Click"
+                />
+        </StackPanel>
+    </Grid>
+</UserControl>
diff --git a/examples/apps/windows/ClientControl.xaml.cpp b/examples/apps/windows/ClientControl.xaml.cpp
new file mode 100644
index 0000000..cc8f40b
--- /dev/null
+++ b/examples/apps/windows/ClientControl.xaml.cpp
@@ -0,0 +1,188 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include <algorithm>
+#include "ClientControl.xaml.h"
+#include "Factory.h"
+#include "TalkHelper.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
+
+std::atomic<int> ClientControl::_clientPort{ TalkConsts::DEF_CLIENT_PORT_INIT };
+
+ClientControl::ClientControl()
+{
+    InitializeComponent();
+
+    ServerPort->Text = DEF_SERVER_PORT.ToString();
+    auto clientPort = _clientPort.load();
+    ClientPort->Text = clientPort.ToString();
+}
+
+void
+ClientControl::Init(
+    IAsyncThreadNotify^  notify,
+    IMainPageUIElements^ mainPageUIElements)
+{
+    _notify = std::move(notify);
+    _mainPageUIElements = std::move(mainPageUIElements);
+}
+
+void
+ClientControl::ProtocolChanged(
+    Protocol protocol)
+{
+    _protocol = protocol;
+}
+
+void
+ClientControl::Connect_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    try
+    {
+        auto clientArgs = ref new ClientArgs();
+
+        auto serverIP = ServerIP->Text;
+        if (serverIP->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Server IP");
+        }
+
+        // check valid chars of Ipv6 Address
+        if (!TalkHelper::AllValidIpv6Chars(serverIP->Data(), serverIP->Data() + serverIP->Length()))
+        {
+            throw Exception::CreateException(E_INVALIDARG, "Not a valid Server IPv6 address");
+        }
+
+        clientArgs->ServerHostName = ref new HostName(serverIP);
+
+        if (ServerPort->Text->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Server Port");
+        }
+        clientArgs->ServerPort = ServerPort->Text;
+
+        auto clientIP = ClientIP->Text;
+        if (clientIP->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Client IP");
+        }
+
+        // check valid chars of Ipv6 Address
+        if (!TalkHelper::AllValidIpv6Chars(clientIP->Data(), clientIP->Data() + clientIP->Length()))
+        {
+            throw Exception::CreateException(E_INVALIDARG, "Not a valid client IPv6 address");
+        }
+
+        clientArgs->ClientHostName = ref new HostName(clientIP);
+
+        if (ClientPort->Text->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Client Port");
+        }
+        clientArgs->ClientPort = ClientPort->Text;
+
+        auto cleintContext = Factory::CreateClientContext(_notify, clientArgs, _protocol);
+        cleintContext->Connect_Click(sender, e);
+
+        // fix Only usage of each socket address (protocol/network address/port)
+        // is normally permitted. 
+        auto clientPort = ++_clientPort;
+        ClientPort->Text = clientPort.ToString();
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread(
+            "Connecting failed with input error: " + ex->Message,
+            NotifyType::Error);
+    }
+}
+
+void
+ClientControl::Send_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    try
+    {
+        auto input = Input->Text;
+        if (input->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Input");
+        }
+
+        if (!CoreApplication::Properties->HasKey("clientContext"))
+        {
+            throw Exception::CreateException(E_UNEXPECTED, "Not Connected");
+        }
+
+        auto clientContext = dynamic_cast<IClientContext^>(
+            CoreApplication::Properties->Lookup("clientContext"));
+        if (clientContext == nullptr)
+        {
+            throw Exception::CreateException(E_UNEXPECTED, "No clientContext");
+        }
+
+        clientContext->Send_Click(sender, e, input);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread(
+            "Sending message failed with error: " + ex->Message,
+            NotifyType::Error);
+    }
+}
+
+void
+ClientControl::Exit_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    _mainPageUIElements->TalkGrid->Visibility = Xaml::Visibility::Collapsed;
+    _mainPageUIElements->ThreadGrid->Visibility = Xaml::Visibility::Visible;
+}
diff --git a/examples/apps/windows/ClientControl.xaml.h b/examples/apps/windows/ClientControl.xaml.h
new file mode 100644
index 0000000..8c247af
--- /dev/null
+++ b/examples/apps/windows/ClientControl.xaml.h
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <atomic>
+#include "ClientControl.g.h"
+#include "TalkConsts.h"
+#include "IAsyncThreadNotify.h"
+#include "IMainPageUIElements.h"
+#include "Protocol.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class ClientControl sealed
+{
+public:
+    ClientControl();
+
+    void Init(IAsyncThreadNotify^ notify, IMainPageUIElements^ mainPageUIElements);
+
+    void ProtocolChanged(Protocol protocol);
+
+private:
+    static constexpr unsigned short DEF_SERVER_PORT = TalkConsts::DEF_SERVER_PORT;
+
+    void Connect_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    void Send_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    void Exit_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    IAsyncThreadNotify^     _notify;
+    IMainPageUIElements^    _mainPageUIElements;
+    Protocol                _protocol;
+    static std::atomic<int> _clientPort;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/DatagramClientContext.cpp b/examples/apps/windows/DatagramClientContext.cpp
new file mode 100644
index 0000000..eb23b0d
--- /dev/null
+++ b/examples/apps/windows/DatagramClientContext.cpp
@@ -0,0 +1,286 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include "DatagramClientContext.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+DatagramClientContext::DatagramClientContext(
+    IAsyncThreadNotify^ notify,
+    DatagramSocket^     client,
+    ClientArgs^         args) :
+    _notify{ std::move(notify) },
+    _client{ std::move(client) },
+    _args{ std::move(args) }
+{
+}
+
+DatagramClientContext::~DatagramClientContext()
+{
+    // A Client can be closed in two ways:
+    //  - explicitly: using the 'delete' keyword (client is closed even if there are outstanding references to it).
+    //  - implicitly: removing the last reference to it (i.e., falling out-of-scope).
+    //
+    // When a Socket is closed implicitly, it can take several seconds for the local port being used
+    // by it to be freed/reclaimed by the lower networking layers. During that time, other sockets on the machine
+    // will not be able to use the port. Thus, it is strongly recommended that Socket instances be explicitly
+    // closed before they go out of scope(e.g., before application exit). The call below explicitly closes the socket.
+    if (_client != nullptr)
+    {
+        delete _client;
+        _client = nullptr;
+    }
+}
+
+void
+DatagramClientContext::Connect_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    task<void> removeContext;
+
+    if (CoreApplication::Properties->HasKey("clientContext"))
+    {
+        auto clientContext = dynamic_cast<IClientContext^>(
+            CoreApplication::Properties->Lookup("clientContext"));
+        if (clientContext == nullptr)
+        {
+            throw ref new FailureException(L"No clientContext");
+        }
+
+        removeContext = create_task(clientContext->CancelIO()).then(
+            []()
+        {
+            CoreApplication::Properties->Remove("clientContext");
+        });
+    }
+    else
+    {
+        removeContext = create_task([]() {});
+    }
+
+    _client->MessageReceived += ref new MessageHandler(
+        this, &DatagramClientContext::OnMessage);
+
+    removeContext.then([this](task<void> prevTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            prevTask.get();
+
+            // Events cannot be hooked up directly to the ScenarioInput2 object, as the object can fall out-of-scope and be
+            // deleted. This would render any event hooked up to the object ineffective. The ClientContext guarantees that
+            // both the socket and object that serves its events have the same lifetime.
+            CoreApplication::Properties->Insert("clientContext", this);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread(
+                "Remove clientContext error: " + ex->Message,
+                NotifyType::Error);
+        }
+        catch (task_canceled&)
+        {
+        }
+    }).then([this]()
+    {
+        auto endpointPair = ref new EndpointPair(_args->ClientHostName, _args->ClientPort,
+            _args->ServerHostName, _args->ServerPort);
+
+        _notify->NotifyFromAsyncThread("Start connecting", NotifyType::Status);
+
+        create_task(_client->ConnectAsync(endpointPair)).then(
+            [this, endpointPair](task<void> prevTask)
+        {
+            try
+            {
+                // Try getting an exception.
+                prevTask.get();
+                _notify->NotifyFromAsyncThread(
+                    "Connect from " + endpointPair->LocalHostName->CanonicalName +
+                    " to " + endpointPair->RemoteHostName->CanonicalName,
+                    NotifyType::Status);
+                SetConnected(true);
+            }
+            catch (Exception^ ex)
+            {
+                _notify->NotifyFromAsyncThread(
+                    "Start binding failed with error: " + ex->Message,
+                    NotifyType::Error);
+                CoreApplication::Properties->Remove("clientContext");
+            }
+            catch (task_canceled&)
+            {
+                CoreApplication::Properties->Remove("clientContext");
+            }
+        });
+    });
+}
+
+void
+DatagramClientContext::Send_Click(
+    Object^          sender,
+    RoutedEventArgs^ e,
+    String^          input)
+{
+    SendMessage(GetDataWriter(), input);
+}
+
+IAsyncAction^
+DatagramClientContext::CancelIO()
+{
+    return _client->CancelIOAsync();
+}
+
+void
+DatagramClientContext::SetConnected(
+    bool connected)
+{
+    _connected = connected;
+}
+
+bool
+ot::DatagramClientContext::IsConnected() const
+{
+    return _connected;
+}
+
+void
+ot::DatagramClientContext::OnMessage(
+    DatagramSocket^           socket,
+    MessageReceivedEventArgs^ eventArgs)
+{
+    try
+    {
+        auto dataReader = eventArgs->GetDataReader();
+        Receive(dataReader, dataReader->UnconsumedBufferLength);
+    }
+    catch (Exception^ ex)
+    {
+        auto socketError = SocketError::GetStatus(ex->HResult);
+        if (socketError == SocketErrorStatus::ConnectionResetByPeer)
+        {
+            // This error would indicate that a previous send operation resulted in an ICMP "Port Unreachable" message.
+            _notify->NotifyFromAsyncThread(
+                "Peer does not listen on the specific port. Please make sure that you run step 1 first "
+                "or you have a server properly working on a remote server.",
+                NotifyType::Error);
+        }
+        else if (socketError != SocketErrorStatus::Unknown)
+        {
+            _notify->NotifyFromAsyncThread(
+                "Error happened when receiving a datagram: " + socketError.ToString(),
+                NotifyType::Error);
+        }
+        else
+        {
+            throw;
+        }
+    }
+}
+
+void
+DatagramClientContext::Receive(
+    DataReader^  dataReader,
+    unsigned int strLen)
+{
+    if (!strLen)
+    {
+        return;
+    }
+
+    auto msg = dataReader->ReadString(strLen);
+    _notify->NotifyFromAsyncThread("Received data from server: \"" + msg + "\"",
+        NotifyType::Status);
+}
+
+void
+DatagramClientContext::SendMessage(
+    DataWriter^ dataWriter,
+    String^     msg)
+{
+    if (!IsConnected())
+    {
+        _notify->NotifyFromAsyncThread("This socket is not yet connected.", NotifyType::Error);
+        return;
+    }
+
+    try
+    {
+        dataWriter->WriteString(msg);
+        _notify->NotifyFromAsyncThread("Sending - " + msg, NotifyType::Status);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread("Sending failed with error: " + ex->Message, NotifyType::Error);
+    }
+
+    // Write the locally buffered data to the network. Please note that write operation will succeed
+    // even if the server is not listening.
+    create_task(dataWriter->StoreAsync()).then(
+        [this](task<unsigned int> writeTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            writeTask.get();
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Send failed with error: " + ex->Message, NotifyType::Error);
+        }
+    });
+}
+
+Windows::Storage::Streams::DataWriter^
+DatagramClientContext::GetDataWriter()
+{
+    if (_dataWriter == nullptr)
+    {
+        _dataWriter = ref new DataWriter(_client->OutputStream);
+    }
+
+    return _dataWriter;
+}
diff --git a/examples/apps/windows/DatagramClientContext.h b/examples/apps/windows/DatagramClientContext.h
new file mode 100644
index 0000000..6591801
--- /dev/null
+++ b/examples/apps/windows/DatagramClientContext.h
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IClientContext.h"
+#include "IAsyncThreadNotify.h"
+#include "ClientArgs.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class DatagramClientContext sealed : public IClientContext
+{
+public:
+    using DatagramSocket = Windows::Networking::Sockets::DatagramSocket;
+
+    DatagramClientContext(IAsyncThreadNotify^ notify, DatagramSocket^ client, ClientArgs^ args);
+    virtual ~DatagramClientContext();
+
+    virtual void Connect_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    virtual void Send_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e, Platform::String^ input);
+
+    virtual Windows::Foundation::IAsyncAction^ CancelIO();
+
+private:
+    using MessageReceivedEventArgs = Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs;
+    using MessageHandler = Windows::Foundation::TypedEventHandler<DatagramSocket^, MessageReceivedEventArgs^>;
+    using DataReader = Windows::Storage::Streams::DataReader;
+    using DataWriter = Windows::Storage::Streams::DataWriter;
+    using Args = ClientArgs;
+
+    void SetConnected(bool connected);
+    bool IsConnected() const;
+
+    void OnMessage(DatagramSocket^ socket, MessageReceivedEventArgs^ eventArgs);
+    void Receive(DataReader^, unsigned int strLen);
+
+    void SendMessage(DataWriter^, String^ msg);
+
+    DataWriter^ GetDataWriter();
+
+    IAsyncThreadNotify^ _notify;
+    DatagramSocket^     _client;
+    Args^               _args;
+    bool                _connected = false;
+    DataReader^         _dataReader;
+    DataWriter^         _dataWriter;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/DatagramListenerContext.cpp b/examples/apps/windows/DatagramListenerContext.cpp
new file mode 100644
index 0000000..339e8c6
--- /dev/null
+++ b/examples/apps/windows/DatagramListenerContext.cpp
@@ -0,0 +1,278 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include "DatagramListenerContext.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+DatagramListenerContext::DatagramListenerContext(
+    IAsyncThreadNotify^ notify,
+    DatagramSocket^     listener,
+    ListenerArgs^       args) :
+    _notify{ std::move(notify) },
+    _listener{ std::move(listener) },
+    _args{ std::move(args) }
+{
+}
+
+DatagramListenerContext::~DatagramListenerContext()
+{
+    // A Listener can be closed in two ways:
+    //  - explicitly: using the 'delete' keyword (listener is closed even if there are outstanding references to it).
+    //  - implicitly: removing the last reference to it (i.e., falling out-of-scope).
+    //
+    // When a Socket is closed implicitly, it can take several seconds for the local port being used
+    // by it to be freed/reclaimed by the lower networking layers. During that time, other sockets on the machine
+    // will not be able to use the port. Thus, it is strongly recommended that Socket instances be explicitly
+    // closed before they go out of scope(e.g., before application exit). The call below explicitly closes the socket.
+    if (_listener != nullptr)
+    {
+        delete _listener;
+        _listener = nullptr;
+    }
+}
+
+void
+DatagramListenerContext::Listen_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    task<void> removeContext;
+
+    if (CoreApplication::Properties->HasKey("listenerContext"))
+    {
+        auto listenerContext = dynamic_cast<IListenerContext^>(
+            CoreApplication::Properties->Lookup("listenerContext"));
+        if (listenerContext == nullptr)
+        {
+            throw ref new FailureException(L"No listenerContext");
+        }
+
+        removeContext = create_task(listenerContext->CancelIO()).then(
+            []()
+        {
+            CoreApplication::Properties->Remove("listenerContext");
+        });
+    }
+    else
+    {
+        removeContext = create_task([]() {});
+    }
+
+    _listener->MessageReceived += ref new MessageHandler(
+        this, &DatagramListenerContext::OnMessage);
+
+    removeContext.then([this](task<void> prevTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            prevTask.get();
+
+            // Events cannot be hooked up directly to the ScenarioInput1 object, as the object can fall out-of-scope and be
+            // deleted. This would render any event hooked up to the object ineffective. The ListenerContext guarantees that
+            // both the listener and object that serves its events have the same lifetime.
+            CoreApplication::Properties->Insert("listenerContext", this);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread(
+                "Remove listenerContext error: " + ex->Message,
+                NotifyType::Error);
+        }
+        catch (task_canceled&)
+        {
+        }
+    }).then([this]()
+    {
+        _notify->NotifyFromAsyncThread("Start listening", NotifyType::Status);
+
+        create_task(_listener->BindEndpointAsync(_args->ServerHostName, _args->ServerPort)).then(
+            [this](task<void> prevTask)
+        {
+            try
+            {
+                // Try getting an exception.
+                prevTask.get();
+                _notify->NotifyFromAsyncThread(
+                    "Listening on address " + _args->ServerHostName->CanonicalName,
+                    NotifyType::Status);
+            }
+            catch (Exception^ ex)
+            {
+                _notify->NotifyFromAsyncThread(
+                    "Start listening failed with error: " + ex->Message,
+                    NotifyType::Error);
+                CoreApplication::Properties->Remove("listenerContext");
+            }
+        });
+    });
+}
+
+IAsyncAction^
+DatagramListenerContext::CancelIO()
+{
+    return _listener->CancelIOAsync();
+}
+
+void
+DatagramListenerContext::OnMessage(
+    DatagramSocket^           socket,
+    MessageReceivedEventArgs^ eventArgs)
+{
+    if (_outputStream != nullptr)
+    {
+        auto dataReader = eventArgs->GetDataReader();
+        Receive(dataReader, dataReader->UnconsumedBufferLength, GetDataWriter());
+        return;
+    }
+
+    // We do not have an output stream yet so create one.
+    create_task(socket->GetOutputStreamAsync(eventArgs->RemoteAddress, eventArgs->RemotePort)).then(
+        [this, socket, eventArgs](IOutputStream^ stream)
+    {
+        {
+            std::lock_guard<mutex_t> lock(_mtx);
+
+            // It might happen that the OnMessage was invoked more than once before the GetOutputStreamAsync call
+            // completed. In this case we will end up with multiple streams - just keep one of them.
+            if (_outputStream == nullptr)
+            {
+                _outputStream = stream;
+            }
+        }
+
+        auto dataReader = eventArgs->GetDataReader();
+        Receive(dataReader, dataReader->UnconsumedBufferLength, GetDataWriter());
+    }).then([this](task<void> prevTask)
+    {
+        try
+        {
+            // Try getting all exceptions from the continuation chain above this point.
+            prevTask.get();
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("On message with an error: " + ex->Message,
+                NotifyType::Error);
+        }
+        catch (task_canceled&)
+        {
+            // Do not print anything here - this will usually happen because user closed the client socket.
+        }
+    });
+}
+
+void
+DatagramListenerContext::Receive(
+    DataReader^  dataReader,
+    unsigned int strLen,
+    DataWriter^  dataWriter)
+{
+    if (!strLen)
+    {
+        return;
+    }
+
+    auto msg = dataReader->ReadString(strLen);
+    _notify->NotifyFromAsyncThread("Received data from client: \"" + msg + "\"",
+        NotifyType::Status);
+    auto echo = CreateEchoMessage(msg);
+    EchoMessage(dataWriter, echo);
+}
+
+String^
+DatagramListenerContext::CreateEchoMessage(
+    String^ msg)
+{
+    wchar_t buf[256];
+    auto len = swprintf_s(buf, L"Server%s received data from client : \"%s\"",
+        _args->ServerName->IsEmpty() ? L"" : (" " + _args->ServerName)->Data(), msg->Data());
+
+    len += swprintf_s(&buf[len], _countof(buf) - len, L" - got %d chars",
+        msg->Length());
+    return ref new String(buf);
+}
+
+void
+DatagramListenerContext::EchoMessage(
+    DataWriter^ dataWriter,
+    String^     echo)
+{
+    try
+    {
+        dataWriter->WriteString(echo);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread("Echoing failed with error: " + ex->Message,
+            NotifyType::Error);
+    }
+
+    create_task(dataWriter->StoreAsync()).then(
+        [this](task<unsigned int> writeTask)
+    {
+        try
+        {
+            // Try getting all exceptions from the continuation chain above this point.
+            writeTask.get();
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Echo message with an error: " + ex->Message,
+                NotifyType::Error);
+        }
+    });
+}
+
+Windows::Storage::Streams::DataWriter^
+DatagramListenerContext::GetDataWriter()
+{
+    if (_dataWriter == nullptr)
+    {
+        _dataWriter = ref new DataWriter(_outputStream);
+    }
+
+    return _dataWriter;
+}
diff --git a/examples/apps/windows/DatagramListenerContext.h b/examples/apps/windows/DatagramListenerContext.h
new file mode 100644
index 0000000..ff8699a
--- /dev/null
+++ b/examples/apps/windows/DatagramListenerContext.h
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <mutex>
+#include "IListenerContext.h"
+#include "IAsyncThreadNotify.h"
+#include "ListenerArgs.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class DatagramListenerContext sealed : public IListenerContext
+{
+public:
+    using DatagramSocket = Windows::Networking::Sockets::DatagramSocket;
+
+    DatagramListenerContext(IAsyncThreadNotify^ notify, DatagramSocket^ listener, ListenerArgs^ args);
+    virtual ~DatagramListenerContext();
+
+    virtual void Listen_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    virtual Windows::Foundation::IAsyncAction^ CancelIO();
+
+private:
+    using MessageReceivedEventArgs = Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs;
+    using MessageHandler = Windows::Foundation::TypedEventHandler<DatagramSocket^, MessageReceivedEventArgs^>;
+    using DataReader = Windows::Storage::Streams::DataReader;
+    using DataWriter = Windows::Storage::Streams::DataWriter;
+    using IOutputStream = Windows::Storage::Streams::IOutputStream;
+    using Listener = DatagramSocket;
+    using Args = ListenerArgs;
+    using mutex_t = std::mutex;
+
+    void OnMessage(DatagramSocket^ socket, MessageReceivedEventArgs^ eventArgs);
+    void Receive(DataReader^, unsigned int strLen, DataWriter^);
+    String^ CreateEchoMessage(String^ msg);
+    void EchoMessage(DataWriter^, String^ echo);
+
+    DataWriter^ GetDataWriter();
+
+    IAsyncThreadNotify^ _notify;
+    Listener^           _listener;
+    Args^               _args;
+    DataWriter^         _dataWriter;
+
+    mutable mutex_t _mtx;
+    // mutex protected data
+    IOutputStream^  _outputStream;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/Factory.cpp b/examples/apps/windows/Factory.cpp
new file mode 100644
index 0000000..b047ab7
--- /dev/null
+++ b/examples/apps/windows/Factory.cpp
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include "Factory.h"
+#include "StreamListenerContext.h"
+#include "DatagramListenerContext.h"
+#include "StreamClientContext.h"
+#include "DatagramClientContext.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+IListenerContext^
+Factory::CreateListenerContext(
+    IAsyncThreadNotify^ notify,
+    ListenerArgs^       listenerArgs,
+    Protocol            protocol)
+{
+    if (protocol == Protocol::TCP)
+    {
+        auto listener = ref new StreamSocketListener();
+        return ref new StreamListenerContext(notify, listener, listenerArgs);
+    }
+    else
+    {
+        auto listener = ref new DatagramSocket();
+        return ref new DatagramListenerContext(notify, listener, listenerArgs);
+    }
+}
+
+IClientContext^
+Factory::CreateClientContext(
+    IAsyncThreadNotify^ notify,
+    ClientArgs^         clientArgs,
+    Protocol            protocol)
+{
+    if (protocol == Protocol::TCP)
+    {
+        auto client = ref new StreamSocket();
+        return ref new StreamClientContext(notify, client, clientArgs);
+    }
+    else
+    {
+        auto client = ref new DatagramSocket();
+        return ref new DatagramClientContext(notify, client, clientArgs);
+    }
+}
diff --git a/examples/apps/windows/Factory.h b/examples/apps/windows/Factory.h
new file mode 100644
index 0000000..6cdeaf8
--- /dev/null
+++ b/examples/apps/windows/Factory.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IAsyncThreadNotify.h"
+#include "ListenerArgs.h"
+#include "IListenerContext.h"
+#include "ClientArgs.h"
+#include "IClientContext.h"
+#include "Protocol.h"
+
+namespace ot
+{
+
+class Factory
+{
+public:
+    static IListenerContext^ CreateListenerContext(IAsyncThreadNotify^, ListenerArgs^, Protocol);
+
+    static IClientContext^ CreateClientContext(IAsyncThreadNotify^, ClientArgs^, Protocol);
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/IAsyncThreadNotify.h b/examples/apps/windows/IAsyncThreadNotify.h
new file mode 100644
index 0000000..dc3c900
--- /dev/null
+++ b/examples/apps/windows/IAsyncThreadNotify.h
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public enum class NotifyType
+{
+    Status,
+    Error,
+};
+
+public interface struct IAsyncThreadNotify
+{
+    void NotifyFromAsyncThread(Platform::String^ message, NotifyType type);
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/IClientContext.h b/examples/apps/windows/IClientContext.h
new file mode 100644
index 0000000..f8c948b
--- /dev/null
+++ b/examples/apps/windows/IClientContext.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+    
+public interface struct IClientContext
+{
+    void Connect_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    void Send_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e, Platform::String^ input);
+
+    Windows::Foundation::IAsyncAction^ CancelIO();
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/IListenerContext.h b/examples/apps/windows/IListenerContext.h
new file mode 100644
index 0000000..b5cb89a
--- /dev/null
+++ b/examples/apps/windows/IListenerContext.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public interface struct IListenerContext
+{
+    void Listen_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    Windows::Foundation::IAsyncAction^ CancelIO();
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/IMainPageUIElements.h b/examples/apps/windows/IMainPageUIElements.h
new file mode 100644
index 0000000..c7cd46f
--- /dev/null
+++ b/examples/apps/windows/IMainPageUIElements.h
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public interface struct IMainPageUIElements
+{
+    property Windows::UI::Xaml::UIElement^ ThreadGrid
+    {
+        Windows::UI::Xaml::UIElement^ get();
+    }
+    
+    property Windows::UI::Xaml::UIElement^ TalkGrid
+    {
+        Windows::UI::Xaml::UIElement^ get();
+    }
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/ListenerArgs.h b/examples/apps/windows/ListenerArgs.h
new file mode 100644
index 0000000..510c016
--- /dev/null
+++ b/examples/apps/windows/ListenerArgs.h
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public ref class ListenerArgs sealed
+{
+public:
+    property Platform::String^              ServerName;
+    property Windows::Networking::HostName^ ServerHostName;
+    property Platform::String^              ServerPort;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/MainPage.xaml b/examples/apps/windows/MainPage.xaml
new file mode 100644
index 0000000..03b1e9b
--- /dev/null
+++ b/examples/apps/windows/MainPage.xaml
@@ -0,0 +1,307 @@
+<!--
+  Copyright (c) 2016, The OpenThread Authors.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the copyright holder nor the
+     names of its contributors may be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+-->
+<Page
+    x:Class="ot.MainPage"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:ot"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    IsTabStop="false"
+    mc:Ignorable="d">
+
+    <Grid>
+        <Grid.Background>
+            <ImageBrush ImageSource="ms-appx:///Assets/Wide310x150Logo.png" Opacity="0.25" Stretch="Uniform"/>
+        </Grid.Background>
+        <!-- Interface List -->
+        <Grid
+            x:Name="ThrdGrid"
+            >
+            <TextBlock
+                Text="Thread UX" 
+                HorizontalAlignment="Left" 
+                VerticalAlignment="Top" 
+                Margin="20" 
+                FontSize="50"
+                />
+            <StackPanel
+                Orientation="Horizontal"
+                VerticalAlignment="Top"
+                >
+                <TextBlock
+                    Text="Interfaces" 
+                    HorizontalAlignment="Left" 
+                    VerticalAlignment="Top" 
+                    Margin="60,90" 
+                    FontSize="25"
+                    />
+                <Button
+                    x:Name="Talk"
+                    Content="Talk"
+                    Margin="10,0"
+                    />
+            </StackPanel>
+            <ListView
+                Name="InterfaceList"
+                Margin="60,130,20,150"
+                SelectionMode="None">
+            </ListView>
+
+            <!-- Interface Configuration -->
+            <Grid 
+                x:Name="InterfaceConfiguration" 
+                Background="#B2FFFFFF"
+                Visibility="Collapsed"
+                >
+                <StackPanel
+                    Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
+                    Width="600" 
+                    Height="360" 
+                    HorizontalAlignment="Center" 
+                    VerticalAlignment="Center"
+                    >
+                    <TextBlock
+                        Text="Interface Configuration" 
+                        HorizontalAlignment="Left" 
+                        VerticalAlignment="Top" 
+                        Margin="20" 
+                        FontSize="25"
+                        />
+                    <Grid Margin="40,10">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="200"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                        </Grid.RowDefinitions>
+                        <TextBlock
+                            Text="Name"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBox
+                            Name="InterfaceConfigName"
+                            Text="Test Network"
+                            Grid.Column="1"
+                            FontSize="18" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Text="Key"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            Grid.Row="1"
+                            />
+                        <TextBox
+                            Name="InterfaceConfigKey"
+                            Text="00112233445566778899aabbccddeeff"
+                            Grid.Row="1"
+                            Grid.Column="1"
+                            FontSize="18" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Text="Max Children"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            Grid.Row="2"
+                            />
+                        <Slider
+                            Name="InterfaceConfigMaxChildren"
+                            Grid.Row="2"
+                            Grid.Column="1"
+                            Minimum="0"
+                            Maximum="32"
+                            Value="16"
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Text="Channel"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            Grid.Row="3"
+                            />
+                        <Slider
+                            Name="InterfaceConfigChannel"
+                            Grid.Row="3"
+                            Grid.Column="1"
+                            Minimum="11"
+                            Maximum="24"
+                            Value="11"
+                            VerticalAlignment="Center"
+                            />
+                        <StackPanel
+                            Grid.Row="5"
+                            Grid.ColumnSpan="2"
+                            HorizontalAlignment="Center"
+                            Orientation="Horizontal">
+                            <Button
+                                Name="InterfaceConfigOkButton"
+                                Content="Ok" 
+                                FontSize="22"
+                                Margin="20,0"
+                                />
+                            <Button
+                                Name="InterfaceConfigCancelButton"
+                                Content="Cancel" 
+                                FontSize="22"
+                                Margin="20,0"
+                                />
+                        </StackPanel>
+                    </Grid>
+                </StackPanel>
+            </Grid>
+
+            <!-- Interface Details -->
+            <Grid 
+                x:Name="InterfaceDetails" 
+                Background="#B2FFFFFF"
+                Visibility="Collapsed"
+                >
+                <StackPanel
+                    Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
+                    Width="600" 
+                    Height="405" 
+                    HorizontalAlignment="Center" 
+                    VerticalAlignment="Center"
+                    >
+                    <TextBlock
+                        Text="Interface Details" 
+                        HorizontalAlignment="Left" 
+                        VerticalAlignment="Top" 
+                        Margin="20" 
+                        FontSize="25"
+                        />
+                    <Grid Margin="40,0">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="200"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                            <RowDefinition Height="50"/>
+                        </Grid.RowDefinitions>
+                        <TextBlock
+                            Text="MAC"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceMacAddress"
+                            Text="00:00:00:00:00:00:00:00"
+                            Grid.Column="1"
+                            FontSize="18" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Text="ML-EID"
+                            Grid.Row="1"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceML_EID"
+                            Text="::01"
+                            Grid.Row="1"
+                            Grid.Column="1"
+                            FontSize="18" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Text="RLOC"
+                            Grid.Row="2"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceRLOC"
+                            Text="::01"
+                            Grid.Row="2"
+                            Grid.Column="1"
+                            FontSize="18" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceNeighborsText"
+                            Text="Neighbors"
+                            Grid.Row="3"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceNeighbors"
+                            Text="0"
+                            Grid.Row="3"
+                            Grid.Column="1"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceChildrenText"
+                            Text="Children"
+                            Grid.Row="4"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <TextBlock
+                            Name="InterfaceChildren"
+                            Text="0"
+                            Grid.Row="4"
+                            Grid.Column="1"
+                            FontSize="22" 
+                            VerticalAlignment="Center"
+                            />
+                        <Button
+                            Name="InterfaceDetailsCloseButton"
+                            Grid.Row="6"
+                            Grid.ColumnSpan="2"
+                            Content="Close" 
+                            HorizontalAlignment="Center"
+                            FontSize="22"
+                            />
+                    </Grid>
+                </StackPanel>
+            </Grid>
+        </Grid>
+        <local:TalkGrid
+            x:Name="TlkGrid"
+            Margin="20"
+            Visibility="Collapsed"
+            />
+    </Grid>
+</Page>
diff --git a/examples/apps/windows/MainPage.xaml.cpp b/examples/apps/windows/MainPage.xaml.cpp
new file mode 100644
index 0000000..55732de
--- /dev/null
+++ b/examples/apps/windows/MainPage.xaml.cpp
@@ -0,0 +1,419 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include "MainPage.xaml.h"
+#include "TalkGrid.xaml.h"
+
+using namespace ot;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+#define GUID_FORMAT L"{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
+#define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
+
+void otLog(PCSTR aFormat, ...)
+{
+    va_list args;
+    va_start(args, aFormat);
+
+    CHAR logString[512] = { 0 };
+    int charsWritten = vsprintf_s(logString, sizeof(logString), aFormat, args);
+    if (charsWritten > 0) OutputDebugStringA(logString);
+
+    va_end(args);
+}
+
+MainPage::MainPage() : _otApi(nullptr)
+{
+    InitializeComponent();
+
+    InterfaceConfigCancelButton->Click +=
+        ref new RoutedEventHandler(
+            [=](Platform::Object^, RoutedEventArgs^) {
+                this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                this->_curAdapter = nullptr;
+            }
+    );
+    InterfaceConfigOkButton->Click +=
+        ref new RoutedEventHandler(
+            [=](Platform::Object^, RoutedEventArgs^) {
+                this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                this->ConnectNetwork(_curAdapter);
+                this->_curAdapter = nullptr;
+            }
+    );
+    InterfaceDetailsCloseButton->Click +=
+        ref new RoutedEventHandler(
+            [=](Platform::Object^, RoutedEventArgs^) {
+                this->InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+            }
+    );
+    Talk->Click +=
+        ref new RoutedEventHandler(
+            [=](Platform::Object^, RoutedEventArgs^) {
+                this->ThreadGrid->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                this->TalkGrid->Visibility = Windows::UI::Xaml::Visibility::Visible;
+            }
+    );
+
+    TlkGrid->Init(this);
+}
+
+void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
+{
+    Loaded += ref new RoutedEventHandler(this, &MainPage::OnLoaded);
+    Unloaded += ref new RoutedEventHandler(this, &MainPage::OnUnloaded);
+}
+
+void MainPage::OnLoaded(Object^ sender, RoutedEventArgs^ e)
+{
+    try
+    {
+        // Initialize api handle
+        _otApi = ref new otApi();
+
+        // Register for state changes
+        _adapterArrivalToken =
+            _otApi->AdapterArrival +=
+                ref new otAdapterArrivalDelegate(
+                    [=](otAdapter^ adapter) {
+                        // Update on the UI thread
+                        this->Dispatcher->RunAsync(
+                            Windows::UI::Core::CoreDispatcherPriority::Normal,
+                            ref new Windows::UI::Core::DispatchedHandler(
+                                [=]() {
+                                    this->AddAdapterToList(adapter);
+                                }));
+                    });
+        
+        // Enumerate the adapter list
+        auto adapters = _otApi->GetAdapters();
+        for (auto&& adapter : adapters) {
+            AddAdapterToList(adapter);
+        }
+    }
+    catch (Exception^)
+    {
+    }
+}
+
+void MainPage::OnUnloaded(Object^ sender, RoutedEventArgs^ e)
+{
+    if (_otApi)
+    {
+        // Unregister
+        _otApi->AdapterArrival -= _adapterArrivalToken;
+
+        // Clear current adapter
+        _curAdapter = nullptr;
+
+        // Remove the adapter list
+        auto adapters = _otApi->GetAdapters();
+        for (auto&& adapter : adapters) {
+            adapter->InvokeAdapterRemoval();
+        }
+
+        // Free the api handle
+        _otApi = nullptr;
+    }
+}
+
+void MainPage::OnResuming()
+{
+}
+
+void MainPage::ShowInterfaceDetails(otAdapter^ adapter)
+{
+    try
+    {
+        InterfaceMacAddress->Text = otApi::MacToString(adapter->ExtendedAddress);
+        InterfaceML_EID->Text = adapter->MeshLocalEid->ToString();
+        InterfaceRLOC->Text = otApi::Rloc16ToString(adapter->Rloc16);
+
+        if (adapter->State > otThreadState::Child)
+        {
+            uint8_t index = 0;
+            otChildInfo childInfo;
+            while (OT_ERROR_NONE == otThreadGetChildInfoByIndex((otInstance*)(void*)adapter->RawHandle, index, &childInfo))
+            {
+                index++;
+            }
+
+            WCHAR szText[64] = { 0 };
+            swprintf_s(szText, 64, L"%d", index);
+            InterfaceChildren->Text = ref new String(szText);
+
+            InterfaceNeighbors->Text = L"unknown";
+
+            InterfaceNeighbors->Visibility = Windows::UI::Xaml::Visibility::Visible;
+            InterfaceNeighborsText->Visibility = Windows::UI::Xaml::Visibility::Visible;
+            InterfaceChildren->Visibility = Windows::UI::Xaml::Visibility::Visible;
+            InterfaceChildrenText->Visibility = Windows::UI::Xaml::Visibility::Visible;
+        }
+
+        // Show the details
+        InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible;
+    }
+    catch (Exception^)
+    {
+
+    }
+}
+
+void MainPage::AddAdapterToList(otAdapter^ adapter)
+{
+    try
+    {
+        GUID interfaceGuid = adapter->InterfaceGuid;
+        WCHAR szName[256] = { 0 };
+        swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
+
+        auto InterfaceStackPanel = ref new StackPanel();
+        InterfaceStackPanel->Name = ref new String(szName);
+        InterfaceStackPanel->Orientation = Orientation::Horizontal;
+
+        otLog("%S arrival!\n", InterfaceStackPanel->Name->Data());
+
+        // Basic description text
+        auto InterfaceTextBlock = ref new TextBlock();
+        InterfaceTextBlock->Text = ref new String(L"openthread interface");
+        InterfaceTextBlock->FontSize = 16;
+        InterfaceTextBlock->Margin = Thickness(10);
+        InterfaceTextBlock->TextWrapping = TextWrapping::Wrap;
+        InterfaceStackPanel->Children->Append(InterfaceTextBlock);
+
+        // Connect button
+        auto ConnectButton = ref new Button();
+        ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        ConnectButton->Content = ref new String(L"Connect");
+        ConnectButton->Click +=
+            ref new RoutedEventHandler(
+                [=](Platform::Object^, RoutedEventArgs^) {
+                    this->_curAdapter = adapter;
+                    this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
+                }
+            );
+        InterfaceStackPanel->Children->Append(ConnectButton);
+
+        // Details button
+        auto DetailsButton = ref new Button();
+        DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        DetailsButton->Content = ref new String(L"Details");
+        DetailsButton->Click +=
+            ref new RoutedEventHandler(
+                [=](Platform::Object^, RoutedEventArgs^) {
+                    this->ShowInterfaceDetails(adapter);
+                }
+            );
+        InterfaceStackPanel->Children->Append(DetailsButton);
+
+        // Disconnect button
+        auto DisconnectButton = ref new Button();
+        DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+        DisconnectButton->Content = ref new String(L"Disconnect");
+        DisconnectButton->Click +=
+            ref new RoutedEventHandler(
+                [=](Platform::Object^, RoutedEventArgs^) {
+                    this->DisconnectNetwork(adapter);
+                }
+            );
+        InterfaceStackPanel->Children->Append(DisconnectButton);
+
+        // Delegate for handling role changes
+        auto OnAdapterRoleChanged =
+            [=]() {
+                GUID interfaceGuid = adapter->InterfaceGuid;
+                auto state = adapter->State;
+                auto stateStr = otApi::ThreadStateToString(adapter->State);
+
+                WCHAR szText[256] = { 0 };
+                swprintf_s(szText, 256, GUID_FORMAT L"\r\n\t%s\r\n\t%s",
+                    GUID_ARG(interfaceGuid),
+                    stateStr->Data(),
+                    state >= otThreadState::Child ? 
+                        adapter->MeshLocalEid->ToString()->Data() : 
+                        L"");
+
+                InterfaceTextBlock->Text = ref new String(szText);
+
+                otLog("%S state = %S\n", InterfaceStackPanel->Name->Data(), stateStr->Data());
+
+                if (state == otThreadState::Disabled)
+                {
+                    ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+                    DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                    DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                }
+                else
+                {
+                    ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
+                    DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+                    DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
+                }
+            };
+
+        // Register for role change callbacks
+        auto adapterRoleChangedToken = 
+            adapter->NetRoleChanged +=
+                ref new otNetRoleChangedDelegate(
+                    [=](auto sender) {
+                        // Update the text on the UI thread
+                        this->Dispatcher->RunAsync(
+                            Windows::UI::Core::CoreDispatcherPriority::Normal,
+                            ref new Windows::UI::Core::DispatchedHandler(
+                                [=]() {
+                                    OnAdapterRoleChanged();
+                                }
+                            )
+                        );
+                    }
+                );
+
+        // Register for address change callbacks
+        auto adapterMeshLocalAddresChangedToken =
+            adapter->IpMeshLocalAddresChanged +=
+                ref new otIpMeshLocalAddresChangedDelegate(
+                    [=](auto sender) {
+                        // Update the text on the UI thread
+                        this->Dispatcher->RunAsync(
+                            Windows::UI::Core::CoreDispatcherPriority::Normal,
+                            ref new Windows::UI::Core::DispatchedHandler(
+                                [=]() {
+                                    OnAdapterRoleChanged();
+                                }
+                            )
+                        );
+                    }
+                );
+
+        // Register for adapter removal callbacks
+        Windows::Foundation::EventRegistrationToken adapterRemovalToken;
+        adapterRemovalToken =
+            adapter->AdapterRemoval +=
+                ref new otAdapterRemovalDelegate(
+                    [=](otAdapter^ adapter) {
+                        // Unregister
+                        adapter->NetRoleChanged -= adapterRoleChangedToken;
+                        adapter->IpMeshLocalAddresChanged -= adapterMeshLocalAddresChangedToken;
+                        adapter->AdapterRemoval -= adapterRemovalToken;
+
+                        // Remove the item on the UI thread
+                        this->Dispatcher->RunAsync(
+                            Windows::UI::Core::CoreDispatcherPriority::Normal,
+                            ref new Windows::UI::Core::DispatchedHandler(
+                                [=]() {
+                                    for (uint32_t i = 0; i < this->InterfaceList->Items->Size; i++)
+                                    {
+                                        auto Item = dynamic_cast<StackPanel^>(this->InterfaceList->Items->GetAt(i));
+                                        if (Item == InterfaceStackPanel)
+                                        {
+                                            otLog("%S removal!\n", InterfaceStackPanel->Name->Data());
+                                            this->InterfaceList->Items->RemoveAt(i);
+                                            break;
+                                        }
+                                    }
+                                }));
+                    });
+
+        // Trigger the initial role change
+        OnAdapterRoleChanged();
+
+        // Add the interface to the list
+        InterfaceList->Items->Append(InterfaceStackPanel);
+    }
+    catch (Exception^)
+    {
+    }
+}
+
+void MainPage::ConnectNetwork(otAdapter^ adapter)
+{
+    try
+    {
+        GUID interfaceGuid = adapter->InterfaceGuid;
+        WCHAR szName[256] = { 0 };
+        swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
+        otLog("%S starting connection...\n", szName);
+
+        // Configure
+        adapter->NetworkName = InterfaceConfigName->Text;
+        adapter->MasterKey = InterfaceConfigKey->Text;
+        adapter->Channel = (uint8_t)InterfaceConfigChannel->Value;
+        adapter->MaxAllowedChildren = (uint8_t)InterfaceConfigMaxChildren->Value;
+        adapter->PanId = 0x4567;
+
+        // Bring up the interface and start the Thread logic
+        adapter->IpEnabled = true;
+        adapter->ThreadEnabled = true;
+    }
+    catch (Exception^)
+    {
+
+    }
+}
+
+void MainPage::DisconnectNetwork(otAdapter^ adapter)
+{
+    try
+    {
+        GUID interfaceGuid = adapter->InterfaceGuid;
+        WCHAR szName[256] = { 0 };
+        swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
+        otLog("%S disconnecting...\n", szName);
+
+        // Stop the Thread network and bring down the interface
+        adapter->ThreadEnabled = false;
+        adapter->IpEnabled = false;
+    }
+    catch (Exception^)
+    {
+
+    }
+}
+
+Windows::UI::Xaml::UIElement^
+MainPage::ThreadGrid::get()
+{
+    return ThrdGrid;
+}
+
+Windows::UI::Xaml::UIElement^
+MainPage::TalkGrid::get()
+{
+    return TlkGrid;
+}
diff --git a/examples/apps/windows/MainPage.xaml.h b/examples/apps/windows/MainPage.xaml.h
new file mode 100644
index 0000000..40d3e7b
--- /dev/null
+++ b/examples/apps/windows/MainPage.xaml.h
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "MainPage.g.h"
+#include "IMainPageUIElements.h"
+
+namespace ot
+{
+    /// <summary>
+    /// An empty page that can be used on its own or navigated to within a Frame.
+    /// </summary>
+    public ref class MainPage sealed : public IMainPageUIElements
+    {
+    public:
+        MainPage();
+
+        void OnResuming();
+
+        void ConnectNetwork(otAdapter^ adapter);
+        void ShowInterfaceDetails(otAdapter^ adapter);
+        void DisconnectNetwork(otAdapter^ adapter);
+
+        property Windows::UI::Xaml::UIElement^ ThreadGrid
+        {
+            virtual Windows::UI::Xaml::UIElement^ get();
+        }
+        
+        property Windows::UI::Xaml::UIElement^ TalkGrid
+        {
+           virtual Windows::UI::Xaml::UIElement^ get();
+        }
+
+        protected:
+        virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
+
+    private:
+
+        void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+        void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+        void AddAdapterToList(otAdapter^ adapter);
+        
+        otApi^ _otApi;
+
+        Windows::Foundation::EventRegistrationToken _adapterArrivalToken;
+
+        otAdapter^ _curAdapter;
+
+    };
+}
diff --git a/examples/apps/windows/OpenThread_TemporaryKey.pfx b/examples/apps/windows/OpenThread_TemporaryKey.pfx
new file mode 100644
index 0000000..46b4755
--- /dev/null
+++ b/examples/apps/windows/OpenThread_TemporaryKey.pfx
Binary files differ
diff --git a/examples/apps/windows/Package.appxmanifest b/examples/apps/windows/Package.appxmanifest
new file mode 100644
index 0000000..f336f66
--- /dev/null
+++ b/examples/apps/windows/Package.appxmanifest
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
+  <Identity Name="7926e3f9-835e-421c-94c1-2812f3569f4a" Publisher="CN=nibanks" Version="1.0.12.0" />
+  <mp:PhoneIdentity PhoneProductId="7926e3f9-835e-421c-94c1-2812f3569f4a" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
+  <Properties>
+    <DisplayName>OpenThread</DisplayName>
+    <PublisherDisplayName>OpenThread</PublisherDisplayName>
+    <Logo>Assets\StoreLogo.png</Logo>
+  </Properties>
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
+  </Dependencies>
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+  <Applications>
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="ot.App">
+      <uap:VisualElements DisplayName="OpenThread" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="OpenThread" BackgroundColor="white">
+        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
+        </uap:DefaultTile>
+        <uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="white" />
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+  <Capabilities>
+    <Capability Name="internetClient" />
+    <Capability Name="privateNetworkClientServer" />
+    <Capability Name="internetClientServer" />
+  </Capabilities>
+</Package>
\ No newline at end of file
diff --git a/examples/apps/windows/Protocol.h b/examples/apps/windows/Protocol.h
new file mode 100644
index 0000000..c325e4b
--- /dev/null
+++ b/examples/apps/windows/Protocol.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+public enum class Protocol
+{
+    TCP,
+    UDP,
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/README.md b/examples/apps/windows/README.md
new file mode 100644
index 0000000..d3b2e8f
--- /dev/null
+++ b/examples/apps/windows/README.md
@@ -0,0 +1,26 @@
+# OpenThread App for Windows #
+
+This sample app provides an example of how to interface with the OpenThread API and talk to each other in a
+[Universal Windows App](https://developer.microsoft.com/en-us/windows/apps). The app is written in C++ /CX
+and provides a simple wrapper around the OpenThread API, hiding the raw C/C++ interface.
+
+The main page of the App is a list of the available interfaces, their current connection state,
+their current ML-EID IPv6 address, and buttons to connect/disconnect and to view some more details.
+
+![Interface List](../../../doc/images/windows-app-interface-list.png)
+
+The details list provides some more information, including extended MAC address, RLOC16 and information
+about the current children.
+
+![Interface List](../../../doc/images/windows-app-details.png)
+
+The "Talk" button of main page switches the user interface to the talk fuctionality. This app acts
+either as a server or a client role and talks to each other over a TCP or a UDP protocol.
+
+The server listens to the clients.
+
+![Talk Functionality](../../../doc/images/windows-app-talk-server.png)
+
+The client sends a message to the server and the server echos that message back to the client.
+
+![Talk Functionality](../../../doc/images/windows-app-talk-client.png)
diff --git a/examples/apps/windows/ServerControl.xaml b/examples/apps/windows/ServerControl.xaml
new file mode 100644
index 0000000..5408b2e
--- /dev/null
+++ b/examples/apps/windows/ServerControl.xaml
@@ -0,0 +1,106 @@
+<!--
+  Copyright (c) 2016, The OpenThread Authors.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the copyright holder nor the
+     names of its contributors may be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+-->
+<UserControl
+    x:Class="ot.ServerControl"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:ot"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="20"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="Auto"/>
+            <ColumnDefinition Width="20"/>
+            <ColumnDefinition Width="*"/>
+        </Grid.ColumnDefinitions>
+        <TextBlock
+            Grid.Row="0"
+            Grid.Column="0"
+            Text="Server Name :"
+            />
+        <TextBox
+            Grid.Row="0"
+            Grid.Column="2"
+            x:Name="ServerName"
+            Width="300"
+            HorizontalAlignment="Left"
+            />
+        <TextBlock
+            Grid.Row="2"
+            Grid.Column="0"
+            Text="Server IP :"
+            />
+        <TextBox
+            Grid.Row="2"
+            Grid.Column="2"
+            x:Name="ServerIP"
+            MinWidth="500"
+            />
+        <TextBlock
+            Grid.Row="4"
+            Grid.Column="0"
+            Text="Server Port :"
+            />
+        <TextBox
+            Grid.Row="4"
+            Grid.Column="2"
+            x:Name="ServerPort"
+            Width="100"
+            HorizontalAlignment="Left"
+            />
+        <StackPanel
+            Orientation="Horizontal"
+            VerticalAlignment="Top"
+            Grid.Row="6"
+            Grid.Column="0"
+            Grid.ColumnSpan="3"
+            >
+            <Button
+                Width="75"
+                Content="Listen"
+                Click="Listen_Click"
+                Margin="0,0,75,0"
+                />
+            <Button
+                Width="75"
+                Content="Exit"
+                Click="Exit_Click"
+                />
+        </StackPanel>
+    </Grid>
+</UserControl>
diff --git a/examples/apps/windows/ServerControl.xaml.cpp b/examples/apps/windows/ServerControl.xaml.cpp
new file mode 100644
index 0000000..37545bd
--- /dev/null
+++ b/examples/apps/windows/ServerControl.xaml.cpp
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include <algorithm>
+#include "ServerControl.xaml.h"
+#include "Factory.h"
+#include "TalkHelper.h"
+
+using namespace ot;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
+
+ServerControl::ServerControl()
+{
+    InitializeComponent();
+
+    ServerPort->Text = DEF_PORT.ToString();
+}
+
+void
+ServerControl::Init(
+    IAsyncThreadNotify^  notify,
+    IMainPageUIElements^ mainPageUIElements)
+{
+    _notify = std::move(notify);
+    _mainPageUIElements = std::move(mainPageUIElements);
+}
+
+void
+ServerControl::ProtocolChanged(
+    Protocol protocol)
+{
+    _protocol = protocol;
+}
+
+void
+ServerControl::Listen_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    try
+    {
+        auto listenerArgs = ref new ListenerArgs();
+
+        listenerArgs->ServerName = ServerName->Text;
+
+        auto serverIP = ServerIP->Text;
+        if (serverIP->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Server IP");
+        }
+
+        // check valid chars of Ipv6 Address
+        if (!TalkHelper::AllValidIpv6Chars(serverIP->Data(), serverIP->Data() + serverIP->Length()))
+        {
+            throw Exception::CreateException(E_INVALIDARG, "Not a valid Server IPv6 address");
+        }
+
+        listenerArgs->ServerHostName = ref new HostName(serverIP);
+
+        if (ServerPort->Text->IsEmpty())
+        {
+            throw Exception::CreateException(E_INVALIDARG, "No Server Port");
+        }
+        listenerArgs->ServerPort = ServerPort->Text;
+
+        auto listenerContext = Factory::CreateListenerContext(_notify, listenerArgs, _protocol);
+        listenerContext->Listen_Click(sender, e);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread(
+            "Listening failed with input error: " + ex->Message,
+            NotifyType::Error);
+    }
+}
+
+void
+ServerControl::Exit_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    _mainPageUIElements->TalkGrid->Visibility = Xaml::Visibility::Collapsed;
+    _mainPageUIElements->ThreadGrid->Visibility = Xaml::Visibility::Visible;
+}
diff --git a/examples/apps/windows/ServerControl.xaml.h b/examples/apps/windows/ServerControl.xaml.h
new file mode 100644
index 0000000..6284f4a
--- /dev/null
+++ b/examples/apps/windows/ServerControl.xaml.h
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ServerControl.g.h"
+#include "TalkConsts.h"
+#include "IAsyncThreadNotify.h"
+#include "IMainPageUIElements.h"
+#include "Protocol.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class ServerControl sealed
+{
+public:
+    ServerControl();
+
+    void Init(IAsyncThreadNotify^ notify, IMainPageUIElements^ mainPageUIElements);
+
+    void ProtocolChanged(Protocol protocol);
+
+private:
+    static constexpr unsigned short DEF_PORT = TalkConsts::DEF_SERVER_PORT;
+
+    void Listen_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    void Exit_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    IAsyncThreadNotify^  _notify;
+    IMainPageUIElements^ _mainPageUIElements;
+    Protocol             _protocol;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/StreamClientContext.cpp b/examples/apps/windows/StreamClientContext.cpp
new file mode 100644
index 0000000..f209e9c
--- /dev/null
+++ b/examples/apps/windows/StreamClientContext.cpp
@@ -0,0 +1,325 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include "StreamClientContext.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+StreamClientContext::StreamClientContext(
+    IAsyncThreadNotify^ notify,
+    StreamSocket^       client,
+    ClientArgs^         args) :
+    _notify{ std::move(notify) },
+    _client{ std::move(client) },
+    _args{ std::move(args) }
+{
+}
+
+StreamClientContext::~StreamClientContext()
+{
+    // A Client can be closed in two ways:
+    //  - explicitly: using the 'delete' keyword (client is closed even if there are outstanding references to it).
+    //  - implicitly: removing the last reference to it (i.e., falling out-of-scope).
+    //
+    // When a Socket is closed implicitly, it can take several seconds for the local port being used
+    // by it to be freed/reclaimed by the lower networking layers. During that time, other sockets on the machine
+    // will not be able to use the port. Thus, it is strongly recommended that Socket instances be explicitly
+    // closed before they go out of scope(e.g., before application exit). The call below explicitly closes the socket.
+    if (_client != nullptr)
+    {
+        delete _client;
+        _client = nullptr;
+    }
+}
+
+void
+StreamClientContext::Connect_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    task<void> removeContext;
+
+    if (CoreApplication::Properties->HasKey("clientContext"))
+    {
+        auto clientContext = dynamic_cast<IClientContext^>(
+            CoreApplication::Properties->Lookup("clientContext"));
+        if (clientContext == nullptr)
+        {
+            throw ref new FailureException(L"No clientContext");
+        }
+
+        removeContext = create_task(clientContext->CancelIO()).then(
+            []()
+        {
+            CoreApplication::Properties->Remove("clientContext");
+        });
+    }
+    else
+    {
+        removeContext = create_task([]() {});
+    }
+
+    removeContext.then([this](task<void> prevTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            prevTask.get();
+
+            // Events cannot be hooked up directly to the ScenarioInput2 object, as the object can fall out-of-scope and be
+            // deleted. This would render any event hooked up to the object ineffective. The ClientContext guarantees that
+            // both the socket and object that serves its events have the same lifetime.
+            CoreApplication::Properties->Insert("clientContext", this);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread(
+                "Remove clientContext error: " + ex->Message,
+                NotifyType::Error);
+        }
+        catch (task_canceled&)
+        {
+        }
+    }).then([this]()
+    {
+        auto endpointPair = ref new EndpointPair(_args->ClientHostName, _args->ClientPort,
+            _args->ServerHostName, _args->ServerPort);
+
+        _notify->NotifyFromAsyncThread("Start connecting", NotifyType::Status);
+
+        create_task(_client->ConnectAsync(endpointPair)).then(
+            [this, endpointPair](task<void> prevTask)
+        {
+            try
+            {
+                // Try getting an exception.
+                prevTask.get();
+                _notify->NotifyFromAsyncThread(
+                    "Connect from " + endpointPair->LocalHostName->CanonicalName +
+                    " to " + endpointPair->RemoteHostName->CanonicalName,
+                    NotifyType::Status);
+                OnConnection(_client);
+            }
+            catch (Exception^ ex)
+            {
+                _notify->NotifyFromAsyncThread(
+                    "Start binding failed with error: " + ex->Message,
+                    NotifyType::Error);
+                CoreApplication::Properties->Remove("clientContext");
+            }
+            catch (task_canceled&)
+            {
+                CoreApplication::Properties->Remove("clientContext");
+            }
+        });
+    });
+}
+
+void
+ot::StreamClientContext::Send_Click(
+    Platform::Object^                   sender,
+    Windows::UI::Xaml::RoutedEventArgs^ e,
+    Platform::String^                   input)
+{
+    SendMessage(GetDataWriter(), input);
+}
+
+IAsyncAction^
+ot::StreamClientContext::CancelIO()
+{
+    return _client->CancelIOAsync();
+}
+
+void
+StreamClientContext::OnConnection(
+    StreamSocket^ streamSocket)
+{
+    SetConnected(true);
+    ReceiveLoop(streamSocket, GetDataReader());
+}
+
+void
+StreamClientContext::SetConnected(
+    bool connected)
+{
+    _connected = connected;
+}
+
+bool
+StreamClientContext::IsConnected() const
+{
+    return _connected;
+}
+
+void
+StreamClientContext::ReceiveLoop(
+    StreamSocket^ streamSocket,
+    DataReader^   dataReader)
+{
+    // Read first 4 bytes (length of the subsequent string).
+    create_task(dataReader->LoadAsync(sizeof(UINT32))).then(
+        [this, dataReader](unsigned int size)
+    {
+        if (size < sizeof(UINT32))
+        {
+            // The underlying socket was closed before we were able to read the whole data.
+            cancel_current_task();
+        }
+
+        auto strLen = dataReader->ReadUInt32();
+        return create_task(dataReader->LoadAsync(strLen)).then(
+            [this, dataReader, strLen](unsigned int actualStrLen)
+        {
+            if (actualStrLen != strLen)
+            {
+                // The underlying socket was closed before we were able to read the whole data.
+                cancel_current_task();
+            }
+
+            Receive(dataReader, strLen);
+        });
+    }).then([this, streamSocket, dataReader](task<void> previousTask)
+    {
+        try
+        {
+            // Try getting all exceptions from the continuation chain above this point.
+            previousTask.get();
+
+            // Everything went ok, so try to receive another string. The receive will continue until the stream is
+            // broken (i.e. peer closed the socket).
+            ReceiveLoop(streamSocket, dataReader);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Read stream failed with error: " + ex->Message,
+                NotifyType::Error);
+
+            // Explicitly close the socket.
+            delete streamSocket;
+        }
+        catch (task_canceled&)
+        {
+            // Do not print anything here - this will usually happen because user closed the client socket.
+
+            // Explicitly close the socket.
+            delete streamSocket;
+        }
+    });
+}
+
+void
+StreamClientContext::Receive(
+    DataReader^  dataReader,
+    unsigned int strLen)
+{
+    if (!strLen)
+    {
+        return;
+    }
+
+    auto msg = dataReader->ReadString(strLen);
+    _notify->NotifyFromAsyncThread("Received data from server: \"" + msg + "\"",
+        NotifyType::Status);
+}
+
+void
+StreamClientContext::SendMessage(
+    DataWriter^ dataWriter,
+    String^     msg)
+{
+    if (!IsConnected())
+    {
+        _notify->NotifyFromAsyncThread("This socket is not yet connected.", NotifyType::Error);
+        return;
+    }
+
+    try
+    {
+        dataWriter->WriteUInt32(msg->Length());
+        dataWriter->WriteString(msg);
+        _notify->NotifyFromAsyncThread("Sending - " + msg, NotifyType::Status);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread("Sending failed with error: " + ex->Message, NotifyType::Error);
+    }
+
+    // Write the locally buffered data to the network. Please note that write operation will succeed
+    // even if the server is not listening.
+    create_task(dataWriter->StoreAsync()).then(
+        [this](task<unsigned int> writeTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            writeTask.get();
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Send failed with error: " + ex->Message, NotifyType::Error);
+        }
+    });
+}
+
+Windows::Storage::Streams::DataReader^
+StreamClientContext::GetDataReader()
+{
+    if (_dataReader == nullptr)
+    {
+        _dataReader = ref new DataReader(_client->InputStream);
+    }
+
+    return _dataReader;
+}
+
+Windows::Storage::Streams::DataWriter^
+StreamClientContext::GetDataWriter()
+{
+    if (_dataWriter == nullptr)
+    {
+        _dataWriter = ref new DataWriter(_client->OutputStream);
+    }
+
+    return _dataWriter;
+}
diff --git a/examples/apps/windows/StreamClientContext.h b/examples/apps/windows/StreamClientContext.h
new file mode 100644
index 0000000..1111823
--- /dev/null
+++ b/examples/apps/windows/StreamClientContext.h
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IClientContext.h"
+#include "IAsyncThreadNotify.h"
+#include "ClientArgs.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class StreamClientContext sealed : public IClientContext
+{
+public:
+    using StreamSocket = Windows::Networking::Sockets::StreamSocket;
+
+    StreamClientContext(IAsyncThreadNotify^ notify, StreamSocket^ client, ClientArgs^ args);
+    virtual ~StreamClientContext();
+
+    virtual void Connect_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    virtual void Send_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e, Platform::String^ input);
+
+    virtual Windows::Foundation::IAsyncAction^ CancelIO();
+
+private:
+    using DataReader = Windows::Storage::Streams::DataReader;
+    using DataWriter = Windows::Storage::Streams::DataWriter;
+    using Args = ClientArgs;
+
+    void OnConnection(StreamSocket^);
+
+    void SetConnected(bool connected);
+    bool IsConnected() const;
+
+    void ReceiveLoop(StreamSocket^, DataReader^);
+    void Receive(DataReader^, unsigned int strLen);
+
+    void SendMessage(DataWriter^, String^ msg);
+
+    DataReader^ GetDataReader();
+    DataWriter^ GetDataWriter();
+
+    IAsyncThreadNotify^ _notify;
+    StreamSocket^       _client;
+    Args^               _args;
+    bool                _connected = false;
+    DataReader^         _dataReader;
+    DataWriter^         _dataWriter;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/StreamListenerContext.cpp b/examples/apps/windows/StreamListenerContext.cpp
new file mode 100644
index 0000000..7852029
--- /dev/null
+++ b/examples/apps/windows/StreamListenerContext.cpp
@@ -0,0 +1,288 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include <utility>
+#include "StreamListenerContext.h"
+
+using namespace ot;
+
+using namespace Concurrency;
+using namespace Platform;
+using namespace Windows::ApplicationModel::Core;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+StreamListenerContext::StreamListenerContext(
+    IAsyncThreadNotify^   notify,
+    StreamSocketListener^ listener,
+    ListenerArgs^         args) :
+    _notify{ std::move(notify) },
+    _listener{ std::move(listener) },
+    _args{ std::move(args) }
+{
+}
+
+StreamListenerContext::~StreamListenerContext()
+{
+    // A Listener can be closed in two ways:
+    //  - explicitly: using the 'delete' keyword (listener is closed even if there are outstanding references to it).
+    //  - implicitly: removing the last reference to it (i.e., falling out-of-scope).
+    //
+    // When a Socket is closed implicitly, it can take several seconds for the local port being used
+    // by it to be freed/reclaimed by the lower networking layers. During that time, other sockets on the machine
+    // will not be able to use the port. Thus, it is strongly recommended that Socket instances be explicitly
+    // closed before they go out of scope(e.g., before application exit). The call below explicitly closes the socket.
+    if (_listener != nullptr)
+    {
+        delete _listener;
+        _listener = nullptr;
+    }
+}
+
+void
+StreamListenerContext::Listen_Click(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    task<void> removeContext;
+
+    if (CoreApplication::Properties->HasKey("listenerContext"))
+    {
+        auto listenerContext = dynamic_cast<IListenerContext^>(
+            CoreApplication::Properties->Lookup("listenerContext"));
+        if (listenerContext == nullptr)
+        {
+            throw ref new FailureException(L"No listenerContext");
+        }
+
+        removeContext = create_task(listenerContext->CancelIO()).then(
+            []()
+        {
+            CoreApplication::Properties->Remove("listenerContext");
+        });
+    }
+    else
+    {
+        removeContext = create_task([]() {});
+    }
+
+    _listener->ConnectionReceived += ref new ConnectionHandler(
+        this, &StreamListenerContext::OnConnection);
+
+    removeContext.then([this](task<void> prevTask)
+    {
+        try
+        {
+            // Try getting an exception.
+            prevTask.get();
+
+            // Events cannot be hooked up directly to the ScenarioInput1 object, as the object can fall out-of-scope and be
+            // deleted. This would render any event hooked up to the object ineffective. The ListenerContext guarantees that
+            // both the listener and object that serves its events have the same lifetime.
+            CoreApplication::Properties->Insert("listenerContext", this);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread(
+                "Remove listenerContext error: " + ex->Message,
+                NotifyType::Error);
+        }
+        catch (task_canceled&)
+        {
+        }
+    }).then([this]()
+    {
+        _notify->NotifyFromAsyncThread("Start listening", NotifyType::Status);
+
+        create_task(_listener->BindEndpointAsync(_args->ServerHostName, _args->ServerPort)).then(
+            [this](task<void> prevTask)
+        {
+            try
+            {
+                // Try getting an exception.
+                prevTask.get();
+                _notify->NotifyFromAsyncThread(
+                    "Listening on address " + _args->ServerHostName->CanonicalName,
+                    NotifyType::Status);
+            }
+            catch (Exception^ ex)
+            {
+                _notify->NotifyFromAsyncThread(
+                    "Start listening failed with error: " + ex->Message,
+                    NotifyType::Error);
+                CoreApplication::Properties->Remove("listenerContext");
+            }
+        });
+    });
+}
+
+IAsyncAction^
+StreamListenerContext::CancelIO()
+{
+    return _listener->CancelIOAsync();
+}
+
+void
+StreamListenerContext::OnConnection(
+    StreamSocketListener^        listener,
+    ConnectionReceivedEventArgs^ args)
+{
+    auto dataReader = ref new DataReader(args->Socket->InputStream);
+    auto dataWriter = ref new DataWriter(args->Socket->OutputStream);
+
+    ReceiveLoop(args->Socket, dataReader, dataWriter);
+}
+
+void
+StreamListenerContext::ReceiveLoop(
+    StreamSocket^ streamSocket,
+    DataReader^   dataReader,
+    DataWriter^   dataWriter)
+{
+    // Read first 4 bytes (length of the subsequent string).
+    create_task(dataReader->LoadAsync(sizeof(UINT32))).then(
+        [this, streamSocket, dataReader, dataWriter](unsigned int size)
+    {
+        if (size < sizeof(UINT32))
+        {
+            // The underlying socket was closed before we were able to read the whole data.
+            cancel_current_task();
+        }
+
+        unsigned int strLen = dataReader->ReadUInt32();
+        return create_task(dataReader->LoadAsync(strLen)).then(
+            [this, dataReader, dataWriter, strLen](unsigned int actualStrLen)
+        {
+            if (actualStrLen != strLen)
+            {
+                // The underlying socket was closed before we were able to read the whole data.
+                cancel_current_task();
+            }
+
+            Receive(dataReader, strLen, dataWriter);
+        });
+    }).then([this, streamSocket, dataReader, dataWriter](task<void> previousTask)
+    {
+        try
+        {
+            // Try getting all exceptions from the continuation chain above this point.
+            previousTask.get();
+
+            // Everything went ok, so try to receive another string. The receive will continue until the stream is
+            // broken (i.e. peer closed the socket).
+            ReceiveLoop(streamSocket, dataReader, dataWriter);
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Read stream failed with error: " + ex->Message,
+                NotifyType::Error);
+
+            // Explicitly close the socket.
+            delete streamSocket;
+        }
+        catch (task_canceled&)
+        {
+            // Do not print anything here - this will usually happen because user closed the client socket.
+
+            // Explicitly close the socket.
+            delete streamSocket;
+        }
+    });
+}
+
+void
+StreamListenerContext::Receive(
+    DataReader^  dataReader,
+    unsigned int strLen,
+    DataWriter^  dataWriter)
+{
+    if (!strLen)
+    {
+        return;
+    }
+
+    auto msg = dataReader->ReadString(strLen);
+    _notify->NotifyFromAsyncThread("Received data from client: \"" + msg + "\"",
+        NotifyType::Status);
+    auto echo = CreateEchoMessage(msg);
+    EchoMessage(dataWriter, echo);
+}
+
+String^
+StreamListenerContext::CreateEchoMessage(
+    String^ msg)
+{
+    wchar_t buf[256];
+    auto len = swprintf_s(buf, L"Server%s received data from client : \"%s\"",
+        _args->ServerName->IsEmpty() ? L"" : (" " + _args->ServerName)->Data(), msg->Data());
+
+    len += swprintf_s(&buf[len], _countof(buf) - len, L" - got %d chars",
+        msg->Length());
+    return ref new String(buf);
+}
+
+void
+StreamListenerContext::EchoMessage(
+    DataWriter^ dataWriter,
+    String^     echo)
+{
+    try
+    {
+        dataWriter->WriteUInt32(echo->Length());
+        dataWriter->WriteString(echo);
+    }
+    catch (Exception^ ex)
+    {
+        _notify->NotifyFromAsyncThread("Echoing failed with error: " + ex->Message,
+            NotifyType::Error);
+    }
+
+    create_task(dataWriter->StoreAsync()).then(
+        [this](task<unsigned int> writeTask)
+    {
+        try
+        {
+            // Try getting all exceptions from the continuation chain above this point.
+            writeTask.get();
+        }
+        catch (Exception^ ex)
+        {
+            _notify->NotifyFromAsyncThread("Echo message with an error: " + ex->Message,
+                NotifyType::Error);
+        }
+    });
+}
diff --git a/examples/apps/windows/StreamListenerContext.h b/examples/apps/windows/StreamListenerContext.h
new file mode 100644
index 0000000..ab9affd
--- /dev/null
+++ b/examples/apps/windows/StreamListenerContext.h
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IListenerContext.h"
+#include "IAsyncThreadNotify.h"
+#include "ListenerArgs.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class StreamListenerContext sealed : public IListenerContext
+{
+public:
+    using StreamSocketListener = Windows::Networking::Sockets::StreamSocketListener;
+
+    StreamListenerContext(IAsyncThreadNotify^ notify, StreamSocketListener^ listener, ListenerArgs^ args);
+    virtual ~StreamListenerContext();
+
+    virtual void Listen_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    virtual Windows::Foundation::IAsyncAction^ CancelIO();
+
+private:
+    using ConnectionReceivedEventArgs = Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs;
+    using ConnectionHandler = Windows::Foundation::TypedEventHandler<StreamSocketListener^, ConnectionReceivedEventArgs^>;
+    using DataReader = Windows::Storage::Streams::DataReader;
+    using DataWriter = Windows::Storage::Streams::DataWriter;
+    using Listener = StreamSocketListener;
+    using StreamSocket = Windows::Networking::Sockets::StreamSocket;
+    using Args = ListenerArgs;
+
+    void OnConnection(StreamSocketListener^ listener, ConnectionReceivedEventArgs^ args);
+    void ReceiveLoop(StreamSocket^, DataReader^, DataWriter^);
+    void Receive(DataReader^, unsigned int strLen, DataWriter^);
+    String^ CreateEchoMessage(String^ msg);
+    void EchoMessage(DataWriter^, String^ echo);
+
+    IAsyncThreadNotify^ _notify;
+    Listener^           _listener;
+    Args^               _args;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/TalkConsts.h b/examples/apps/windows/TalkConsts.h
new file mode 100644
index 0000000..100faf5
--- /dev/null
+++ b/examples/apps/windows/TalkConsts.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+
+struct TalkConsts
+{
+    static constexpr unsigned short DEF_SERVER_PORT = 51000;
+    static constexpr unsigned short DEF_CLIENT_PORT_INIT = 51100;
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/TalkGrid.xaml b/examples/apps/windows/TalkGrid.xaml
new file mode 100644
index 0000000..5d8aa2e
--- /dev/null
+++ b/examples/apps/windows/TalkGrid.xaml
@@ -0,0 +1,108 @@
+<!--
+  Copyright (c) 2016, The OpenThread Authors.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the copyright holder nor the
+     names of its contributors may be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+-->
+<Grid
+    x:Class="ot.TalkGrid"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="using:ot"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    mc:Ignorable="d">
+    <Grid.RowDefinitions>
+        <RowDefinition Height="Auto"/>
+        <RowDefinition Height="20"/>
+        <RowDefinition Height="Auto"/>
+        <RowDefinition Height="20"/>
+        <RowDefinition Height="*"/>
+        <RowDefinition Height="20"/>
+        <RowDefinition Height="Auto"/>
+    </Grid.RowDefinitions>
+    <StackPanel
+            Grid.Row="0"
+            Orientation="Horizontal"
+            VerticalAlignment="Top"
+            >
+        <RadioButton
+                x:Name="TcpRadio"
+                Content="TCP"
+                GroupName="ProtocolGroup"
+                Checked="Protocol_Changed"
+                Margin="0,0,20,0"
+                />
+        <RadioButton
+                x:Name="UdpRadio"
+                Content="UDP"
+                GroupName="ProtocolGroup"
+                Checked="Protocol_Changed"
+                Margin="0,0,20,0"
+                />
+    </StackPanel>
+    <StackPanel
+            Grid.Row="2"
+            Orientation="Horizontal"
+            VerticalAlignment="Top"
+            >
+        <RadioButton
+                x:Name="ServerRadio"
+                Content="Server"
+                GroupName="RoleGroup"
+                Checked="Role_Changed"
+                Margin="0,0,20,0"
+                />
+        <RadioButton
+                x:Name="ClientRadio"
+                Content="Client"
+                GroupName="RoleGroup"
+                Checked="Role_Changed"
+                Margin="0,0,20,0"
+                />
+    </StackPanel>
+    <local:ServerControl
+        x:Name="ServerRole"
+        Grid.Row="4"
+        Visibility="Collapsed"
+        />
+    <local:ClientControl
+        x:Name="ClientRole"
+        Grid.Row="4"
+        Visibility="Collapsed"
+        />
+    <Border
+        x:Name="StatusBorder"
+        Grid.Row="6"
+        >
+        <TextBlock
+            x:Name="StatusBlock"
+            FontWeight="Bold"
+            Text=""
+            FontSize="16"
+            TextWrapping="Wrap"
+            MaxHeight="80"
+            />
+    </Border>
+</Grid>
diff --git a/examples/apps/windows/TalkGrid.xaml.cpp b/examples/apps/windows/TalkGrid.xaml.cpp
new file mode 100644
index 0000000..632c26d
--- /dev/null
+++ b/examples/apps/windows/TalkGrid.xaml.cpp
@@ -0,0 +1,150 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
+#include "TalkGrid.xaml.h"
+#include "ClientControl.xaml.h"
+#include "ServerControl.xaml.h"
+
+using namespace ot;
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::UI;
+using namespace Windows::UI::Core;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Controls::Primitives;
+using namespace Windows::UI::Xaml::Data;
+using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Media;
+using namespace Windows::UI::Xaml::Navigation;
+
+// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
+
+TalkGrid::TalkGrid()
+{
+    InitializeComponent();
+
+    TcpRadio->IsChecked = true;
+    ServerRadio->IsChecked = true;
+}
+
+
+void
+TalkGrid::Init(
+    IMainPageUIElements^ mainPageUIElements)
+{
+    ServerRole->Init(this, mainPageUIElements);
+    ClientRole->Init(this, mainPageUIElements);
+}
+
+void
+TalkGrid::NotifyFromAsyncThread(
+    String^    message,
+    NotifyType type)
+{
+    Dispatcher->RunAsync(CoreDispatcherPriority::Normal,
+        ref new DispatchedHandler([this, message, type]()
+    {
+        Notify(message, type);
+    }));
+}
+
+void
+TalkGrid::Notify(
+    String^    message,
+    NotifyType type)
+{
+    switch (type)
+    {
+    case NotifyType::Status:
+        StatusBorder->Background = ref new SolidColorBrush(Colors::Green);
+        break;
+    case NotifyType::Error:
+        StatusBorder->Background = ref new SolidColorBrush(Colors::Red);
+        break;
+    default:
+        break;
+    }
+
+    StatusBlock->Text = message;
+
+    // Collapse the StatusBlock if it has no text to conserve real estate.
+    if (StatusBlock->Text != "")
+    {
+        StatusBorder->Visibility = Xaml::Visibility::Visible;
+    }
+    else
+    {
+        StatusBorder->Visibility = Xaml::Visibility::Collapsed;
+    }
+}
+
+void
+TalkGrid::Protocol_Changed(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    auto radioBtn = dynamic_cast<RadioButton^>(sender);
+    if (!radioBtn)
+    {
+        return;
+    }
+
+    auto protocol = (radioBtn == TcpRadio) ? Protocol::TCP : Protocol::UDP;
+
+    ServerRole->ProtocolChanged(protocol);
+    ClientRole->ProtocolChanged(protocol);
+}
+
+void
+TalkGrid::Role_Changed(
+    Object^          sender,
+    RoutedEventArgs^ e)
+{
+    auto radioBtn = dynamic_cast<RadioButton^>(sender);
+    if (!radioBtn)
+    {
+        return;
+    }
+
+    if (radioBtn == ServerRadio)
+    {
+        // switch to server role UI
+        ClientRole->Visibility = Xaml::Visibility::Collapsed;
+        ServerRole->Visibility = Xaml::Visibility::Visible;
+    }
+    else
+    {
+        // switch to client role UI
+        ServerRole->Visibility = Xaml::Visibility::Collapsed;
+        ClientRole->Visibility = Xaml::Visibility::Visible;
+    }
+}
diff --git a/examples/apps/windows/TalkGrid.xaml.h b/examples/apps/windows/TalkGrid.xaml.h
new file mode 100644
index 0000000..e387095
--- /dev/null
+++ b/examples/apps/windows/TalkGrid.xaml.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "TalkGrid.g.h"
+#include "IAsyncThreadNotify.h"
+#include "IMainPageUIElements.h"
+#include "Protocol.h"
+
+namespace ot
+{
+
+[Windows::Foundation::Metadata::WebHostHidden]
+public ref class TalkGrid sealed : public IAsyncThreadNotify
+{
+public:
+    TalkGrid();
+
+    void Init(IMainPageUIElements^ mainPageUIElements);
+
+    // IAsyncThreadMessage
+    virtual void NotifyFromAsyncThread(String^ message, NotifyType type);
+
+    void Notify(String^ message, NotifyType type);
+
+private:
+    // change protocol
+    // TCP <-> UDP
+    void Protocol_Changed(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+
+    // change role from server client (or vice versa)
+    void Role_Changed(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/TalkHelper.h b/examples/apps/windows/TalkHelper.h
new file mode 100644
index 0000000..d6fb849
--- /dev/null
+++ b/examples/apps/windows/TalkHelper.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace ot
+{
+ref class ClientControl;
+ref class ServerControl;
+
+class TalkHelper
+{
+private:
+    friend ref class ClientControl;
+    friend ref class ServerControl;
+
+    // this is a private method for specific usage, not a general purpose one
+    // checks all chars are valid Ipv6 chars in the range [begin, end)
+    static bool AllValidIpv6Chars(const wchar_t* begin, const wchar_t* end)
+    {
+        for (auto it = begin; it != end; ++it)
+        {
+            auto c = *it;
+
+            if ((c >= '0' && c <= '9') || (c == ':') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+            {
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+};
+
+}
diff --git a/examples/apps/windows/otAdapter.h b/examples/apps/windows/otAdapter.h
new file mode 100644
index 0000000..4908182
--- /dev/null
+++ b/examples/apps/windows/otAdapter.h
@@ -0,0 +1,607 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#define OTDLL 1
+#include <openthread/openthread.h>
+#include <openthread/border_router.h>
+#include <openthread/thread_ftd.h>
+#include <openthread/commissioner.h>
+#include <openthread/joiner.h>
+
+#include <wrl.h>
+#include <collection.h>
+
+using namespace Platform;
+using namespace Platform::Collections;
+using namespace Platform::Metadata;
+using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking;
+
+namespace ot
+{
+
+ref class otAdapter;
+
+public delegate void otAdapterRemovalDelegate(otAdapter^ sender);
+
+public delegate void otIpAddressAddedDelegate(otAdapter^ sender);
+public delegate void otIpAddressRemovedDelegate(otAdapter^ sender);
+public delegate void otIpRlocAddedDelegate(otAdapter^ sender);
+public delegate void otIpRlocRemovedDelegate(otAdapter^ sender);
+public delegate void otIpLinkLocalAddresChangedDelegate(otAdapter^ sender);
+public delegate void otIpMeshLocalAddresChangedDelegate(otAdapter^ sender);
+
+public delegate void otNetRoleChangedDelegate(otAdapter^ sender);
+public delegate void otNetPartitionIdChangedDelegate(otAdapter^ sender);
+public delegate void otNetKeySequenceCounterChangedDelegate(otAdapter^ sender);
+
+public delegate void otThreadChildAddedDelegate(otAdapter^ sender);
+public delegate void otThreadChildRemovedDelegate(otAdapter^ sender);
+public delegate void otThreadNetDataUpdatedDelegate(otAdapter^ sender);
+
+[Flags]
+public enum class otLinkModeFlags : unsigned int
+{
+    None                = 0,
+    RxOnWhenIdle        = 0x1,  /* 1, if the sender has its receiver on when not transmitting.  0, otherwise. */
+    SecureDataRequests  = 0x2,  /* 1, if the sender will use IEEE 802.15.4 to secure all data requests.  0, otherwise. */
+    DeviceType          = 0x4,  /* 1, if the sender is an FFD.  0, otherwise. */
+    NetworkData         = 0x8   /* 1, if the sender requires the full Network Data.  0, otherwise. */
+};
+
+public enum class otThreadState
+{
+    Offline,
+    Disabled,
+    Detached,
+    Child,
+    Router,
+    Leader
+};
+
+// Helper class for OpenThread Interface/Adapter specific APIs
+public ref class otAdapter sealed
+{
+private:
+
+    void *_Instance;
+    #define DeviceInstance ((otInstance*)_Instance)
+
+    #define ThrowOnFailure(exp) \
+    do { \
+        auto res = exp; \
+        if (res != 0) \
+            throw Exception::CreateException(TheadErrorToHResult(res), #exp); \
+    } while (false)
+
+public:
+
+#pragma region Events
+
+    event otAdapterRemovalDelegate^                 AdapterRemoval;
+
+    event otIpAddressAddedDelegate^                 IpAddressAdded;
+    event otIpAddressRemovedDelegate^               IpAddressRemoved;
+    event otIpRlocAddedDelegate^                    IpRlocAdded;
+    event otIpRlocRemovedDelegate^                  IpRlocRemoved;
+    event otIpLinkLocalAddresChangedDelegate^       IpLinkLocalAddresChanged;
+    event otIpMeshLocalAddresChangedDelegate^       IpMeshLocalAddresChanged;
+
+    event otNetRoleChangedDelegate^                 NetRoleChanged;
+    event otNetPartitionIdChangedDelegate^          NetPartitionIdChanged;
+    event otNetKeySequenceCounterChangedDelegate^   NetKeySequenceCounterChanged;
+
+    event otThreadChildAddedDelegate^               ThreadChildAdded;
+    event otThreadChildRemovedDelegate^             ThreadChildRemoved;
+    event otThreadNetDataUpdatedDelegate^           ThreadNetDataUpdated;
+
+#pragma endregion
+
+#pragma region Properties
+
+    property IntPtr RawHandle
+    {
+        IntPtr get() { return _Instance; }
+    }
+
+    property Guid InterfaceGuid
+    {
+        Guid get() { return otGetDeviceGuid(DeviceInstance); }
+    }
+
+    property uint32_t IfIndex
+    {
+        uint32_t get() { return otGetDeviceIfIndex(DeviceInstance); }
+    }
+
+    property uint32_t CompartmentId
+    {
+        uint32_t get() { return otGetCompartmentId(DeviceInstance); }
+    }
+
+#pragma region Link Layer
+
+    property signed int /*int8_t*/ MaxTransmitPower
+    {
+        signed int get() { return otLinkGetMaxTransmitPower(DeviceInstance); }
+        void set(signed int value)
+        {
+            if (value > 127) throw Exception::CreateException(E_INVALIDARG);
+            otLinkSetMaxTransmitPower(DeviceInstance, (int8_t)value);
+        }
+    }
+
+    property uint32_t PollPeriod
+    {
+        uint32_t get() { return otLinkGetPollPeriod(DeviceInstance); }
+        void set(uint32_t value) { otLinkSetPollPeriod(DeviceInstance, value); }
+    }
+
+    property uint8_t Channel
+    { 
+        uint8_t get() { return otLinkGetChannel(DeviceInstance); }
+        void set(uint8_t value) { ThrowOnFailure(otLinkSetChannel(DeviceInstance, value)); }
+    }
+
+    property uint16_t PanId
+    {
+        uint16_t get() { return otLinkGetPanId(DeviceInstance); }
+        void set(uint16_t value) { ThrowOnFailure(otLinkSetPanId(DeviceInstance, value)); }
+    }
+
+    property uint16_t ShortAddress
+    {
+        uint16_t get() { return otLinkGetShortAddress(DeviceInstance); }
+    }
+
+    property uint64_t ExtendedAddress
+    {
+        uint64_t get()
+        {
+            auto addr = otLinkGetExtendedAddress(DeviceInstance);
+            auto ret = *(uint64_t*)addr;
+            otFreeMemory(addr);
+            return ret;
+        }
+        void set(uint64_t value) 
+        {
+            ThrowOnFailure(otLinkSetExtendedAddress(DeviceInstance, (otExtAddress*)&value));
+        }
+    }
+
+    property uint64_t FactoryAssignedIeeeEui64
+    {
+        uint64_t get()
+        {
+            uint64_t addr;
+            otLinkGetFactoryAssignedIeeeEui64(DeviceInstance, (otExtAddress*)&addr);
+            return addr;
+        }
+    }
+
+    property uint64_t JoinerId
+    {
+        uint64_t get()
+        {
+            uint64_t addr;
+            otLinkGetJoinerId(DeviceInstance, (otExtAddress*)&addr);
+            return addr;
+        }
+    }
+
+#pragma endregion
+
+#pragma region IP Layer
+
+    property bool IpEnabled
+    {
+        bool get() { return otIp6IsEnabled(DeviceInstance); }
+        void set(bool value) { ThrowOnFailure(otIp6SetEnabled(DeviceInstance, value)); }
+    }
+
+#pragma endregion
+
+#pragma region Thread Layer
+
+    property uint64_t ExtendedPanId
+    {
+        uint64_t get()
+        {
+            auto panid = otThreadGetExtendedPanId(DeviceInstance);
+            auto ret = *(uint64_t*)panid;
+            otFreeMemory(panid);
+            return ret;
+        }
+        void set(uint64_t value)
+        {
+            otThreadSetExtendedPanId(DeviceInstance, (uint8_t*)&value);
+        }
+    }
+
+    property otLinkModeFlags LinkMode
+    {
+        otLinkModeFlags get()
+        {
+            auto linkmode = otThreadGetLinkMode(DeviceInstance);
+            otLinkModeFlags flags = otLinkModeFlags::None;
+            if (linkmode.mRxOnWhenIdle)         flags = flags | otLinkModeFlags::RxOnWhenIdle;
+            if (linkmode.mSecureDataRequests)   flags = flags | otLinkModeFlags::SecureDataRequests;
+            if (linkmode.mDeviceType)           flags = flags | otLinkModeFlags::DeviceType;
+            if (linkmode.mNetworkData)          flags = flags | otLinkModeFlags::NetworkData;
+            return flags;
+        }
+        void set(otLinkModeFlags value)
+        {
+            otLinkModeConfig linkmode = { 0 };
+            if ((value & otLinkModeFlags::RxOnWhenIdle) != otLinkModeFlags::None)
+                linkmode.mRxOnWhenIdle = true;
+            if ((value & otLinkModeFlags::SecureDataRequests) != otLinkModeFlags::None)
+                linkmode.mSecureDataRequests = true;
+            if ((value & otLinkModeFlags::DeviceType) != otLinkModeFlags::None)
+                linkmode.mDeviceType = true;
+            if ((value & otLinkModeFlags::NetworkData) != otLinkModeFlags::None)
+                linkmode.mNetworkData = true;
+            ThrowOnFailure(otThreadSetLinkMode(DeviceInstance, linkmode));
+        }
+    }
+
+    static uint32_t charToValue(wchar_t c)
+    {
+        if (c >= L'a' && c <= L'f')
+        {
+            return c - L'a';
+        }
+        else if (c >= L'A' && c <= L'F')
+        {
+            return c - L'A';
+        }
+        else if (c >= L'0' && c <= L'9')
+        {
+            return c - L'0';
+        }
+        else
+        {
+            throw Exception::CreateException(E_INVALIDARG);
+        }
+    }
+
+    property String^ MasterKey
+    {
+        String^ get()
+        {
+            constexpr char hexmap[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+            auto key = otThreadGetMasterKey(DeviceInstance);
+            WCHAR szKey[OT_MASTER_KEY_SIZE * 2 + 1] = { 0 };
+            for (uint8_t i = 0; i < OT_MASTER_KEY_SIZE; i++)
+            {
+                szKey[2 * i]     = hexmap[(key->m8[i] & 0xF0) >> 4];
+                szKey[2 * i + 1] = hexmap[key->m8[i] & 0x0F];
+            }
+            otFreeMemory(key);
+            return ref new String(szKey);
+        }
+        void set(String^ value)
+        {
+            otMasterKey key;
+            uint8_t keyLen = 0;
+            for (uint32_t i = 0; i < value->Length() - 1; i+=2)
+            {
+                key.m8[keyLen++] = (uint8_t)((charToValue(value->Data()[i]) << 4) |
+                    charToValue(value->Data()[i + 1]));
+            }
+            if (keyLen * 2 == value->Length() - 1)
+            {
+                key.m8[keyLen++] = (uint8_t)(charToValue(value->Data()[value->Length()-1])) << 4;
+            }
+            memset(key.m8 + keyLen, 0, sizeof(key) - keyLen);
+            ThrowOnFailure(otThreadSetMasterKey(DeviceInstance, &key));
+        }
+    }
+
+    property String^ NetworkName
+    {
+        String^ get()
+        {
+            auto _name = otThreadGetNetworkName(DeviceInstance);
+            WCHAR name[OT_NETWORK_NAME_MAX_SIZE + 1];
+            MultiByteToWideChar(CP_UTF8, 0, _name, -1, name, ARRAYSIZE(name));
+            otFreeMemory(_name);
+            return ref new String(name);
+        }
+        void set(String^ value)
+        {
+            char name[OT_NETWORK_NAME_MAX_SIZE + 1];
+            auto len = WideCharToMultiByte(CP_UTF8, 0, value->Data(), -1, name, ARRAYSIZE(name), nullptr, nullptr);
+            if (len <= 0) throw Exception::CreateException(E_INVALIDARG);
+            ThrowOnFailure(otThreadSetNetworkName(DeviceInstance, name));
+        }
+    }
+
+    property uint8_t MaxAllowedChildren
+    {
+        uint8_t get() { return otThreadGetMaxAllowedChildren(DeviceInstance); }
+        void set(uint8_t value) { ThrowOnFailure(otThreadSetMaxAllowedChildren(DeviceInstance, value)); }
+    }
+
+    property uint32_t ChildTimeout
+    {
+        uint32_t get() { return otThreadGetChildTimeout(DeviceInstance); }
+        void set(uint32_t value) { otThreadSetChildTimeout(DeviceInstance, value); }
+    }
+
+    property bool ThreadEnabled
+    {
+        //bool get() { return otIsThreadStarted(DeviceInstance); }
+        void set(bool value) { ThrowOnFailure(otThreadSetEnabled(DeviceInstance, value)); }
+    }
+
+    property bool AutoStart
+    {
+        bool get() { return otThreadGetAutoStart(DeviceInstance); }
+        void set(bool value) { ThrowOnFailure(otThreadSetAutoStart(DeviceInstance, value)); }
+    }
+
+    property bool Singleton
+    {
+        bool get() { return otThreadIsSingleton(DeviceInstance); }
+    }
+
+    property bool RouterRoleEnabled
+    {
+        bool get() { return otThreadIsRouterRoleEnabled(DeviceInstance); }
+        void set(bool value) { otThreadSetRouterRoleEnabled(DeviceInstance, value); }
+    }
+
+    property uint8_t PreferredRouterId
+    {
+        void set(uint8_t value) { ThrowOnFailure(otThreadSetPreferredRouterId(DeviceInstance, value)); }
+    }
+
+    property HostName^ MeshLocalEid
+    {
+        HostName^ get()
+        {
+            auto addr = otThreadGetMeshLocalEid(DeviceInstance);
+            WCHAR szAddr[46];
+            RtlIpv6AddressToString((IN6_ADDR*)addr, szAddr);
+            otFreeMemory(addr);
+            return ref new HostName(ref new String(szAddr));
+        }
+    }
+
+    property HostName^ LeaderRloc
+    {
+        HostName^ get()
+        {
+            IN6_ADDR addr;
+            ThrowOnFailure(otThreadGetLeaderRloc(DeviceInstance, (otIp6Address*)&addr));
+            WCHAR szAddr[46];
+            RtlIpv6AddressToString(&addr, szAddr);
+            return ref new HostName(ref new String(szAddr));
+        }
+    }
+
+    property uint8_t LocalLeaderWeight
+    {
+        uint8_t get() { return otThreadGetLocalLeaderWeight(DeviceInstance); }
+        void set(uint8_t value) { otThreadSetLocalLeaderWeight(DeviceInstance, value); }
+    }
+
+    property uint32_t LocalLeaderPartitionId
+    {
+        uint32_t get() { return otThreadGetLocalLeaderPartitionId(DeviceInstance); }
+        void set(uint32_t value) { otThreadSetLocalLeaderPartitionId(DeviceInstance, value); }
+    }
+
+    property uint8_t LeaderWeight
+    {
+        uint8_t get() { return otThreadGetLeaderWeight(DeviceInstance); }
+    }
+
+    property uint32_t LeaderRouterId
+    {
+        uint32_t get() { return otThreadGetLeaderRouterId(DeviceInstance); }
+    }
+
+    property uint32_t PartitionId
+    {
+        uint32_t get() { return otThreadGetPartitionId(DeviceInstance); }
+    }
+
+    property uint16_t Rloc16
+    {
+        uint16_t get() { return otThreadGetRloc16(DeviceInstance); }
+    }
+
+    property otThreadState State
+    {
+        otThreadState get()
+        {
+            return (otThreadState)otThreadGetDeviceRole(DeviceInstance);
+        }
+    }
+
+#pragma endregion
+
+#pragma endregion
+
+#pragma region Constructor/Destructor
+
+    otAdapter(_In_ IntPtr /*otInstance**/ aInstance)
+    {
+        _Instance = (void*)aInstance;
+
+        IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this);
+
+        // Register for device availability callbacks
+        otSetStateChangedCallback(DeviceInstance, ThreadStateChangeCallback, pInspectable);
+    }
+
+    virtual ~otAdapter()
+    {
+        // Unregister for callbacks for the device
+        otSetStateChangedCallback(DeviceInstance, nullptr, nullptr);
+
+        // Free the device
+        otFreeMemory(DeviceInstance);
+    }
+
+#pragma endregion
+
+#pragma region Functions
+
+    void PlatformReset()
+    {
+        otInstanceReset(DeviceInstance);
+    }
+
+    void FactoryReset()
+    {
+        otInstanceFactoryReset(DeviceInstance);
+    }
+
+    void BecomeRouter()
+    {
+        ThrowOnFailure(otThreadBecomeRouter(DeviceInstance));
+    }
+
+    void BecomeLeader()
+    {
+        ThrowOnFailure(otThreadBecomeLeader(DeviceInstance));
+    }
+
+#pragma endregion
+
+internal:
+
+    void InvokeAdapterRemoval()
+    {
+        AdapterRemoval(this);
+    }
+
+private:
+
+    friend ref class otApi;
+
+    static void OTCALL
+    ThreadStateChangeCallback(
+        uint32_t aFlags,
+        _In_ void* aContext
+        )
+    {
+        IInspectable* pInspectable = (IInspectable*)aContext;
+        otAdapter^ pThis = reinterpret_cast<otAdapter^>(pInspectable);
+
+        if (aFlags & OT_CHANGED_IP6_ADDRESS_ADDED)
+        {
+            pThis->IpAddressAdded(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED)
+        {
+            pThis->IpAddressRemoved(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_RLOC_ADDED)
+        {
+            pThis->IpRlocAdded(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_RLOC_REMOVED)
+        {
+            pThis->IpRlocRemoved(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_LL_ADDR)
+        {
+            pThis->IpLinkLocalAddresChanged(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_ML_ADDR)
+        {
+            pThis->IpMeshLocalAddresChanged(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_ROLE)
+        {
+            pThis->NetRoleChanged(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_PARTITION_ID)
+        {
+            pThis->NetPartitionIdChanged(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER)
+        {
+            pThis->NetKeySequenceCounterChanged(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_CHILD_ADDED)
+        {
+            pThis->ThreadChildAdded(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_CHILD_REMOVED)
+        {
+            pThis->ThreadChildRemoved(pThis);
+        }
+
+        if (aFlags & OT_CHANGED_THREAD_NETDATA)
+        {
+            pThis->ThreadNetDataUpdated(pThis);
+        }
+    }
+
+    static HRESULT
+    TheadErrorToHResult(
+        int /* otError */ error
+    )
+    {
+        switch (error)
+        {
+        case OT_ERROR_NO_BUFS:           return E_OUTOFMEMORY;
+        case OT_ERROR_DROP:
+        case OT_ERROR_NO_ROUTE:          return HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE);
+        case OT_ERROR_INVALID_ARGS:      return E_INVALIDARG;
+        case OT_ERROR_SECURITY:          return E_ACCESSDENIED;
+        case OT_ERROR_NOT_CAPABLE:
+        case OT_ERROR_NOT_IMPLEMENTED:   return E_NOTIMPL;
+        case OT_ERROR_INVALID_STATE:     return E_NOT_VALID_STATE;
+        case OT_ERROR_NOT_FOUND:         return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+        case OT_ERROR_ALREADY:           return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
+        case OT_ERROR_RESPONSE_TIMEOUT:  return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+        default:                         return E_FAIL;
+        }
+    }
+};
+
+} // namespace ot
+
diff --git a/examples/apps/windows/otApi.h b/examples/apps/windows/otApi.h
new file mode 100644
index 0000000..3a0a001
--- /dev/null
+++ b/examples/apps/windows/otApi.h
@@ -0,0 +1,252 @@
+/*
+*  Copyright (c) 2016, The OpenThread Authors.
+*  All rights reserved.
+*
+*  Redistribution and use in source and binary forms, with or without
+*  modification, are permitted provided that the following conditions are met:
+*  1. Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*  2. Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in the
+*     documentation and/or other materials provided with the distribution.
+*  3. Neither the name of the copyright holder nor the
+*     names of its contributors may be used to endorse or promote products
+*     derived from this software without specific prior written permission.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+*  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+*  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+*  POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include "otAdapter.h"
+#include <collection.h>
+
+using namespace Platform;
+using namespace Platform::Collections;
+using namespace Windows::Foundation::Collections;
+
+#define MAC8_FORMAT L"%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X"
+#define MAC8_ARG(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
+
+namespace ot
+{
+
+public delegate void otAdapterArrivalDelegate(otAdapter^ aAdapter);
+
+// Helper class for OpenThread API
+public ref class otApi sealed
+{
+private:
+
+    void *_apiInstance;
+    #define ApiInstance ((otApiInstance*)_apiInstance)
+
+    CRITICAL_SECTION _cs;
+    Vector<otAdapter^>^ _adapters;
+
+public:
+
+    // Event for device availability changes
+    event otAdapterArrivalDelegate^ AdapterArrival;
+
+    property IntPtr RawHandle
+    {
+        IntPtr get() { return _apiInstance; }
+    }
+
+    // Constructor
+    otApi() :
+        _adapters(ref new Vector<otAdapter^>())
+    {
+        // Initialize the API handle
+        _apiInstance = otApiInit();
+        if (_apiInstance == nullptr)
+        {
+            throw Exception::CreateException(E_UNEXPECTED, L"otApiInit failed.");
+        }
+
+        InitializeCriticalSection(&_cs);
+
+        IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this);
+
+        // Register for device availability callbacks
+        otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, pInspectable);
+
+        // Query list of devices
+        auto deviceList = otEnumerateDevices(ApiInstance);
+        if (deviceList)
+        {
+            EnterCriticalSection(&_cs);
+
+            // Add each adapter to our cache unless it already was inserted from a notification
+            for (DWORD i = 0; i < deviceList->aDevicesLength; i++)
+            {
+                if (GetAdapter(deviceList->aDevices[i]) == nullptr)
+                {
+                    auto deviceInstance = otInstanceInit(ApiInstance, &deviceList->aDevices[i]);
+                    if (deviceInstance)
+                    {
+                        _adapters->Append(ref new otAdapter(deviceInstance));
+                    }
+                }
+            }
+
+            LeaveCriticalSection(&_cs);
+
+            otFreeMemory(deviceList);
+        }
+    }
+
+    // Destructor
+    virtual ~otApi()
+    {
+        // Clear registration for callbacks
+        otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
+
+        DeleteCriticalSection(&_cs);
+
+        // Clean up api
+        otApiFinalize(ApiInstance);
+        _apiInstance = nullptr;
+    }
+
+    // Returns the entire list of adapters
+    IVectorView<otAdapter^>^ GetAdapters()
+    {
+        IVectorView<otAdapter^>^ adapters;
+        EnterCriticalSection(&_cs);
+        adapters = _adapters->GetView(); // TODO - Need to copy
+        LeaveCriticalSection(&_cs);
+        return adapters;
+    }
+
+    // Helper to get an adapter, given its device guid
+    otAdapter^ GetAdapter(Guid aDeviceGuid)
+    {
+        otAdapter^ ret = nullptr;
+
+        EnterCriticalSection(&_cs);
+
+        for (auto&& adapter : _adapters)
+        {
+            if (adapter->InterfaceGuid == aDeviceGuid)
+            {
+                ret = adapter;
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&_cs);
+
+        return ret;
+    }
+
+    // Helper function to convert mac address to string
+    static String^ MacToString(uint64_t mac)
+    {
+        WCHAR szMac[64] = { 0 };
+        swprintf_s(szMac, 64, MAC8_FORMAT, MAC8_ARG(((UCHAR*)&mac)));
+        return ref new String(szMac);
+    }
+
+    // Helper function to convert RLOC16/PANID to string
+    static String^ Rloc16ToString(uint16_t rloc)
+    {
+        WCHAR szRloc[16] = { 0 };
+        swprintf_s(szRloc, 16, L"0x%X", rloc);
+        return ref new String(szRloc);
+    }
+
+    // Helper function to convert state to string
+    static String^ ThreadStateToString(otThreadState state)
+    {
+        switch (state)
+        {
+        default:
+        case otThreadState::Offline:    return L"Offline";
+        case otThreadState::Disabled:   return L"Disabled";
+        case otThreadState::Detached:   return L"Disconnected";
+        case otThreadState::Child:      return L"Connected - Child";
+        case otThreadState::Router:     return L"Connected - Router";
+        case otThreadState::Leader:     return L"Connected - Leader";
+        }
+    }
+
+private:
+
+    // Callback from OpenThread indicating arrival or removal of interfaces
+    static void OTCALL
+    ThreadDeviceAvailabilityCallback(
+        bool        aAdded,
+        const GUID* aDeviceGuid,
+        _In_ void*  aContext
+    )
+    {
+        IInspectable* pInspectable = (IInspectable*)aContext;
+        otApi^ pThis = reinterpret_cast<otApi^>(pInspectable);
+
+        if (aAdded)
+        {
+            otAdapter^ adapter = nullptr;
+
+            // Add the device to the list, if it isn't already there
+            EnterCriticalSection(&pThis->_cs);
+
+            if (pThis->GetAdapter(*aDeviceGuid) == nullptr)
+            {
+                auto deviceInstance = otInstanceInit((otApiInstance*)pThis->_apiInstance, aDeviceGuid);
+                if (deviceInstance)
+                {
+                    pThis->_adapters->Append(adapter = ref new otAdapter(deviceInstance));
+                }
+            }
+
+            LeaveCriticalSection(&pThis->_cs);
+
+            if (adapter)
+            {
+                // Send a notification of arrival
+                pThis->AdapterArrival(adapter);
+            }
+        }
+        else
+        {
+            otAdapter^ ret = nullptr;
+            Guid guid = *aDeviceGuid;
+
+            EnterCriticalSection(&pThis->_cs);
+
+            // Look up in the cached list of adapters to remove it
+            uint32_t i = 0;
+            for (auto&& adapter : pThis->_adapters)
+            {
+                if (adapter->InterfaceGuid == guid)
+                {
+                    ret = adapter;
+                    pThis->_adapters->RemoveAt(i);
+                    break;
+                }
+                i++;
+            }
+
+            LeaveCriticalSection(&pThis->_cs);
+
+            if (ret)
+            {
+                ret->InvokeAdapterRemoval();
+            }
+        }
+    }
+};
+
+} // namespace ot
diff --git a/examples/apps/windows/pch.cpp b/examples/apps/windows/pch.cpp
new file mode 100644
index 0000000..3489481
--- /dev/null
+++ b/examples/apps/windows/pch.cpp
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.h"
diff --git a/examples/apps/windows/pch.h b/examples/apps/windows/pch.h
new file mode 100644
index 0000000..af044ab
--- /dev/null
+++ b/examples/apps/windows/pch.h
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <WinSock2.h>
+#include <Windows.h>
+#include <collection.h>
+#include <ppltasks.h>
+
+#include <ws2def.h>
+#include <ws2ipdef.h>
+#include <mstcpip.h>
+
+#include "otApi.h"
+
+#include "App.xaml.h"
diff --git a/examples/common-switches.mk b/examples/common-switches.mk
new file mode 100644
index 0000000..c43d684
--- /dev/null
+++ b/examples/common-switches.mk
@@ -0,0 +1,91 @@
+#
+#  Copyright (c) 2016-2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+ifeq ($(TMF_PROXY),1)
+configure_OPTIONS              += --enable-tmf-proxy
+endif
+
+ifeq ($(BORDER_ROUTER),1)
+configure_OPTIONS              += --enable-border-router
+endif
+
+ifeq ($(CERT_LOG),1)
+configure_OPTIONS              += --enable-cert-log
+endif
+
+ifeq ($(COAP),1)
+configure_OPTIONS              += --enable-application-coap
+endif
+
+ifeq ($(COMMISSIONER),1)
+configure_OPTIONS              += --enable-commissioner
+endif
+
+ifeq ($(COVERAGE),1)
+configure_OPTIONS              += --enable-coverage
+endif
+
+ifeq ($(DEBUG),1)
+configure_OPTIONS              += --enable-debug --enable-optimization=no
+endif
+
+ifeq ($(DHCP6_CLIENT),1)
+configure_OPTIONS              += --enable-dhcp6-client
+endif
+
+ifeq ($(DHCP6_SERVER),1)
+configure_OPTIONS              += --enable-dhcp6-server
+endif
+
+ifeq ($(DISABLE_DOC),1)
+configure_OPTIONS              += --disable-docs
+endif
+
+ifeq ($(DNS_CLIENT),1)
+configure_OPTIONS              += --enable-dns-client
+endif
+
+ifeq ($(JAM_DETECTION),1)
+configure_OPTIONS              += --enable-jam-detection
+endif
+
+ifeq ($(JOINER),1)
+configure_OPTIONS              += --enable-joiner
+endif
+
+ifeq ($(LEGACY),1)
+configure_OPTIONS              += --enable-legacy
+endif
+
+ifeq ($(MAC_WHITELIST),1)
+configure_OPTIONS              += --enable-mac-whitelist
+endif
+
+ifeq ($(MTD_NETDIAG),1)
+configure_OPTIONS              += --enable-mtd-network-diagnostic
+endif
diff --git a/examples/drivers/windows/README.md b/examples/drivers/windows/README.md
new file mode 100644
index 0000000..9d1e9c9
--- /dev/null
+++ b/examples/drivers/windows/README.md
@@ -0,0 +1,54 @@
+# OpenThread on Windows #
+
+These components are the building blocks to get OpenThread integrated into the Windows
+networking stack and provide an interface for applications to control it.
+
+## Architecture ##
+
+[ndis]: https://msdn.microsoft.com/en-us/windows/hardware/drivers/network/ndis-drivers
+[lwf]: https://msdn.microsoft.com/en-us/windows/hardware/drivers/network/ndis-filter-drivers
+[miniport]: https://msdn.microsoft.com/en-us/windows/hardware/drivers/network/ndis-miniport-drivers2
+[ioctl]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363219(v=vs.85).aspx
+[oid]: https://msdn.microsoft.com/en-us/library/windows/hardware/ff566707(v=vs.85).aspx
+[nbl]: https://msdn.microsoft.com/en-us/windows/hardware/drivers/network/net-buffer-architecture
+
+![Windows Architecture](../../../doc/images/windows_design.png)
+
+This design allows for support of both simple radio devices and devices running the complete
+OpenThread stack.
+
+### otApi.dll ###
+
+This is the dynamic libray for applications to control the OpenThread stack from user mode. It
+exposes all the control path APIs from `openthread.h`. It interfaces with the driver by the use
+of [IOCTL][ioctl]s. The IOCTLs allow otApi.dll to serialize and send commands, and poll for notifications,
+which can then be returned back to the client.
+
+### otLwf.sys ###
+
+This is where most of the real logic lives. `otLwf.sys` is an [NDIS][ndis] Light Weight Filter ([LWF][lwf]) driver.
+It plugs into the networking stack, binding to a protocol driver (TCPIP) at the top, and an NDIS [Miniport][miniport]
+at the bottom. It's job is to take IPv6 packets from TCPIP and pass the necessary data down to the Miniport
+in order to send the packets out over the network.
+
+`otLwf.sys` supports operating in two modes: Full Stack and Tunnel. Full Stack mode is where OpenThread is
+running on the host (in Windows) and a simple radio device is connected externally. Tunnel mode is where
+OpenThread is running on the external device and Windows is merely a pass through for commands and packets.
+
+In both cases, `otLwf.sys` uses the Spinel command interface for interacting with the connected device. When operating
+in Full Stack mode, `otLwf.sys` uses only the low level PHY/MAC commands. In Tunnel mode, it uses the higher layer
+Spinel commands and lets the device manage the actual Thread stack.
+
+### ottmp.sys ###
+
+This is the component responsible passing the Spinel commands from `otLwf.sys` down to the device. It is responsible
+for abstracting the actual mechanism (USB, Serial, SPI) used for communicating with the device. It handles the device
+arrival/removal and the encoding/decoding of data when communicating with it. The current implementation only handles
+Serial devices.
+
+### Device ###
+
+Windows supports OpenThread devices that implement the Spinel protocol. It supports devices that support either the raw
+link-layer PHY/MAC commands and devices that support the Thread commands (and devices that support both). By default,
+Windows will operate in Full Stack mode, only sending raw link-layer commands.
+
diff --git a/examples/drivers/windows/include/openthread-core-windows-config.h b/examples/drivers/windows/include/openthread-core-windows-config.h
new file mode 100644
index 0000000..c0af32a
--- /dev/null
+++ b/examples/drivers/windows/include/openthread-core-windows-config.h
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes Windows compile-time configuration constants
+ *   for OpenThread.
+ */
+
+#ifndef OPENTHREAD_CORE_WINDOWS_CONFIG_H_
+#define OPENTHREAD_CORE_WINDOWS_CONFIG_H_
+
+/**
+ * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
+ *
+ * The number of message buffers in the buffer pool.
+ *
+ */
+#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS               2048
+
+/**
+ * @def OPENTHREAD_CONFIG_MAX_CHILDREN
+ *
+ * The maximum number of children.
+ *
+ */
+#define OPENTHREAD_CONFIG_MAX_CHILDREN                      32
+
+/**
+ * @def OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD
+ *
+ * The minimum number of supported IPv6 address registrations per child.
+ *
+ */
+#define OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD                6
+
+/**
+ * @def OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES
+ *
+ * The maximum number of Joiner entries maintained by the Commissioner.
+ *
+ */
+#define OPENTHREAD_CONFIG_MAX_JOINER_ENTRIES                16
+
+/**
+ * @def OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
+ *
+ * The message pool is managed by platform defined logic when this flag is set.
+ * This feature would typically be used when operating in a multi-threaded system
+ * and multiple threads need to access the message pool.
+ *
+ */
+#define OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT       1
+
+/**
+ * @def OPENTHREAD_CONFIG_LOG_LEVEL
+ *
+ * The log level.
+ *
+ */
+#define OPENTHREAD_CONFIG_LOG_LEVEL                         OT_LOG_LEVEL_DEBG
+
+ /**
+ * @def OPENTHREAD_CONFIG_LOG_PKT_DUMP
+ *
+ * Define to enable log content of packets.
+ *
+ */
+#define OPENTHREAD_CONFIG_LOG_PKT_DUMP                      0
+
+#endif  // OPENTHREAD_CORE_WINDOWS_CONFIG_H_
+
diff --git a/examples/drivers/windows/include/otLwfIoctl.h b/examples/drivers/windows/include/otLwfIoctl.h
new file mode 100644
index 0000000..5dbdf6d
--- /dev/null
+++ b/examples/drivers/windows/include/otLwfIoctl.h
@@ -0,0 +1,706 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines the IOCTL interface for otLwf.sys.
+ */
+
+#ifndef __OTLWFIOCTL_H__
+#define __OTLWFIOCTL_H__
+
+#include <openthread/types.h>
+
+__inline LONG ThreadErrorToNtstatus(otError error) { return (LONG)-((int)error); }
+
+// User-mode IOCTL path for CreateFile
+#define OTLWF_IOCLT_PATH      TEXT("\\\\.\\\\otlwf")
+
+//
+// IOCLTs and Data Types
+//
+
+#define OTLWF_CTL_CODE(request, method, access) \
+    CTL_CODE(FILE_DEVICE_NETWORK, request, method, access)
+
+// Different possible notification types
+typedef enum _OTLWF_NOTIF_TYPE
+{
+    OTLWF_NOTIF_UNSPECIFIED,
+    OTLWF_NOTIF_DEVICE_AVAILABILITY,
+    OTLWF_NOTIF_STATE_CHANGE,
+    OTLWF_NOTIF_DISCOVER,
+    OTLWF_NOTIF_ACTIVE_SCAN,
+    OTLWF_NOTIF_ENERGY_SCAN,
+    OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT,
+    OTLWF_NOTIF_COMMISSIONER_PANID_QUERY,
+    OTLWF_NOTIF_JOINER_COMPLETE
+
+} OTLWF_NOTIF_TYPE;
+
+#define MAX_ENERGY_REPORT_LENGTH    64
+
+//
+// Queries (async) the next notification in the queue
+//
+#define IOCTL_OTLWF_QUERY_NOTIFICATION \
+    OTLWF_CTL_CODE(0, METHOD_BUFFERED, FILE_READ_DATA)
+    typedef struct _OTLWF_NOTIFICATION {
+        GUID                InterfaceGuid;
+        OTLWF_NOTIF_TYPE    NotifType;
+        union
+        {
+            // Payload for OTLWF_NOTIF_DEVICE_AVAILABILITY
+            struct
+            {
+                BOOLEAN                 Available;
+            } DeviceAvailabilityPayload;
+
+            // Payload for OTLWF_NOTIF_STATE_CHANGE
+            struct
+            {
+                uint32_t                Flags;
+            } StateChangePayload;
+
+            // Payload for OTLWF_NOTIF_DISCOVER
+            struct
+            {
+                BOOLEAN                 Valid;
+                otActiveScanResult      Results;
+            } DiscoverPayload;
+
+            // Payload for OTLWF_NOTIF_ACTIVE_SCAN
+            struct
+            {
+                BOOLEAN                 Valid;
+                otActiveScanResult      Results;
+            } ActiveScanPayload;
+
+            // Payload for OTLWF_NOTIF_ENERGY_SCAN
+            struct
+            {
+                BOOLEAN                 Valid;
+                otEnergyScanResult      Results;
+            } EnergyScanPayload;
+
+            // Payload for OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT
+            struct
+            {
+                uint32_t                ChannelMask;
+                uint8_t                 EnergyListLength;
+                uint8_t                 EnergyList[MAX_ENERGY_REPORT_LENGTH];
+
+            } CommissionerEnergyReportPayload;
+
+            // Payload for OTLWF_NOTIF_COMMISSIONER_PANID_QUERY
+            struct
+            {
+                uint16_t                PanId;
+                uint32_t                ChannelMask;
+            } CommissionerPanIdQueryPayload;
+
+            // Payload for OTLWF_NOTIF_JOINER_COMPLETE
+            struct
+            {
+                otError             Error;
+            } JoinerCompletePayload;
+        };
+    } OTLWF_NOTIFICATION, *POTLWF_NOTIFICATION;
+
+//
+// Enumerates all the Thread interfaces guids
+//
+#define IOCTL_OTLWF_ENUMERATE_DEVICES \
+    OTLWF_CTL_CODE(1, METHOD_BUFFERED, FILE_READ_DATA)
+    typedef struct _OTLWF_INTERFACE_LIST
+    {
+        uint16_t cInterfaceGuids;
+        GUID     InterfaceGuids[1];
+    } OTLWF_INTERFACE_LIST, *POTLWF_INTERFACE_LIST;
+
+//
+// Queries the detials of a given device Thread interfaces
+//
+#define IOCTL_OTLWF_QUERY_DEVICE \
+    OTLWF_CTL_CODE(2, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid (in)
+    typedef struct _OTLWF_DEVICE {
+        ULONG           CompartmentID;
+    } OTLWF_DEVICE, *POTLWF_DEVICE;
+
+//
+// Proxies to ot* APIs in otLwf.sys
+//
+
+/* REMOVED
+#define IOCTL_OTLWF_OT_ENABLED \
+    OTLWF_CTL_CODE(100, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aEnabled
+*/
+#define IOCTL_OTLWF_OT_INTERFACE \
+    OTLWF_CTL_CODE(101, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aUp
+
+#define IOCTL_OTLWF_OT_THREAD \
+    OTLWF_CTL_CODE(102, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aStarted
+
+#define IOCTL_OTLWF_OT_ACTIVE_SCAN \
+    OTLWF_CTL_CODE(103, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aScanChannels
+    // uint16_t - aScanDuration
+
+#define IOCTL_OTLWF_OT_DISCOVER \
+    OTLWF_CTL_CODE(104, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aScanChannels
+    // uint16_t - aScanDuration
+    // uint16_t - aPanid
+
+#define IOCTL_OTLWF_OT_CHANNEL \
+    OTLWF_CTL_CODE(105, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aChannel
+
+#define IOCTL_OTLWF_OT_CHILD_TIMEOUT \
+    OTLWF_CTL_CODE(106, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aTimeout
+
+#define IOCTL_OTLWF_OT_EXTENDED_ADDRESS \
+    OTLWF_CTL_CODE(107, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtendedAddress
+
+#define IOCTL_OTLWF_OT_EXTENDED_PANID \
+    OTLWF_CTL_CODE(108, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtendedPanId - aExtendedPanId
+
+#define IOCTL_OTLWF_OT_LEADER_RLOC \
+    OTLWF_CTL_CODE(109, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Address - aLeaderRloc
+
+#define IOCTL_OTLWF_OT_LINK_MODE \
+    OTLWF_CTL_CODE(110, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otLinkModeConfig - aConfig
+
+#define IOCTL_OTLWF_OT_MASTER_KEY \
+    OTLWF_CTL_CODE(111, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otMasterKey - aKey
+    // uint8_t - aKeyLength
+
+#define IOCTL_OTLWF_OT_MESH_LOCAL_EID \
+    OTLWF_CTL_CODE(112, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Address - aMeshLocalEid
+
+#define IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX \
+    OTLWF_CTL_CODE(113, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otMeshLocalPrefix - aPrefix
+
+#define IOCTL_OTLWF_OT_NETWORK_DATA_LEADER \
+    OTLWF_CTL_CODE(114, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t[] - aData
+
+#define IOCTL_OTLWF_OT_NETWORK_DATA_LOCAL \
+    OTLWF_CTL_CODE(115, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t[] - aData
+
+#define IOCTL_OTLWF_OT_NETWORK_NAME \
+    OTLWF_CTL_CODE(116, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otNetworkName - aNetworkName
+
+#define IOCTL_OTLWF_OT_PAN_ID \
+    OTLWF_CTL_CODE(117, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otPanId - aPanId
+
+#define IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED \
+    OTLWF_CTL_CODE(118, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aEnabled
+
+#define IOCTL_OTLWF_OT_SHORT_ADDRESS \
+    OTLWF_CTL_CODE(119, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otShortAddress - aShortAddress
+
+/* NOT USED
+#define IOCTL_OTLWF_OT_UNICAST_ADDRESSES \
+    OTLWF_CTL_CODE(120, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otNetifAddress[] - aAddresses
+*/
+
+#define IOCTL_OTLWF_OT_ACTIVE_DATASET \
+    OTLWF_CTL_CODE(121, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otOperationalDataset - aDataset
+
+#define IOCTL_OTLWF_OT_PENDING_DATASET \
+    OTLWF_CTL_CODE(122, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otOperationalDataset - aDataset
+
+#define IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT \
+    OTLWF_CTL_CODE(123, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aWeight
+
+#define IOCTL_OTLWF_OT_ADD_BORDER_ROUTER \
+    OTLWF_CTL_CODE(124, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otBorderRouterConfig - aConfig
+
+#define IOCTL_OTLWF_OT_REMOVE_BORDER_ROUTER \
+    OTLWF_CTL_CODE(125, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Prefix - aPrefix
+
+#define IOCTL_OTLWF_OT_ADD_EXTERNAL_ROUTE \
+    OTLWF_CTL_CODE(126, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExternalRouteConfig - aConfig
+
+#define IOCTL_OTLWF_OT_REMOVE_EXTERNAL_ROUTE \
+    OTLWF_CTL_CODE(127, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Prefix - aPrefix
+
+#define IOCTL_OTLWF_OT_SEND_SERVER_DATA \
+    OTLWF_CTL_CODE(128, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY \
+    OTLWF_CTL_CODE(129, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aDelay
+
+#define IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER \
+    OTLWF_CTL_CODE(130, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aKeySequenceCounter
+
+#define IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT \
+    OTLWF_CTL_CODE(131, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aTimeout
+
+#define IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD \
+    OTLWF_CTL_CODE(132, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aThreshold
+
+#define IOCTL_OTLWF_OT_RELEASE_ROUTER_ID \
+    OTLWF_CTL_CODE(133, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aRouterId
+
+#define IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED \
+    OTLWF_CTL_CODE(134, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aEnabled
+
+#define IOCTL_OTLWF_OT_ADD_MAC_WHITELIST \
+    OTLWF_CTL_CODE(135, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtAddr
+    // int8_t - aRssi (optional)
+
+#define IOCTL_OTLWF_OT_REMOVE_MAC_WHITELIST \
+    OTLWF_CTL_CODE(136, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtAddr
+
+#define IOCTL_OTLWF_OT_MAC_WHITELIST_ENTRY \
+    OTLWF_CTL_CODE(137, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aIndex (input)
+    // otMacWhitelistEntry - aEntry (output)
+
+#define IOCTL_OTLWF_OT_CLEAR_MAC_WHITELIST \
+    OTLWF_CTL_CODE(138, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_DEVICE_ROLE \
+    OTLWF_CTL_CODE(139, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otDeviceRole - aRole
+    // otMleAttachFilter - aFilter (only for OT_DEVICE_ROLE_CHILD)
+
+#define IOCTL_OTLWF_OT_CHILD_INFO_BY_ID \
+    OTLWF_CTL_CODE(140, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint16_t - aChildId (input)
+    // otChildInfo - aChildInfo (output)
+
+#define IOCTL_OTLWF_OT_CHILD_INFO_BY_INDEX \
+    OTLWF_CTL_CODE(141, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aChildIndex (input)
+    // otChildInfo - aChildInfo (output)
+
+#define IOCTL_OTLWF_OT_EID_CACHE_ENTRY \
+    OTLWF_CTL_CODE(142, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aIndex (input)
+    // otEidCacheEntry - aEntry (output)
+
+#define IOCTL_OTLWF_OT_LEADER_DATA \
+    OTLWF_CTL_CODE(143, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otLeaderData - aLeaderData
+
+#define IOCTL_OTLWF_OT_LEADER_ROUTER_ID \
+    OTLWF_CTL_CODE(144, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aRouterID
+
+#define IOCTL_OTLWF_OT_LEADER_WEIGHT \
+    OTLWF_CTL_CODE(145, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aWeight
+
+#define IOCTL_OTLWF_OT_NETWORK_DATA_VERSION \
+    OTLWF_CTL_CODE(146, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aVersion
+
+#define IOCTL_OTLWF_OT_PARTITION_ID \
+    OTLWF_CTL_CODE(147, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aPartition
+
+#define IOCTL_OTLWF_OT_RLOC16 \
+    OTLWF_CTL_CODE(148, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint16_t - aRloc16
+
+#define IOCTL_OTLWF_OT_ROUTER_ID_SEQUENCE \
+    OTLWF_CTL_CODE(149, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aIdSequence
+
+#define IOCTL_OTLWF_OT_ROUTER_INFO \
+    OTLWF_CTL_CODE(150, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint16_t - aRouterId (input)
+    // otRouterInfo - aRouterInfo (output)
+
+#define IOCTL_OTLWF_OT_STABLE_NETWORK_DATA_VERSION \
+    OTLWF_CTL_CODE(151, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aVersion
+
+#define IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED \
+    OTLWF_CTL_CODE(152, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aEnabled
+
+#define IOCTL_OTLWF_OT_ADD_MAC_BLACKLIST \
+    OTLWF_CTL_CODE(153, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtAddr
+
+#define IOCTL_OTLWF_OT_REMOVE_MAC_BLACKLIST \
+    OTLWF_CTL_CODE(154, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtAddr
+
+#define IOCTL_OTLWF_OT_MAC_BLACKLIST_ENTRY \
+    OTLWF_CTL_CODE(155, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aIndex (input)
+    // otMacBlacklistEntry - aEntry (output)
+
+#define IOCTL_OTLWF_OT_CLEAR_MAC_BLACKLIST \
+    OTLWF_CTL_CODE(156, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER \
+    OTLWF_CTL_CODE(157, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // int8_t - aPower
+
+#define IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX \
+    OTLWF_CTL_CODE(158, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aLocal (input)
+    // uint8_t - aIterator (input)
+    // uint8_t - aNewIterator (output)
+    // otBorderRouterConfig - aConfig (output)
+
+#define IOCTL_OTLWF_OT_POLL_PERIOD \
+    OTLWF_CTL_CODE(159, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aPollPeriod
+
+#define IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID \
+    OTLWF_CTL_CODE(160, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aPartitionId
+
+#define IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY \
+    OTLWF_CTL_CODE(161, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aExtAddr (input)
+    // uint8_t - aLinkQuality (input or output)
+
+#define IOCTL_OTLWF_OT_PLATFORM_RESET \
+    OTLWF_CTL_CODE(162, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_PARENT_INFO \
+    OTLWF_CTL_CODE(163, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otRouterInfo - aParentInfo
+
+#define IOCTL_OTLWF_OT_SINGLETON \
+    OTLWF_CTL_CODE(164, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aSingleton
+
+#define IOCTL_OTLWF_OT_MAC_COUNTERS \
+    OTLWF_CTL_CODE(165, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otMacCounters - aCounters
+
+#define IOCTL_OTLWF_OT_MAX_CHILDREN \
+    OTLWF_CTL_CODE(166, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aMaxChildren
+
+#define IOCTL_OTLWF_OT_COMMISIONER_START \
+    OTLWF_CTL_CODE(167, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_COMMISIONER_STOP \
+    OTLWF_CTL_CODE(168, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define OPENTHREAD_PSK_MAX_LENGTH                32
+#define OPENTHREAD_PROV_URL_MAX_LENGTH           64
+#define OPENTHREAD_VENDOR_NAME_MAX_LENGTH        32
+#define OPENTHREAD_VENDOR_MODEL_MAX_LENGTH       32
+#define OPENTHREAD_VENDOR_SW_VERSION_MAX_LENGTH  16
+#define OPENTHREAD_VENDOR_DATA_MAX_LENGTH        64
+typedef struct otCommissionConfig
+{
+    char PSKd[OPENTHREAD_PSK_MAX_LENGTH + 1];
+    char ProvisioningUrl[OPENTHREAD_PROV_URL_MAX_LENGTH + 1];
+    char VendorName[OPENTHREAD_VENDOR_NAME_MAX_LENGTH + 1];
+    char VendorModel[OPENTHREAD_VENDOR_MODEL_MAX_LENGTH + 1];
+    char VendorSwVersion[OPENTHREAD_VENDOR_SW_VERSION_MAX_LENGTH + 1];
+    char VendorData[OPENTHREAD_VENDOR_DATA_MAX_LENGTH + 1];
+} otCommissionConfig;
+
+#define IOCTL_OTLWF_OT_JOINER_START \
+    OTLWF_CTL_CODE(169, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otCommissionConfig - aConfig
+
+#define IOCTL_OTLWF_OT_JOINER_STOP \
+    OTLWF_CTL_CODE(170, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_FACTORY_EUI64 \
+    OTLWF_CTL_CODE(171, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aEui64
+
+#define IOCTL_OTLWF_OT_HASH_MAC_ADDRESS \
+    OTLWF_CTL_CODE(172, METHOD_BUFFERED, FILE_READ_DATA)
+    // GUID - InterfaceGuid
+    // otExtAddress - aEui64
+
+#define IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD \
+    OTLWF_CTL_CODE(173, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aThreshold
+
+#define IOCTL_OTLWF_OT_COMMISSIONER_PANID_QUERY \
+    OTLWF_CTL_CODE(174, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint16_t - aPanId
+    // uint32_t - aChannekMask
+    // otIp6Address - aAddress
+
+#define IOCTL_OTLWF_OT_COMMISSIONER_ENERGY_SCAN \
+    OTLWF_CTL_CODE(175, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aChannekMask
+    // uint8_t - aCount
+    // uint16_t - aPeriod
+    // uint16_t - aScanDuration
+    // otIp6Address - aAddress
+
+#define IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER \
+    OTLWF_CTL_CODE(176, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aRouterJitter
+
+#define IOCTL_OTLWF_OT_JOINER_UDP_PORT \
+    OTLWF_CTL_CODE(177, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint16_t - aJoinerUdpPort
+
+#define IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_GET \
+    OTLWF_CTL_CODE(178, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Address - aDestination
+    // uint8_t - aCount
+    // uint8_t[aCount] - aTlvTypes
+
+#define IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_RESET \
+    OTLWF_CTL_CODE(179, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otIp6Address - aDestination
+    // uint8_t - aCount
+    // uint8_t[aCount] - aTlvTypes
+
+#define IOCTL_OTLWF_OT_COMMISIONER_ADD_JOINER \
+    OTLWF_CTL_CODE(180, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aExtAddressValid
+    // otExtAddress - aExtAddress (optional)
+    // char[OPENTHREAD_PSK_MAX_LENGTH + 1] - aPSKd
+    // uint32_t - aTimeout
+
+#define IOCTL_OTLWF_OT_COMMISIONER_REMOVE_JOINER \
+    OTLWF_CTL_CODE(181, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aExtAddressValid
+    // otExtAddress - aExtAddress (optional)
+
+#define IOCTL_OTLWF_OT_COMMISIONER_PROVISIONING_URL \
+    OTLWF_CTL_CODE(182, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // char[OPENTHREAD_PROV_URL_MAX_LENGTH + 1] - aProvisioningUrl (optional)
+
+#define IOCTL_OTLWF_OT_COMMISIONER_ANNOUNCE_BEGIN \
+    OTLWF_CTL_CODE(183, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aChannelMask
+    // uint8_t - aCount
+    // uint16_t - aPeriod
+    // otIp6Address - aAddress
+
+#define IOCTL_OTLWF_OT_ENERGY_SCAN \
+    OTLWF_CTL_CODE(184, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aScanChannels
+    // uint16_t - aScanDuration
+
+#define IOCTL_OTLWF_OT_SEND_ACTIVE_GET \
+    OTLWF_CTL_CODE(185, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvTypes
+    // otIp6Address - aAddress (optional)
+
+#define IOCTL_OTLWF_OT_SEND_ACTIVE_SET \
+    OTLWF_CTL_CODE(186, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otOperationalDataset - aDataset
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvTypes
+
+#define IOCTL_OTLWF_OT_SEND_PENDING_GET \
+    OTLWF_CTL_CODE(187, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvTypes
+    // otIp6Address - aAddress (optional)
+
+#define IOCTL_OTLWF_OT_SEND_PENDING_SET \
+    OTLWF_CTL_CODE(188, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otOperationalDataset - aDataset
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvTypes
+
+#define IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_GET \
+    OTLWF_CTL_CODE(189, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvs
+
+#define IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_SET \
+    OTLWF_CTL_CODE(190, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otOperationalDataset - aDataset
+    // uint8_t - aLength
+    // uint8_t[aLength] - aTlvs
+
+#define IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME \
+    OTLWF_CTL_CODE(191, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint32_t - aKeySwitchGuardTime
+
+#define IOCTL_OTLWF_OT_FACTORY_RESET \
+    OTLWF_CTL_CODE(192, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+
+#define IOCTL_OTLWF_OT_THREAD_AUTO_START \
+    OTLWF_CTL_CODE(193, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // BOOLEAN - aAutoStart
+
+#define IOCTL_OTLWF_OT_PREFERRED_ROUTER_ID \
+    OTLWF_CTL_CODE(194, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // uint8_t - aRouterId
+
+#define IOCTL_OTLWF_OT_PSKC \
+    OTLWF_CTL_CODE(195, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // otPSKc - aPSKc
+
+#define IOCTL_OTLWF_OT_PARENT_PRIORITY \
+    OTLWF_CTL_CODE(196, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+    // GUID - InterfaceGuid
+    // int8_t - aParentPriority
+
+// OpenThread function IOCTL codes
+#define MIN_OTLWF_IOCTL_FUNC_CODE 100
+#define MAX_OTLWF_IOCTL_FUNC_CODE 196
+
+#endif //__OTLWFIOCTL_H__
diff --git a/examples/drivers/windows/include/otNBLContext.h b/examples/drivers/windows/include/otNBLContext.h
new file mode 100644
index 0000000..b5204f2
--- /dev/null
+++ b/examples/drivers/windows/include/otNBLContext.h
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines the context structure for NBLs send between otLwf and it's miniport.
+ */
+
+#ifndef __OT_NBL_CONTEXT_H__
+#define __OT_NBL_CONTEXT_H__
+
+#ifndef _NDIS_
+#define NET_BUFFER_LIST_INFO(_NBL, _Id)             ((_NBL)->NetBufferListInfo[(_Id)])
+#endif
+
+// Flag that indicates the ACK received had the Frame pending flag
+#define OT_NBL_FLAG_ACK_FRAME_PENDING   0x01
+
+// Represents the data necessary for the MAC layer to send out the NetBufferList
+// Must be saved in: NET_BUFFER_LIST_INFO(NetBufferList, MediaSpecificInformationEx)
+typedef struct _OT_NBL_CONTEXT
+{
+    // Flags
+    UCHAR Flags;
+
+    // Channel used to transmit/receive the frame.
+    UCHAR Channel;
+
+    // Transmit/receive power in dBm.
+    CHAR  Power;
+
+    // Link Quality Indicator for received frames.
+    UCHAR Lqi;
+
+} OT_NBL_CONTEXT, *POT_NBL_CONTEXT;
+
+// OT_NBL_CONTEXT must fit in the pointer used for MediaSpecificInformationEx in the NBL
+C_ASSERT(sizeof(OT_NBL_CONTEXT) <= sizeof(PVOID));
+
+// Helper to set the OT_NBL_CONTEXT attached to the NetBufferList
+__forceinline VOID SetNBLContext(_In_ PNET_BUFFER_LIST NetBufferList, _In_ POT_NBL_CONTEXT Context)
+{
+    *(POT_NBL_CONTEXT)(&NET_BUFFER_LIST_INFO(NetBufferList, MediaSpecificInformationEx)) = *Context;
+}
+
+// Helper to return the OT_NBL_CONTEXT attached to the NetBufferList
+__forceinline POT_NBL_CONTEXT GetNBLContext(_In_ PNET_BUFFER_LIST NetBufferList)
+{
+    return (POT_NBL_CONTEXT)(&NET_BUFFER_LIST_INFO(NetBufferList, MediaSpecificInformationEx));
+}
+
+#endif //__OT_NBL_CONTEXT_H__
diff --git a/examples/drivers/windows/include/otNode.h b/examples/drivers/windows/include/otNode.h
new file mode 100644
index 0000000..8b8a95b
--- /dev/null
+++ b/examples/drivers/windows/include/otNode.h
@@ -0,0 +1,403 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines a node interface for openthread.h to be used for certification tests
+ */
+
+#ifndef OTNODE_H_
+#define OTNODE_H_
+
+#include <openthread/openthread.h>
+
+#ifndef OTNODEAPI
+#define OTNODEAPI __declspec(dllimport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Represents a virtual node for an openthread interface
+ */
+typedef struct otNode otNode;
+
+/**
+ * Logs a WPP message
+ */
+OTNODEAPI int32_t OTCALL otNodeLog(const char *aMessage);
+
+/**
+ * Allocates a new virtual node
+ */
+OTNODEAPI otNode* OTCALL otNodeInit(uint32_t id);
+
+/**
+ * Frees a node
+ */
+OTNODEAPI int32_t OTCALL otNodeFinalize(otNode* aNode);
+
+/**
+ * Sets the link mode
+ */
+OTNODEAPI int32_t OTCALL otNodeSetMode(otNode* aNode, const char *aMode);
+
+/**
+ * Starts the thread interface
+ */
+OTNODEAPI int32_t OTCALL otNodeInterfaceUp(otNode* aNode);
+
+/**
+ * Stops the thread interface
+ */
+OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode);
+
+/**
+ * Starts the thread logic
+ */
+OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode);
+
+/**
+ * Stops the thread logic
+ */
+OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode);
+
+/**
+ * Starts the commissioner logic
+ */
+OTNODEAPI int32_t OTCALL otNodeCommissionerStart(otNode* aNode);
+
+/**
+ * Adds a new joiner to the list for commissioning
+ */
+OTNODEAPI int32_t OTCALL otNodeCommissionerJoinerAdd(otNode* aNode, const char *aExtAddr, const char *aPSKd);
+
+/**
+ * Stops the commissioner logic
+ */
+OTNODEAPI int32_t OTCALL otNodeCommissionerStop(otNode* aNode);
+
+/**
+ * Starts the joiner logic
+ */
+OTNODEAPI int32_t OTCALL otNodeJoinerStart(otNode* aNode, const char *aPSKd, const char *aProvisioningUrl);
+
+/**
+ * Clears the node's whitelist
+ */
+OTNODEAPI int32_t OTCALL otNodeClearWhitelist(otNode* aNode);
+
+/**
+ * Enables the node's whitelist
+ */
+OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode);
+
+/**
+ * Disables the node's whitelist
+ */
+OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode);
+
+/**
+ * Adds an entry to the node's whitelist
+ */
+OTNODEAPI int32_t OTCALL otNodeAddWhitelist(otNode* aNode, const char *aExtAddr, int8_t aRssi);
+
+/**
+ * Removes an entry to the node's whitelist
+ */
+OTNODEAPI int32_t OTCALL otNodeRemoveWhitelist(otNode* aNode, const char *aExtAddr);
+
+/**
+ * Gets the node's short mac address (Rloc16)
+ */
+OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode);
+
+/**
+ * Gets the node's extended mac address
+ */
+OTNODEAPI const char* OTCALL otNodeGetAddr64(otNode* aNode);
+
+/**
+ * Gets the node's hash mac address
+ */
+OTNODEAPI const char* OTCALL otNodeGetHashMacAddress(otNode* aNode);
+
+/**
+ * Sets the channel for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetChannel(otNode* aNode, uint8_t aChannel);
+
+/**
+ * Gets the channel for the node
+ */
+OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode);
+
+/**
+ * sets the node's master key
+ */
+OTNODEAPI int32_t OTCALL otNodeSetMasterkey(otNode* aNode, const char *aMasterkey);
+
+/**
+ * Gets the node's master key
+ */
+OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode);
+
+/**
+ * Gets the key sequence counter for the node
+ */
+OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode);
+
+/**
+ * Sets the key sequence counter for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetKeySequenceCounter(otNode* aNode, uint32_t aSequence);
+
+/**
+ * Sets the key switch guard time for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetKeySwitchGuardTime(otNode* aNode, uint32_t aSequence);
+
+/**
+ * Sets the network id timeout for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetNetworkIdTimeout(otNode* aNode, uint8_t aTimeout);
+
+/**
+ * Gets the network name for the node
+ */
+OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode);
+
+/**
+ * Sets the network name for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetNetworkName(otNode* aNode, const char *aName);
+
+/**
+ * Gets the pan id for the node
+ */
+OTNODEAPI uint16_t OTCALL otNodeGetPanId(otNode* aNode);
+
+/**
+ * Sets the pan id for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetPanId(otNode* aNode, uint16_t aPanId);
+
+/**
+ * Gets the partition id for the node
+ */
+OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode);
+
+/**
+ * Sets the partition id for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetPartitionId(otNode* aNode, uint32_t aPartitionId);
+
+/**
+ * Sets the router upgrade threshold for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetRouterUpgradeThreshold(otNode* aNode, uint8_t aThreshold);
+
+/**
+ * Sets the router downgrade threshold for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetRouterDowngradeThreshold(otNode* aNode, uint8_t aThreshold);
+
+/**
+ * Releases a router id for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeReleaseRouterId(otNode* aNode, uint8_t aRouterId);
+
+/**
+ * Gets the node's state
+ */
+OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode);
+
+/**
+ * Sets the node's state
+ */
+OTNODEAPI int32_t OTCALL otNodeSetState(otNode* aNode, const char *aState);
+
+/**
+ * Gets the child timeout for the node
+ */
+OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode);
+
+/**
+ * Sets the child timeout for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetTimeout(otNode* aNode, uint32_t aTimeout);
+
+/**
+ * Gets the leader weight for the node
+ */
+OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode);
+
+/**
+ * Sets the leader weight for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetWeight(otNode* aNode, uint8_t aWeight);
+
+/**
+ * Adds an IP address for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeAddIpAddr(otNode* aNode, const char *aAddr);
+
+/**
+ * Gets the IP address for the node
+ */
+OTNODEAPI const char* OTCALL otNodeGetAddrs(otNode* aNode);
+
+/**
+ * Gets the context reuse delay for the node
+ */
+OTNODEAPI uint32_t OTCALL otNodeGetContextReuseDelay(otNode* aNode);
+
+/**
+ * Sets the context reuse delay for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetContextReuseDelay(otNode* aNode, uint32_t aDelay);
+
+/**
+ * Adds an IP prefix for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeAddPrefix(otNode* aNode, const char *aPrefix, const char *aFlags, const char *aPreference);
+
+/**
+ * Removes an IP prefix from the node
+ */
+OTNODEAPI int32_t OTCALL otNodeRemovePrefix(otNode* aNode, const char *aPrefix);
+
+/**
+ * Adds an IP route for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeAddRoute(otNode* aNode, const char *aPrefix, const char *aPreference);
+
+/**
+ * Removes an IP route from the node
+ */
+OTNODEAPI int32_t OTCALL otNodeRemoveRoute(otNode* aNode, const char *aPrefix);
+
+/**
+ * Registers the net data for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeRegisterNetdata(otNode* aNode);
+
+/**
+ * Performs an energy scan for the node
+ */
+OTNODEAPI int32_t OTCALL otNodeEnergyScan(otNode* aNode, uint32_t aMask, uint8_t aCount, uint16_t aPeriod, uint16_t aDuration, const char *aAddr);
+
+/**
+ * Performs a panid query for the node
+ */
+OTNODEAPI int32_t OTCALL otNodePanIdQuery(otNode* aNode, uint16_t aPanId, uint32_t aMask, const char *aAddr);
+
+/**
+ * Performs an scan for the node
+ */
+OTNODEAPI const char* OTCALL otNodeScan(otNode* aNode);
+
+/**
+ * Performs an scan for the node
+ */
+OTNODEAPI uint32_t OTCALL otNodePing(otNode* aNode, const char *aAddr, uint16_t aSize, uint32_t aMinReplies, uint16_t aTimeout);
+
+/**
+ * Sets the router selection jitter value for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetRouterSelectionJitter(otNode* aNode, uint8_t aRouterJitter);
+
+/**
+ * Sends the announce message for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeCommissionerAnnounceBegin(otNode* aNode, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, const char *aAddr);
+
+/**
+ * Sets the active dataset for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetActiveDataset(otNode* aNode, uint64_t aTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aMasterKey);
+
+/**
+ * Sets the pending dataset for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetPendingDataset(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint16_t aPanId, uint16_t aChannel);
+
+/**
+ * Sends a pending set for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSendPendingSet(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint32_t aDelayTimer, uint16_t aPanId, uint16_t aChannel, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName);
+
+/**
+ * Sends a active set for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSendActiveSet(otNode* aNode, uint64_t aActiveTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aExtPanId, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName, const char *aBinary);
+
+/**
+ * Sets the maximum number of children for a node
+ */
+OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildren);
+
+/**
+ * The interface used to listen in on virtual nodes' MAC frames
+ */
+
+typedef struct otListener otListener;
+
+/**
+ * Creates and starts a new listener
+ */
+OTNODEAPI otListener* OTCALL otListenerInit(uint32_t nodeid);
+
+/**
+ * Frees a listener
+ */
+OTNODEAPI int32_t OTCALL otListenerFinalize(otListener* aListener);
+
+/**
+ * Structure that represents a received MAC frame from the listener
+ */
+typedef struct otMacFrame
+{
+    uint8_t buffer[128];
+    uint8_t length;
+    uint32_t nodeid;
+} otMacFrame;
+
+/**
+ * Reads the next MAC frame from the listener
+ */
+OTNODEAPI int32_t OTCALL otListenerRead(otListener* aListener, otMacFrame *aFrame);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // OTNODE_H_
diff --git a/examples/drivers/windows/include/otOID.h b/examples/drivers/windows/include/otOID.h
new file mode 100644
index 0000000..348268d
--- /dev/null
+++ b/examples/drivers/windows/include/otOID.h
@@ -0,0 +1,236 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines the OID interface between otLwf and it's miniport.
+ */
+
+#ifndef __OTOID_H__
+#define __OTOID_H__
+
+#pragma once
+
+//
+// Macros for defining native OpenThread OIDs
+//
+
+#define OT_OPERATIONAL_OID      (0x01U)
+#define OT_STATISTICS_OID       (0x02U) 
+
+#define OT_MANDATORY_OID        (0x01U)
+#define OT_OPTIONAL_OID         (0x02U)
+
+#define OT_DEFINE_OID(Seq,o,m)  ((0xD0000000U) | ((o) << 16) | ((m) << 8) | (Seq))
+
+//
+// OpenThread Status Indication codes (and associated payload types)
+//
+
+#define NDIS_STATUS_OT_ENERGY_SCAN_RESULT           ((NDIS_STATUS)0x40050000L)
+    typedef struct _OT_ENERGY_SCAN_RESULT
+    {
+        NDIS_STATUS         Status;
+        CHAR                MaxRssi;
+    } OT_ENERGY_SCAN_RESULT, * POT_ENERGY_SCAN_RESULT;
+
+//
+// General OID Definitions
+//
+
+// Used to query initial constants of the miniport
+#define OID_OT_CAPABILITIES                         OT_DEFINE_OID(0, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef enum OT_MP_MODE
+    {
+        OT_MP_MODE_RADIO,  // Supports the physical radio layer
+        OT_MP_MODE_THREAD  // Supports the full Thread stack
+    } OT_MP_MODE;
+    typedef enum OT_RADIO_CAPABILITY
+    {
+        // Radio supports Ack timeouts internally
+        OT_RADIO_CAP_ACK_TIMEOUT                        = 1 << 0,
+        // Radio supports MAC retry logic and timers; as well as collision avoidance.
+        OT_RADIO_CAP_MAC_RETRY_AND_COLLISION_AVOIDANCE  = 1 << 1,
+        // Radio supports sleeping. If the device supports sleeping, it is assumed to
+        // default to the sleep state on bring up.
+        OT_RADIO_CAP_SLEEP                              = 1 << 2,
+    } OT_RADIO_CAPABILITY;
+    typedef struct _OT_CAPABILITIES
+    {
+        #define OT_CAPABILITIES_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        OT_MP_MODE         MiniportMode;
+        USHORT             RadioCapabilities;  // OT_RADIO_CAPABILITY flags
+    } OT_CAPABILITIES, * POT_CAPABILITIES;
+
+#define SIZEOF_OT_CAPABILITIES_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_CAPABILITIES, RadioCapabilities)
+
+//
+// Radio Mode OIDs
+//
+
+// Used to query/set sleep mode; only used if RadioCapabilities 
+// indicates support for OT_RADIO_CAP_SLEEP.
+#define OID_OT_SLEEP_MODE                           OT_DEFINE_OID(100, OT_OPERATIONAL_OID, OT_OPTIONAL_OID)
+    typedef struct _OT_SLEEP_MODE
+    {
+        #define OT_SLEEP_MODE_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        BOOLEAN            InSleepMode;
+    } OT_SLEEP_MODE, * POT_SLEEP_MODE;
+
+#define SIZEOF_OT_SLEEP_MODE_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_SLEEP_MODE, InSleepMode)
+
+// Used to query/set promiscuous mode
+#define OID_OT_PROMISCUOUS_MODE                     OT_DEFINE_OID(101, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_PROMISCUOUS_MODE
+    {
+        #define OT_PROMISCUOUS_MODE_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        BOOLEAN            InPromiscuousMode;
+    } OT_PROMISCUOUS_MODE, * POT_PROMISCUOUS_MODE;
+
+#define SIZEOF_OT_PROMISCUOUS_MODE_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_PROMISCUOUS_MODE, InPromiscuousMode)
+
+// Used to query the factory Extended Address
+#define OID_OT_FACTORY_EXTENDED_ADDRESS             OT_DEFINE_OID(102, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_FACTORY_EXTENDED_ADDRESS
+    {
+        #define OT_FACTORY_EXTENDED_ADDRESS_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        ULONGLONG          ExtendedAddress;
+    } OT_FACTORY_EXTENDED_ADDRESS, * POT_FACTORY_EXTENDED_ADDRESS;
+
+#define SIZEOF_OT_FACTORY_EXTENDED_ADDRESS_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_FACTORY_EXTENDED_ADDRESS, ExtendedAddress)
+
+// Used to query/set the Pan ID
+#define OID_OT_PAND_ID                              OT_DEFINE_OID(103, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_PAND_ID
+    {
+        #define OT_PAND_ID_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        USHORT             PanID;
+    } OT_PAND_ID, * POT_PAND_ID;
+
+#define SIZEOF_OT_PAND_ID_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_PAND_ID, PanID)
+
+// Used to query/set the Short Address
+#define OID_OT_SHORT_ADDRESS                        OT_DEFINE_OID(104, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_SHORT_ADDRESS
+    {
+        #define OT_SHORT_ADDRESS_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        USHORT             ShortAddress;
+    } OT_SHORT_ADDRESS, * POT_SHORT_ADDRESS;
+
+#define SIZEOF_OT_SHORT_ADDRESS_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_SHORT_ADDRESS, ShortAddress)
+
+// Used to query/set the Extended Address
+#define OID_OT_EXTENDED_ADDRESS                     OT_DEFINE_OID(105, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_EXTENDED_ADDRESS
+    {
+        #define OT_EXTENDED_ADDRESS_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        ULONGLONG          ExtendedAddress;
+    } OT_EXTENDED_ADDRESS, * POT_EXTENDED_ADDRESS;
+
+#define SIZEOF_OT_EXTENDED_ADDRESS_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_EXTENDED_ADDRESS, ExtendedAddress)
+
+// Used to query/set the current listening channel
+#define OID_OT_CURRENT_CHANNEL                      OT_DEFINE_OID(106, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_CURRENT_CHANNEL
+    {
+        #define OT_CURRENT_CHANNEL_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        UCHAR              Channel;
+    } OT_CURRENT_CHANNEL, * POT_CURRENT_CHANNEL;
+
+#define SIZEOF_OT_CURRENT_CHANNEL_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_CURRENT_CHANNEL, Channel)
+
+// Used to query the current RSSI for the current channel
+#define OID_OT_RSSI                                 OT_DEFINE_OID(107, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_RSSI
+    {
+        #define OT_RSSI_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        CHAR               Rssi;
+    } OT_RSSI, * POT_RSSI;
+
+#define SIZEOF_OT_RSSI_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_RSSI, Rssi)
+
+// The maximum of each type (short or extended) of MAC address to pend
+#define MAX_PENDING_MAC_SIZE    32
+
+// Used to set the list of MAC addresses for SEDs we currently have packets pending
+#define OID_OT_PENDING_MAC_OFFLOAD                  OT_DEFINE_OID(108, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_PENDING_MAC_OFFLOAD
+    {
+        #define OT_PENDING_MAC_OFFLOAD_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        UCHAR              ShortAddressCount;
+        UCHAR              ExtendedAddressCount;
+        // Dynamic array of USHORT ShortAddresses of count ShortAddressCount
+        // Dynamic array of ULONGLONG ExtendedAddresses of count ExtendedAddressCount
+    } OT_PENDING_MAC_OFFLOAD, * POT_PENDING_MAC_OFFLOAD;
+
+#define SIZEOF_OT_PENDING_MAC_OFFLOAD_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_PENDING_MAC_OFFLOAD, ExtendedAddressCount)
+
+#define COMPLETE_SIZEOF_OT_PENDING_MAC_OFFLOAD_REVISION_1(ShortAddressCount, ExtendedAddressCount) \
+    (SIZEOF_OT_PENDING_MAC_OFFLOAD_REVISION_1 + sizeof(USHORT) * ShortAddressCount + sizeof(ULONGLONG) * ExtendedAddressCount)
+
+// Used to issue an energy scan request for the given channel
+#define OID_OT_ENERGY_SCAN                          OT_DEFINE_OID(109, OT_OPERATIONAL_OID, OT_MANDATORY_OID)
+    typedef struct _OT_ENERGY_SCAN
+    {
+        #define OT_ENERGY_SCAN_REVISION_1 1
+        NDIS_OBJECT_HEADER Header;
+        UCHAR              Channel;
+        USHORT             DurationMs;
+    } OT_ENERGY_SCAN, * POT_ENERGY_SCAN;
+
+#define SIZEOF_OT_ENERGY_SCAN_REVISION_1 \
+    RTL_SIZEOF_THROUGH_FIELD(OT_ENERGY_SCAN, DurationMs)
+
+//
+// Thread Mode OIDs
+//
+
+// TODO ...
+
+#endif //__OTOID_H__
diff --git a/examples/drivers/windows/include/rtlrefcount.h b/examples/drivers/windows/include/rtlrefcount.h
new file mode 100644
index 0000000..4819bc5
--- /dev/null
+++ b/examples/drivers/windows/include/rtlrefcount.h
@@ -0,0 +1,441 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This module contains routines and type definitions for managing reference
+ *  counts.
+ *
+ *  N.B. The functions defined here use the minimum fencing required for correct
+ *       management of the reference count contract. No additional memory
+ *       ordering should be assumed.
+ */
+
+#pragma once
+
+//
+// Architecture support macros.
+// (Undefined at the bottom to avoid global namespace pollution)
+//
+
+#if defined(_WIN64)
+
+#define RtlIncrementLongPtrNoFence InterlockedIncrementNoFence64
+#define RtlDecrementLongPtrRelease InterlockedDecrementRelease64
+#define RtlExchangeAddLongPtrNoFence InterlockedExchangeAddNoFence64
+#define RtlExchangeAddLongPtrRelease InterlockedExchangeAddRelease64
+#define RtlCompareExchangeLongPtrNoFence InterlockedCompareExchangeNoFence64
+#define RtlCompareExchangeLongPtrRelease InterlockedCompareExchangeRelease64
+
+#else
+
+#define RtlIncrementLongPtrNoFence InterlockedIncrementNoFence
+#define RtlDecrementLongPtrRelease InterlockedDecrementRelease
+#define RtlExchangeAddLongPtrNoFence InterlockedExchangeAddNoFence
+#define RtlExchangeAddLongPtrRelease InterlockedExchangeAddRelease
+#define RtlCompareExchangeLongPtrNoFence InterlockedCompareExchangeNoFence
+#define RtlCompareExchangeLongPtrRelease InterlockedCompareExchangeRelease
+
+#endif
+
+#if defined(_X86_) || defined(_AMD64_)
+
+#define RtlBarrierAfterInterlock()
+
+#elif defined(_ARM64_)
+
+#define RtlBarrierAfterInterlock()  __dmb(_ARM64_BARRIER_ISH)
+
+#elif defined(_ARM_)
+
+#define RtlBarrierAfterInterlock()  __dmb(_ARM_BARRIER_ISH)
+
+#else
+
+#define Unsupported architecture.
+
+#endif
+
+#define RTL_REF_COUNT_INIT 1
+
+FORCEINLINE
+VOID
+RtlInitializeReferenceCount (
+    _Out_ PRTL_REFERENCE_COUNT RefCount
+    )
+
+/*++
+
+Routine Description:
+
+    This function initializes a reference count to 1.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count to initialize.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    *RefCount = RTL_REF_COUNT_INIT;
+    return;
+}
+
+FORCEINLINE
+VOID
+RtlInitializeReferenceCountEx (
+    _Out_ PRTL_REFERENCE_COUNT RefCount,
+    _In_ ULONG Bias
+    )
+
+/*++
+
+Routine Description:
+
+    This function initializes a reference count to a positive value.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count to initialize.
+
+    Bias - Supplies an initial reference count (must be positive).
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    *RefCount = Bias;
+    return;
+}
+
+FORCEINLINE
+VOID
+RtlIncrementReferenceCount (
+    _Inout_ PRTL_REFERENCE_COUNT RefCount
+    )
+
+/*++
+
+Routine Description:
+
+    This function increments the specified reference count, preventing object
+    deletion.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    if (RtlIncrementLongPtrNoFence(RefCount) > 1) {
+        return;
+    }
+
+    __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+}
+
+FORCEINLINE
+VOID
+RtlIncrementReferenceCountEx (
+    _Inout_ PRTL_REFERENCE_COUNT RefCount,
+    _In_ ULONG Bias
+    )
+
+/*++
+
+Routine Description:
+
+    This function increases the specified reference count by the specified bias,
+    preventing object deletion.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+    Bias - Supplies a reference bias amount.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    if (RtlExchangeAddLongPtrNoFence(RefCount, Bias) > 0) {
+        return;
+    }
+
+    __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+}
+
+FORCEINLINE
+BOOLEAN
+RtlIncrementReferenceCountNonZero (
+    _Inout_ volatile RTL_REFERENCE_COUNT *RefCount,
+    _In_ ULONG Bias
+    )
+
+/*++
+
+Routine Description:
+
+    This function increases the specified reference count by the specified bias,
+    unless the reference count was previously zero.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+    Bias - Supplies a reference bias amount.
+
+Return Value:
+
+    TRUE if the reference count was incremented, FALSE otherwise.
+
+--*/
+
+{
+
+    RTL_REFERENCE_COUNT NewValue;
+    RTL_REFERENCE_COUNT OldValue;
+
+    PrefetchForWrite(RefCount);
+    OldValue = ReadLongPtrNoFence(RefCount);
+    for (;;) {
+        NewValue = OldValue + Bias;
+        if ((ULONG_PTR)NewValue > Bias) {
+            NewValue = RtlCompareExchangeLongPtrNoFence(RefCount,
+                                                        NewValue,
+                                                        OldValue);
+
+            if (NewValue == OldValue) {
+                return TRUE;
+            }
+
+            OldValue = NewValue;
+
+        } else if ((ULONG_PTR)NewValue == Bias) {
+            return FALSE;
+
+        } else {
+            __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+        }
+    }
+}
+
+FORCEINLINE
+BOOLEAN
+RtlDecrementReferenceCount (
+    _Inout_ PRTL_REFERENCE_COUNT RefCount
+    )
+
+/*++
+
+Routine Description:
+
+    This function reduces the specified reference count, potentially triggering
+    the destruction of the guarded object.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+Return Value:
+
+    TRUE if the object should be destroyed, FALSE otherwise.
+
+--*/
+
+{
+
+    RTL_REFERENCE_COUNT NewValue;
+
+    //
+    // A release fence is required to ensure all guarded memory accesses are
+    // complete before any thread can begin destroying the object.
+    //
+
+    NewValue = RtlDecrementLongPtrRelease(RefCount);
+    if (NewValue > 0) {
+        return FALSE;
+
+    } else if (NewValue == 0) {
+
+        //
+        // An acquire fence is required before object destruction to ensure
+        // that the destructor cannot observe values changing on other threads.
+        //
+
+        RtlBarrierAfterInterlock();
+        return TRUE;
+    }
+
+    __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+    return FALSE;
+}
+
+FORCEINLINE
+BOOLEAN
+RtlDecrementReferenceCountEx (
+    _Inout_ PRTL_REFERENCE_COUNT RefCount,
+    _In_ ULONG Bias
+    )
+
+/*++
+
+Routine Description:
+
+    This function reduces the specified reference count by the specified amount,
+    potentially triggering the destruction of the guarded object.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+    Bias - Supplies a reference bias amount.
+
+Return Value:
+
+    TRUE if the object should be destroyed, FALSE otherwise.
+
+--*/
+
+{
+
+    RTL_REFERENCE_COUNT NewValue;
+
+    //
+    // A release fence is required to ensure all guarded memory accesses are
+    // complete before any thread can begin destroying the object.
+    //
+
+    NewValue = RtlExchangeAddLongPtrRelease(RefCount, -(LONG)Bias) - Bias;
+    if (NewValue > 0) {
+        return FALSE;
+
+    } else if (NewValue == 0) {
+
+        //
+        // An acquire fence is required before object destruction to ensure
+        // that the destructor cannot observe values changing on other threads.
+        //
+
+        RtlBarrierAfterInterlock();
+        return TRUE;
+    }
+
+    __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+    return FALSE;
+}
+
+FORCEINLINE
+BOOLEAN
+RtlDecrementReferenceCountNonZero (
+    _Inout_ volatile RTL_REFERENCE_COUNT *RefCount,
+    _In_ ULONG Bias
+    )
+
+/*++
+
+Routine Description:
+
+    This function reduces the specified reference count by the specified amount,
+    unless doing so would result in a zero value.
+
+Arguments:
+
+    RefCount - Supplies a pointer to a reference count.
+
+    Bias - Supplies a reference bias amount.
+
+Return Value:
+
+    TRUE if the reference count would be zero, FALSE otherwise.
+
+--*/
+
+{
+
+    RTL_REFERENCE_COUNT NewValue;
+    RTL_REFERENCE_COUNT OldValue;
+
+    PrefetchForWrite(RefCount);
+    OldValue = ReadLongPtrNoFence(RefCount);
+    for (;;) {
+        NewValue = OldValue - Bias;
+        if (NewValue > 0) {
+
+            //
+            // A release fence is required to ensure all guarded memory
+            // accesses are complete before any thread can begin destroying
+            // the object.
+            //
+
+            NewValue = RtlCompareExchangeLongPtrRelease(RefCount,
+                                                        NewValue,
+                                                        OldValue);
+
+            if (NewValue == OldValue) {
+                return FALSE;
+            }
+
+            OldValue = NewValue;
+
+        } else if (NewValue == 0) {
+            return TRUE;
+
+        } else {
+            __fastfail(FAST_FAIL_INVALID_REFERENCE_COUNT);
+        }
+    }
+}
+
+#undef RtlIncrementLongPtrNoFence
+#undef RtlDecrementLongPtrRelease
+#undef RtlExchangeAddLongPtrNoFence
+#undef RtlExchangeAddLongPtrRelease
+#undef RtlCompareExchangeLongPtrNoFence
+#undef RtlCompareExchangeLongPtrRelease
+#undef RtlBarrierAfterInterlock
diff --git a/examples/drivers/windows/include_c99/stdbool.h b/examples/drivers/windows/include_c99/stdbool.h
new file mode 100644
index 0000000..e28121c
--- /dev/null
+++ b/examples/drivers/windows/include_c99/stdbool.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDBOOL_H_
+#define _STDBOOL_H_
+
+#ifndef __cplusplus
+
+typedef _Bool bool;
+#define false 0
+#define true 1
+
+#endif // __cplusplus
+
+#endif  // _STDBOOL_H_
diff --git a/examples/drivers/windows/include_c99/stdint.h b/examples/drivers/windows/include_c99/stdint.h
new file mode 100644
index 0000000..8e2dac8
--- /dev/null
+++ b/examples/drivers/windows/include_c99/stdint.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDINT_H_
+#define _STDINT_H_
+
+typedef signed char        int8_t;
+typedef short              int16_t;
+typedef int                int32_t;
+typedef long long          int64_t;
+typedef unsigned char      uint8_t;
+typedef unsigned short     uint16_t;
+typedef unsigned int       uint32_t;
+typedef unsigned long long uint64_t;
+
+#endif  // _STDINT_H_
diff --git a/examples/drivers/windows/otApi/dllmain.cpp b/examples/drivers/windows/otApi/dllmain.cpp
new file mode 100644
index 0000000..63d2326
--- /dev/null
+++ b/examples/drivers/windows/otApi/dllmain.cpp
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "dllmain.tmh"
+
+BOOL 
+__stdcall 
+DllMain(
+    HINSTANCE hinstDll, 
+    DWORD dwReason, 
+    LPVOID /* lpvReserved */
+    )
+{
+    switch (dwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hinstDll);
+        WPP_INIT_TRACING(L"otApi");
+        break;
+
+    case DLL_PROCESS_DETACH:
+        WPP_CLEANUP();
+        break;
+
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+        break;
+    }
+
+    return TRUE;
+}
+
diff --git a/examples/drivers/windows/otApi/otApi.cpp b/examples/drivers/windows/otApi/otApi.cpp
new file mode 100644
index 0000000..f3bdc0e
--- /dev/null
+++ b/examples/drivers/windows/otApi/otApi.cpp
@@ -0,0 +1,3869 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "otApi.tmh"
+
+//#define DEBUG_ASYNC_IO
+
+using namespace std;
+
+// The maximum time we will wait for an overlapped result. Essentially, the maximum
+// amount of time each synchronous IOCTL should take.
+const DWORD c_MaxOverlappedWaitTimeMS = 5 * 1000;
+
+// Version string returned by the API
+const char c_Version[] = "Windows"; // TODO - What should we really put here?
+
+template<class CallbackT>
+class otCallback
+{
+public:
+    RTL_REFERENCE_COUNT CallbackRefCount;
+    HANDLE              CallbackCompleteEvent;
+    GUID                InterfaceGuid;
+    CallbackT           Callback;
+    PVOID               CallbackContext;
+
+    otCallback(
+        CallbackT _Callback,
+        PVOID _CallbackContext
+        ) : 
+        CallbackRefCount(1),
+        CallbackCompleteEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
+        Callback(_Callback),
+        CallbackContext(_CallbackContext)
+    {
+    }
+
+    otCallback(
+        const GUID& _InterfaceGuid,
+        CallbackT _Callback,
+        PVOID _CallbackContext
+        ) : 
+        CallbackRefCount(1),
+        CallbackCompleteEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
+        InterfaceGuid(_InterfaceGuid),
+        Callback(_Callback),
+        CallbackContext(_CallbackContext)
+    {
+    }
+
+    ~otCallback()
+    {
+        if (CallbackCompleteEvent) CloseHandle(CallbackCompleteEvent);
+    }
+
+    void AddRef()
+    {
+        RtlIncrementReferenceCount(&CallbackRefCount);
+    }
+
+    void Release(bool waitForShutdown = false)
+    {
+        if (RtlDecrementReferenceCount(&CallbackRefCount))
+        {
+            // Set completion event if there are no more refs
+            SetEvent(CallbackCompleteEvent);
+        }
+
+        if (waitForShutdown)
+        {
+            WaitForSingleObject(CallbackCompleteEvent, INFINITE);
+        }
+    }
+};
+
+typedef otCallback<otDeviceAvailabilityChangedCallback> otApiDeviceAvailabilityCallback;
+typedef otCallback<otHandleActiveScanResult> otApiActiveScanCallback;
+typedef otCallback<otHandleEnergyScanResult> otApiEnergyScanCallback;
+typedef otCallback<otStateChangedCallback> otApiStateChangeCallback;
+typedef otCallback<otCommissionerEnergyReportCallback> otApiCommissionerEnergyReportCallback;
+typedef otCallback<otCommissionerPanIdConflictCallback> otApiCommissionerPanIdConflictCallback;
+typedef otCallback<otJoinerCallback> otApiJoinerCallback;
+
+typedef struct otApiInstance
+{
+    // Handle to the driver
+    HANDLE                      DeviceHandle;
+
+    // Async IO variables
+    OVERLAPPED                  Overlapped;
+    PTP_WAIT                    ThreadpoolWait;
+
+    // Notification variables
+    CRITICAL_SECTION            CallbackLock;
+    OTLWF_NOTIFICATION          NotificationBuffer;
+
+    // Callbacks
+    otApiDeviceAvailabilityCallback*    DeviceAvailabilityCallbacks;
+    vector<otApiActiveScanCallback*>    ActiveScanCallbacks;
+    vector<otApiEnergyScanCallback*>    EnergyScanCallbacks;
+    vector<otApiActiveScanCallback*>    DiscoverCallbacks;
+    vector<otApiStateChangeCallback*>   StateChangedCallbacks;
+    vector<otApiCommissionerEnergyReportCallback*>  CommissionerEnergyReportCallbacks;
+    vector<otApiCommissionerPanIdConflictCallback*> CommissionerPanIdConflictCallbacks;
+    vector<otApiJoinerCallback*>        JoinerCallbacks;
+
+    // Constructor
+    otApiInstance() : 
+        DeviceHandle(INVALID_HANDLE_VALUE),
+        Overlapped({0}),
+        ThreadpoolWait(nullptr),
+        DeviceAvailabilityCallbacks(nullptr)
+    { 
+        InitializeCriticalSection(&CallbackLock);
+    }
+
+    ~otApiInstance()
+    {
+        DeleteCriticalSection(&CallbackLock);
+    }
+
+    // Helper function to set a callback
+    template<class CallbackT>
+    bool 
+    SetCallback(
+        vector<otCallback<CallbackT>*> &Callbacks, 
+        const GUID& InterfaceGuid,
+        CallbackT Callback,
+        PVOID CallbackContext
+        )
+    {
+        bool alreadyExists = false;
+        otCallback<CallbackT>* CallbackToRelease = nullptr;
+
+        EnterCriticalSection(&CallbackLock);
+
+        if (Callback == nullptr)
+        {
+            for (size_t i = 0; i < Callbacks.size(); i++)
+            {
+                if (Callbacks[i]->InterfaceGuid == InterfaceGuid)
+                {
+                    CallbackToRelease = Callbacks[i];
+                    Callbacks.erase(Callbacks.begin() + i);
+                    break;
+                }
+            }
+        }
+        else
+        {
+            for (size_t i = 0; i < Callbacks.size(); i++)
+            {
+                if (Callbacks[i]->InterfaceGuid == InterfaceGuid)
+                {
+                    alreadyExists = true;
+                    break;
+                }
+            }
+
+            if (!alreadyExists)
+            {
+                Callbacks.push_back(new otCallback<CallbackT>(InterfaceGuid, Callback, CallbackContext));
+            }
+        }
+
+        LeaveCriticalSection(&CallbackLock);
+
+        if (CallbackToRelease)
+        {
+            CallbackToRelease->Release(true);
+            delete CallbackToRelease;
+        }
+
+        return !alreadyExists;
+    }
+
+} otApiInstance;
+
+typedef struct otInstance
+{
+    otApiInstance   *ApiHandle;      // Pointer to the Api handle
+    NET_IFINDEX      InterfaceIndex; // Interface Index
+    NET_LUID         InterfaceLuid;  // Interface Luid
+    GUID             InterfaceGuid;  // Interface guid
+    ULONG            CompartmentID;  // Interface Compartment ID
+
+} otInstance;
+
+// otpool wait callback for async IO completion
+VOID CALLBACK 
+otIoComplete(
+    _Inout_     PTP_CALLBACK_INSTANCE Instance,
+    _Inout_opt_ PVOID                 Context,
+    _Inout_     PTP_WAIT              Wait,
+    _In_        TP_WAIT_RESULT        WaitResult
+    );
+
+OTAPI 
+otApiInstance *
+OTCALL
+otApiInit(
+    )
+{
+    DWORD dwError = ERROR_SUCCESS;
+    otApiInstance *aApitInstance = nullptr;
+    
+    LogFuncEntry(API_DEFAULT);
+
+    aApitInstance = new(std::nothrow)otApiInstance();
+    if (aApitInstance == nullptr)
+    {
+        dwError = GetLastError();
+        LogWarning(API_DEFAULT, "Failed to allocate otApiInstance");
+        goto error;
+    }
+
+    // Open the pipe to the OpenThread driver
+    aApitInstance->DeviceHandle = 
+        CreateFile(
+            OTLWF_IOCLT_PATH,
+            GENERIC_READ | GENERIC_WRITE,
+            0,
+            nullptr,                // no SECURITY_ATTRIBUTES structure
+            OPEN_EXISTING,          // No special create flags
+            FILE_FLAG_OVERLAPPED,   // Allow asynchronous requests
+            nullptr
+            );
+    if (aApitInstance->DeviceHandle == INVALID_HANDLE_VALUE)
+    {
+        dwError = GetLastError();
+        LogError(API_DEFAULT, "CreateFile failed, %!WINERROR!", dwError);
+        goto error;
+    }
+
+    // Create event for completion of async IO
+    aApitInstance->Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    if (aApitInstance->Overlapped.hEvent == nullptr)
+    {
+        dwError = GetLastError();
+        LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
+        goto error;
+    }
+
+    // Create the otpool wait
+    aApitInstance->ThreadpoolWait = 
+        CreateThreadpoolWait(
+            otIoComplete,
+            aApitInstance,
+            nullptr
+            );
+    if (aApitInstance->ThreadpoolWait == nullptr)
+    {
+        dwError = GetLastError();
+        LogError(API_DEFAULT, "CreateThreadpoolWait failed, %!WINERROR!", dwError);
+        goto error;
+    }
+
+    // Start the otpool waiting on the overlapped event
+    SetThreadpoolWait(aApitInstance->ThreadpoolWait, aApitInstance->Overlapped.hEvent, nullptr);
+
+#ifdef DEBUG_ASYNC_IO
+    LogVerbose(API_DEFAULT, "Querying for 1st notification");
+#endif
+
+    // Request first notification asynchronously
+    if (!DeviceIoControl(
+            aApitInstance->DeviceHandle,
+            IOCTL_OTLWF_QUERY_NOTIFICATION,
+            nullptr, 0,
+            &aApitInstance->NotificationBuffer, sizeof(OTLWF_NOTIFICATION),
+            nullptr, 
+            &aApitInstance->Overlapped))
+    {
+        dwError = GetLastError();
+        if (dwError != ERROR_IO_PENDING)
+        {
+            LogError(API_DEFAULT, "DeviceIoControl for first notification failed, %!WINERROR!", dwError);
+            goto error;
+        }
+        dwError = ERROR_SUCCESS;
+    }
+
+error:
+
+    if (dwError != ERROR_SUCCESS)
+    {
+        otApiFinalize(aApitInstance);
+        aApitInstance = nullptr;
+    }
+    
+    LogFuncExit(API_DEFAULT);
+
+    return aApitInstance;
+}
+
+OTAPI 
+void 
+OTCALL
+otApiFinalize(
+    _In_ otApiInstance *aApitInstance
+)
+{
+    if (aApitInstance == nullptr) return;
+    
+    LogFuncEntry(API_DEFAULT);
+
+    // If we never got the handle, nothing left to clean up
+    if (aApitInstance->DeviceHandle != INVALID_HANDLE_VALUE)
+    {
+        //
+        // Make sure we unregister callbacks
+        //
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        otApiDeviceAvailabilityCallback* DeviceAvailabilityCallbacks = aApitInstance->DeviceAvailabilityCallbacks;
+        aApitInstance->DeviceAvailabilityCallbacks = nullptr;
+
+        vector<otApiActiveScanCallback*> ActiveScanCallbacks(aApitInstance->ActiveScanCallbacks);
+        aApitInstance->ActiveScanCallbacks.clear();
+
+        vector<otApiEnergyScanCallback*> EnergyScanCallbacks(aApitInstance->EnergyScanCallbacks);
+        aApitInstance->EnergyScanCallbacks.clear();
+
+        vector<otApiActiveScanCallback*> DiscoverCallbacks(aApitInstance->DiscoverCallbacks);
+        aApitInstance->DiscoverCallbacks.clear();
+
+        vector<otApiStateChangeCallback*> StateChangedCallbacks(aApitInstance->StateChangedCallbacks);
+        aApitInstance->StateChangedCallbacks.clear();
+
+        vector<otApiCommissionerEnergyReportCallback*> CommissionerEnergyReportCallbacks(aApitInstance->CommissionerEnergyReportCallbacks);
+        aApitInstance->CommissionerEnergyReportCallbacks.clear();
+
+        vector<otApiCommissionerPanIdConflictCallback*> CommissionerPanIdConflictCallbacks(aApitInstance->CommissionerPanIdConflictCallbacks);
+        aApitInstance->CommissionerPanIdConflictCallbacks.clear();
+
+        vector<otApiJoinerCallback*> JoinerCallbacks(aApitInstance->JoinerCallbacks);
+        aApitInstance->JoinerCallbacks.clear();
+
+        #ifdef DEBUG_ASYNC_IO
+        LogVerbose(API_DEFAULT, "Clearing Threadpool Wait");
+        #endif
+
+        // Clear the threadpool wait to prevent further waits from being scheduled
+        PTP_WAIT tpWait = aApitInstance->ThreadpoolWait;
+        aApitInstance->ThreadpoolWait = nullptr;
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+
+        // Clear all callbacks
+        if (DeviceAvailabilityCallbacks)
+        {
+            DeviceAvailabilityCallbacks->Release(true);
+            delete DeviceAvailabilityCallbacks;
+        }
+        for (size_t i = 0; i < ActiveScanCallbacks.size(); i++)
+        {
+            ActiveScanCallbacks[i]->Release(true);
+            delete ActiveScanCallbacks[i];
+        }
+        for (size_t i = 0; i < EnergyScanCallbacks.size(); i++)
+        {
+            EnergyScanCallbacks[i]->Release(true);
+            delete EnergyScanCallbacks[i];
+        }
+        for (size_t i = 0; i < DiscoverCallbacks.size(); i++)
+        {
+            DiscoverCallbacks[i]->Release(true);
+            delete DiscoverCallbacks[i];
+        }
+        for (size_t i = 0; i < StateChangedCallbacks.size(); i++)
+        {
+            StateChangedCallbacks[i]->Release(true);
+            delete StateChangedCallbacks[i];
+        }
+        for (size_t i = 0; i < CommissionerEnergyReportCallbacks.size(); i++)
+        {
+            CommissionerEnergyReportCallbacks[i]->Release(true);
+            delete CommissionerEnergyReportCallbacks[i];
+        }
+        for (size_t i = 0; i < CommissionerPanIdConflictCallbacks.size(); i++)
+        {
+            CommissionerPanIdConflictCallbacks[i]->Release(true);
+            delete CommissionerPanIdConflictCallbacks[i];
+        }
+        for (size_t i = 0; i < JoinerCallbacks.size(); i++)
+        {
+            JoinerCallbacks[i]->Release(true);
+            delete JoinerCallbacks[i];
+        }
+        
+        // Clean up threadpool wait
+        if (tpWait)
+        {
+            #ifdef DEBUG_ASYNC_IO
+            LogVerbose(API_DEFAULT, "Waiting for outstanding threadpool callbacks to compelte");
+            #endif
+
+            // Cancel any queued waits and wait for any outstanding calls to compelte
+            WaitForThreadpoolWaitCallbacks(tpWait, TRUE);
+        
+            #ifdef DEBUG_ASYNC_IO
+            LogVerbose(API_DEFAULT, "Cancelling any pending IO");
+            #endif
+
+            // Cancel any async IO
+            CancelIoEx(aApitInstance->DeviceHandle, &aApitInstance->Overlapped);
+
+            // Free the threadpool wait
+            CloseThreadpoolWait(tpWait);
+        }
+
+        // Clean up overlapped event
+        if (aApitInstance->Overlapped.hEvent)
+        {
+            CloseHandle(aApitInstance->Overlapped.hEvent);
+        }
+    
+        // Close the device handle
+        CloseHandle(aApitInstance->DeviceHandle);
+    }
+
+    delete aApitInstance;
+    
+    LogFuncExit(API_DEFAULT);
+}
+
+OTAPI 
+void 
+OTCALL
+otFreeMemory(
+    _In_ const void *mem
+    )
+{
+    free((void*)mem);
+}
+
+// Handles cleanly invoking the register callback
+VOID
+ProcessNotification(
+    _In_ otApiInstance         *aApitInstance,
+    _In_ POTLWF_NOTIFICATION    Notif
+    )
+{
+    if (Notif->NotifType == OTLWF_NOTIF_DEVICE_AVAILABILITY)
+    {
+        otCallback<otDeviceAvailabilityChangedCallback>* Callback = nullptr;
+        
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        if (aApitInstance->DeviceAvailabilityCallbacks != nullptr)
+        {
+            aApitInstance->DeviceAvailabilityCallbacks->AddRef();
+            Callback = aApitInstance->DeviceAvailabilityCallbacks;
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->DeviceAvailabilityPayload.Available != FALSE, 
+                &Notif->InterfaceGuid, 
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_STATE_CHANGE)
+    {
+        otCallback<otStateChangedCallback>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->StateChangedCallbacks.size(); i++)
+        {
+            if (aApitInstance->StateChangedCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->StateChangedCallbacks[i]->AddRef();
+                Callback = aApitInstance->StateChangedCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->StateChangePayload.Flags, 
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_DISCOVER)
+    {
+        otCallback<otHandleActiveScanResult>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->DiscoverCallbacks.size(); i++)
+        {
+            if (aApitInstance->DiscoverCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->DiscoverCallbacks[i]->AddRef();
+                Callback = aApitInstance->DiscoverCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->DiscoverPayload.Valid ? &Notif->DiscoverPayload.Results : nullptr, 
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_ACTIVE_SCAN)
+    {
+        otCallback<otHandleActiveScanResult>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->ActiveScanCallbacks.size(); i++)
+        {
+            if (aApitInstance->ActiveScanCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->ActiveScanCallbacks[i]->AddRef();
+                Callback = aApitInstance->ActiveScanCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->ActiveScanPayload.Valid ? &Notif->ActiveScanPayload.Results : nullptr, 
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_ENERGY_SCAN)
+    {
+        otCallback<otHandleEnergyScanResult>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->EnergyScanCallbacks.size(); i++)
+        {
+            if (aApitInstance->EnergyScanCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->EnergyScanCallbacks[i]->AddRef();
+                Callback = aApitInstance->EnergyScanCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->EnergyScanPayload.Valid ? &Notif->EnergyScanPayload.Results : nullptr, 
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT)
+    {
+        otCallback<otCommissionerEnergyReportCallback>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->CommissionerEnergyReportCallbacks.size(); i++)
+        {
+            if (aApitInstance->CommissionerEnergyReportCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->CommissionerEnergyReportCallbacks[i]->AddRef();
+                Callback = aApitInstance->CommissionerEnergyReportCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->CommissionerEnergyReportPayload.ChannelMask,
+                Notif->CommissionerEnergyReportPayload.EnergyList,
+                Notif->CommissionerEnergyReportPayload.EnergyListLength,
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_COMMISSIONER_PANID_QUERY)
+    {
+        otCallback<otCommissionerPanIdConflictCallback>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->CommissionerPanIdConflictCallbacks.size(); i++)
+        {
+            if (aApitInstance->CommissionerPanIdConflictCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->CommissionerPanIdConflictCallbacks[i]->AddRef();
+                Callback = aApitInstance->CommissionerPanIdConflictCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            Callback->Callback(
+                Notif->CommissionerPanIdQueryPayload.PanId,
+                Notif->CommissionerPanIdQueryPayload.ChannelMask,
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else if (Notif->NotifType == OTLWF_NOTIF_JOINER_COMPLETE)
+    {
+        otCallback<otJoinerCallback>* Callback = nullptr;
+
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+
+        for (size_t i = 0; i < aApitInstance->JoinerCallbacks.size(); i++)
+        {
+            if (aApitInstance->JoinerCallbacks[i]->InterfaceGuid == Notif->InterfaceGuid)
+            {
+                aApitInstance->JoinerCallbacks[i]->AddRef();
+                Callback = aApitInstance->JoinerCallbacks[i];
+                break;
+            }
+        }
+
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+        
+        // Invoke the callback outside the lock and release ref when done
+        if (Callback)
+        {
+            aApitInstance->SetCallback(
+                aApitInstance->JoinerCallbacks,
+                Notif->InterfaceGuid, (otJoinerCallback)nullptr, (PVOID)nullptr
+                );
+
+            Callback->Callback(
+                Notif->JoinerCompletePayload.Error,
+                Callback->CallbackContext);
+
+            Callback->Release();
+        }
+    }
+    else
+    {
+        // Unexpected notif type
+    }
+}
+
+// Threadpool wait callback for async IO completion
+VOID CALLBACK 
+otIoComplete(
+    _Inout_     PTP_CALLBACK_INSTANCE /* Instance */,
+    _Inout_opt_ PVOID                 Context,
+    _Inout_     PTP_WAIT              /* Wait */,
+    _In_        TP_WAIT_RESULT        /* WaitResult */
+    )
+{
+#ifdef DEBUG_ASYNC_IO
+    LogFuncEntry(API_DEFAULT);
+#endif
+
+    otApiInstance *aApitInstance = (otApiInstance*)Context;
+    if (aApitInstance == nullptr) return;
+
+    // Get the result of the IO operation
+    DWORD dwBytesTransferred = 0;
+    if (!GetOverlappedResult(
+            aApitInstance->DeviceHandle,
+            &aApitInstance->Overlapped,
+            &dwBytesTransferred,
+            FALSE))
+    {
+        DWORD dwError = GetLastError();
+        LogError(API_DEFAULT, "GetOverlappedResult for notification failed, %!WINERROR!", dwError);
+    }
+    else
+    {
+        LogVerbose(API_DEFAULT, "Received successful callback for notification, type=%d", 
+                     aApitInstance->NotificationBuffer.NotifType);
+
+        // Invoke the callback if set
+        ProcessNotification(aApitInstance, &aApitInstance->NotificationBuffer);
+            
+        // Try to get the threadpool wait to see if we are allowed to continue processing notifications
+        EnterCriticalSection(&aApitInstance->CallbackLock);
+        PTP_WAIT tpWait = aApitInstance->ThreadpoolWait;
+        LeaveCriticalSection(&aApitInstance->CallbackLock);
+
+        if (tpWait)
+        {
+            // Start waiting for next notification
+            SetThreadpoolWait(tpWait, aApitInstance->Overlapped.hEvent, nullptr);
+            
+#ifdef DEBUG_ASYNC_IO
+            LogVerbose(API_DEFAULT, "Querying for next notification");
+#endif
+
+            // Request next notification
+            if (!DeviceIoControl(
+                    aApitInstance->DeviceHandle,
+                    IOCTL_OTLWF_QUERY_NOTIFICATION,
+                    nullptr, 0,
+                    &aApitInstance->NotificationBuffer, sizeof(OTLWF_NOTIFICATION),
+                    nullptr, 
+                    &aApitInstance->Overlapped))
+            {
+                DWORD dwError = GetLastError();
+                if (dwError != ERROR_IO_PENDING)
+                {
+                    LogError(API_DEFAULT, "DeviceIoControl for new notification failed, %!WINERROR!", dwError);
+                }
+            }
+        }
+    }
+    
+#ifdef DEBUG_ASYNC_IO
+    LogFuncExit(API_DEFAULT);
+#endif
+}
+
+DWORD
+SendIOCTL(
+    _In_ otApiInstance *aApitInstance,
+    _In_ DWORD dwIoControlCode,
+    _In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer,
+    _In_ DWORD nInBufferSize,
+    _Out_writes_bytes_opt_(nOutBufferSize) LPVOID lpOutBuffer,
+    _In_ DWORD nOutBufferSize
+    )
+{
+    DWORD dwError = ERROR_SUCCESS;
+    OVERLAPPED Overlapped = { 0 };
+    DWORD dwBytesReturned = 0;
+    
+    Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    if (Overlapped.hEvent == nullptr)
+    {
+        dwError = GetLastError();
+        LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
+        goto error;
+    }
+    
+    // Send the IOCTL the OpenThread driver
+    if (!DeviceIoControl(
+            aApitInstance->DeviceHandle,
+            dwIoControlCode,
+            lpInBuffer, nInBufferSize,
+            lpOutBuffer, nOutBufferSize,
+            nullptr, 
+            &Overlapped))
+    {
+        dwError = GetLastError();
+        if (dwError != ERROR_IO_PENDING)
+        {
+            LogError(API_DEFAULT, "DeviceIoControl(0x%x) failed, %!WINERROR!", dwIoControlCode, dwError);
+            goto error;
+        }
+        dwError = ERROR_SUCCESS;
+    }
+
+    // Get the result of the IO operation
+    if (!GetOverlappedResultEx(
+            aApitInstance->DeviceHandle,
+            &Overlapped,
+            &dwBytesReturned,
+            c_MaxOverlappedWaitTimeMS,
+            FALSE
+            ))
+    {
+        dwError = GetLastError();
+        if (dwError == WAIT_TIMEOUT)
+        {
+            dwError = ERROR_TIMEOUT;
+            CancelIoEx(aApitInstance->DeviceHandle, &Overlapped);
+        }
+        LogError(API_DEFAULT, "GetOverlappedResult failed, %!WINERROR!", dwError);
+        goto error;
+    }
+
+    if (dwBytesReturned != nOutBufferSize)
+    {
+        dwError = ERROR_INVALID_DATA;
+        LogError(API_DEFAULT, "GetOverlappedResult returned invalid output size, expected=%u actual=%u", 
+                     nOutBufferSize, dwBytesReturned);
+        goto error;
+    }
+
+error:
+
+    if (Overlapped.hEvent)
+    {
+        CloseHandle(Overlapped.hEvent);
+    }
+
+    return dwError;
+}
+
+__pragma(pack(push,1))
+template <class T1, class T2>
+struct PackedBuffer2
+{
+    T1 data1; T2 data2;
+    PackedBuffer2(const T1 &d1, const T2 &d2) : data1(d1), data2(d2) { }
+};
+template <class T1, class T2, class T3>
+struct PackedBuffer3
+{
+    T1 data1; T2 data2; T3 data3;
+    PackedBuffer3(const T1 &d1, const T2 &d2, const T3 &d3) : data1(d1), data2(d2), data3(d3) { }
+};
+template <class T1, class T2, class T3, class T4>
+struct PackedBuffer4
+{
+    T1 data1; T2 data2; T3 data3; T4 data4;
+    PackedBuffer4(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4) : data1(d1), data2(d2), data3(d3), data4(d4) { }
+};
+template <class T1, class T2, class T3, class T4, class T5>
+struct PackedBuffer5
+{
+    T1 data1; T2 data2; T3 data3; T4 data4; T5 data5;
+    PackedBuffer5(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4, const T5 &d5) : data1(d1), data2(d2), data3(d3), data4(d4), data5(d5) { }
+};
+template <class T1, class T2, class T3, class T4, class T5, class T6>
+struct PackedBuffer6
+{
+    T1 data1; T2 data2; T3 data3; T4 data4; T5 data5; T6 data6;
+    PackedBuffer6(const T1 &d1, const T2 &d2, const T3 &d3, const T4 &d4, const T5 &d5, const T6 &d6) : data1(d1), data2(d2), data3(d3), data4(d4), data5(d5), data6(d6) { }
+};
+ __pragma(pack(pop))
+
+template <class in, class out>
+DWORD
+QueryIOCTL(
+    _In_ otInstance *aInstance,
+    _In_ DWORD dwIoControlCode,
+    _In_ const in *input,
+    _Out_ out* output
+    )
+{
+    PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, *input);
+    return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), output, sizeof(out));
+}
+
+template <class out>
+DWORD
+QueryIOCTL(
+    _In_ otInstance *aInstance,
+    _In_ DWORD dwIoControlCode,
+    _Out_ out* output
+    )
+{
+    return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &aInstance->InterfaceGuid, sizeof(GUID), output, sizeof(out));
+}
+
+template <class in>
+DWORD
+SetIOCTL(
+    _In_ otInstance *aInstance,
+    _In_ DWORD dwIoControlCode,
+    _In_ const in* input
+    )
+{
+    PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, *input);
+    return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), nullptr, 0);
+}
+
+template <class in>
+DWORD
+SetIOCTL(
+    _In_ otInstance *aInstance,
+    _In_ DWORD dwIoControlCode,
+    _In_ const in input
+    )
+{
+    PackedBuffer2<GUID,in> Buffer(aInstance->InterfaceGuid, input);
+    return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &Buffer, sizeof(Buffer), nullptr, 0);
+}
+
+DWORD
+SetIOCTL(
+    _In_ otInstance *aInstance,
+    _In_ DWORD dwIoControlCode
+    )
+{
+    return SendIOCTL(aInstance->ApiHandle, dwIoControlCode, &aInstance->InterfaceGuid, sizeof(GUID), nullptr, 0);
+}
+
+otError
+DwordToThreadError(
+    DWORD dwError
+    )
+{
+    if (((int)dwError) > 0)
+    {
+        return OT_ERROR_GENERIC;
+    }
+    else
+    {
+        return (otError)(-(int)dwError);
+    }
+}
+
+OTAPI 
+void 
+OTCALL
+otSetDeviceAvailabilityChangedCallback(
+    _In_ otApiInstance *aApitInstance,
+    _In_ otDeviceAvailabilityChangedCallback aCallback,
+    _In_ void *aCallbackContext
+    )
+{
+    otApiDeviceAvailabilityCallback* CallbackToRelease = nullptr;
+
+    EnterCriticalSection(&aApitInstance->CallbackLock);
+
+    if (aApitInstance->DeviceAvailabilityCallbacks != nullptr)
+    {
+        CallbackToRelease = aApitInstance->DeviceAvailabilityCallbacks;
+        aApitInstance->DeviceAvailabilityCallbacks = nullptr;
+    }
+
+    if (aCallback != nullptr)
+    {
+        aApitInstance->DeviceAvailabilityCallbacks = 
+            new otApiDeviceAvailabilityCallback(aCallback, aCallbackContext);
+    }
+    
+    LeaveCriticalSection(&aApitInstance->CallbackLock);
+
+    if (CallbackToRelease)
+    {
+        CallbackToRelease->Release(true);
+        delete CallbackToRelease;
+    }
+}
+
+OTAPI 
+otDeviceList* 
+OTCALL
+otEnumerateDevices(
+    _In_ otApiInstance *aApitInstance
+    )
+{
+    DWORD dwError = ERROR_SUCCESS;
+    OVERLAPPED Overlapped = { 0 };
+    DWORD dwBytesReturned = 0;
+    otDeviceList* pDeviceList = nullptr;
+    DWORD cbDeviceList = sizeof(otDeviceList);
+    
+    LogFuncEntry(API_DEFAULT);
+
+    Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    if (Overlapped.hEvent == nullptr)
+    {
+        dwError = GetLastError();
+        LogError(API_DEFAULT, "CreateEvent (Overlapped.hEvent) failed, %!WINERROR!", dwError);
+        goto error;
+    }
+    
+    pDeviceList = (otDeviceList*)malloc(cbDeviceList);
+    if (pDeviceList == nullptr)
+    {
+        LogWarning(API_DEFAULT, "Failed to allocate otDeviceList of %u bytes.", cbDeviceList);
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto error;
+    }
+    RtlZeroMemory(pDeviceList, cbDeviceList);
+    
+    // Query in a loop to account for it changing between calls
+    while (true)
+    {
+        // Send the IOCTL to query the interfaces
+        if (!DeviceIoControl(
+                aApitInstance->DeviceHandle,
+                IOCTL_OTLWF_ENUMERATE_DEVICES,
+                nullptr, 0,
+                pDeviceList, cbDeviceList,
+                nullptr, 
+                &Overlapped))
+        {
+            dwError = GetLastError();
+            if (dwError != ERROR_IO_PENDING)
+            {
+                LogError(API_DEFAULT, "DeviceIoControl(IOCTL_OTLWF_ENUMERATE_DEVICES) failed, %!WINERROR!", dwError);
+                goto error;
+            }
+            dwError = ERROR_SUCCESS;
+        }
+
+        // Get the result of the IO operation
+        if (!GetOverlappedResultEx(
+                aApitInstance->DeviceHandle,
+                &Overlapped,
+                &dwBytesReturned,
+                c_MaxOverlappedWaitTimeMS,
+                TRUE))
+        {
+            dwError = GetLastError();
+            if (dwError == WAIT_TIMEOUT)
+            {
+                dwError = ERROR_TIMEOUT;
+                CancelIoEx(aApitInstance->DeviceHandle, &Overlapped);
+            }
+            LogError(API_DEFAULT, "GetOverlappedResult for notification failed, %!WINERROR!", dwError);
+            goto error;
+        }
+        
+        // Calculate the expected size of the full buffer
+        cbDeviceList = 
+            FIELD_OFFSET(otDeviceList, aDevices) +
+            pDeviceList->aDevicesLength * sizeof(otDeviceList::aDevices);
+        
+        // Make sure they returned a complete buffer
+        if (dwBytesReturned != sizeof(otDeviceList::aDevicesLength)) break;
+        
+        // If we get here that means we didn't have a big enough buffer
+        // Reallocate a new buffer
+        free(pDeviceList);
+        pDeviceList = (otDeviceList*)malloc(cbDeviceList);
+        if (pDeviceList == nullptr)
+        {
+            LogError(API_DEFAULT, "Failed to allocate otDeviceList of %u bytes.", cbDeviceList);
+            dwError = ERROR_NOT_ENOUGH_MEMORY;
+            goto error;
+        }
+        RtlZeroMemory(pDeviceList, cbDeviceList);
+    }
+
+error:
+
+    if (dwError != ERROR_SUCCESS)
+    {
+        free(pDeviceList);
+        pDeviceList = nullptr;
+    }
+
+    if (Overlapped.hEvent)
+    {
+        CloseHandle(Overlapped.hEvent);
+    }
+    
+    LogFuncExitMsg(API_DEFAULT, "%d devices", pDeviceList == nullptr ? -1 : (int)pDeviceList->aDevicesLength);
+
+    return pDeviceList;
+}
+    
+OTAPI 
+otInstance *
+OTCALL
+otInstanceInit(
+    _In_ otApiInstance *aApitInstance, 
+    _In_ const GUID *aDeviceGuid
+    )
+{
+    otInstance *aInstance = nullptr;
+
+    OTLWF_DEVICE Result = {0};
+    if (aApitInstance &&
+        SendIOCTL(
+            aApitInstance, 
+            IOCTL_OTLWF_QUERY_DEVICE, 
+            (LPVOID)aDeviceGuid, 
+            sizeof(GUID), 
+            &Result, 
+            sizeof(Result)
+            ) == ERROR_SUCCESS)
+    {
+        aInstance = (otInstance*)malloc(sizeof(otInstance));
+        if (aInstance)
+        {
+            aInstance->ApiHandle = aApitInstance;
+            aInstance->InterfaceGuid = *aDeviceGuid;
+            aInstance->CompartmentID = Result.CompartmentID;
+
+            if (ConvertInterfaceGuidToLuid(aDeviceGuid, &aInstance->InterfaceLuid) != ERROR_SUCCESS ||
+                ConvertInterfaceLuidToIndex(&aInstance->InterfaceLuid, &aInstance->InterfaceIndex) != ERROR_SUCCESS)
+            {
+                LogError(API_DEFAULT, "Failed to convert interface guid to index!");
+                free(aInstance);
+                aInstance = nullptr;
+            }
+        }
+    }
+
+    return aInstance;
+}
+
+OTAPI 
+GUID 
+OTCALL
+otGetDeviceGuid(
+    otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return {};
+    return aInstance->InterfaceGuid;
+}
+
+OTAPI 
+uint32_t 
+OTCALL
+otGetDeviceIfIndex(
+    otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return (uint32_t)-1;
+    return aInstance->InterfaceIndex;
+}
+
+OTAPI 
+uint32_t 
+OTCALL
+otGetCompartmentId(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return (uint32_t)-1;
+    return aInstance->CompartmentID;
+}
+
+OTAPI 
+const char *
+OTCALL
+otGetVersionString()
+{
+    char* szVersion = (char*)malloc(sizeof(c_Version));
+    if (szVersion)
+    {
+        memcpy_s(szVersion, sizeof(c_Version), c_Version, sizeof(c_Version));
+    }
+    return szVersion;
+}
+
+OTAPI 
+otError 
+OTCALL
+otIp6SetEnabled(
+    _In_ otInstance *aInstance,
+    bool aEnabled
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_INTERFACE, (BOOLEAN)aEnabled));
+}
+
+OTAPI 
+bool 
+OTCALL
+otIp6IsEnabled(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_INTERFACE, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadSetEnabled(
+    _In_ otInstance *aInstance,
+    bool aEnabled
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD, (BOOLEAN)aEnabled));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetAutoStart(
+    _In_ otInstance *aInstance,
+    bool aStartAutomatically
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD_AUTO_START, (BOOLEAN)(aStartAutomatically ? TRUE : FALSE)));
+}
+
+OTAPI
+bool
+OTCALL
+otThreadGetAutoStart(
+    otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_THREAD_AUTO_START, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+bool 
+OTCALL
+otThreadIsSingleton(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_SINGLETON, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError 
+OTCALL
+otLinkActiveScan(
+    _In_ otInstance *aInstance, 
+    uint32_t aScanChannels, 
+    uint16_t aScanDuration,
+    otHandleActiveScanResult aCallback,
+    _In_ void *aCallbackContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->ActiveScanCallbacks,
+        aInstance->InterfaceGuid, aCallback, aCallbackContext
+        );
+    
+    PackedBuffer3<GUID,uint32_t,uint16_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aScanDuration);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ACTIVE_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+bool 
+OTCALL
+otLinkIsActiveScanInProgress(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_SCAN, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError 
+OTCALL 
+otLinkEnergyScan(
+    _In_ otInstance *aInstance, 
+    uint32_t aScanChannels, 
+    uint16_t aScanDuration,
+    _In_ otHandleEnergyScanResult aCallback, 
+    _In_ void *aCallbackContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->EnergyScanCallbacks,
+        aInstance->InterfaceGuid, aCallback, aCallbackContext
+        );
+    
+    PackedBuffer3<GUID,uint32_t,uint16_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aScanDuration);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ENERGY_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+bool 
+OTCALL 
+otLinkIsEnergyScanInProgress(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ENERGY_SCAN, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadDiscover(
+    _In_ otInstance *aInstance, 
+    uint32_t aScanChannels, 
+    uint16_t aPanid,
+    bool aJoiner,
+    bool aEnableEui64Filtering,
+    otHandleActiveScanResult aCallback,
+    void *aCallbackContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->DiscoverCallbacks,
+        aInstance->InterfaceGuid, aCallback, aCallbackContext
+        );
+
+    PackedBuffer5<GUID,uint32_t,uint16_t, uint8_t, uint8_t> Buffer(aInstance->InterfaceGuid, aScanChannels, aPanid, aJoiner, aEnableEui64Filtering);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_DISCOVER, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+bool 
+OTCALL
+otIsDiscoverInProgress(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = FALSE;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_DISCOVER, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError
+OTCALL
+otLinkSendDataRequest(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    UNREFERENCED_PARAMETER(aInstance);
+    return OT_ERROR_NOT_IMPLEMENTED; // TODO
+}
+
+OTAPI 
+uint8_t 
+OTCALL
+otLinkGetChannel(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHANNEL, &Result);
+    return Result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otLinkSetChannel(
+    _In_ otInstance *aInstance, 
+    uint8_t aChannel
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_CHANNEL, aChannel));
+}
+
+OTAPI
+otError
+OTCALL
+otDatasetSetDelayTimerMinimal(
+    _In_ otInstance *aInstance,
+    uint32_t aDelayTimerMinimal
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    // TODO
+    UNREFERENCED_PARAMETER(aDelayTimerMinimal);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+OTAPI
+uint32_t
+OTCALL
+otDatasetGetDelayTimerMinimal(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return 0;
+    // TODO
+    return 0;
+}
+
+OTAPI 
+uint8_t 
+OTCALL
+otThreadGetMaxAllowedChildren(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_CHILDREN, &Result);
+    return Result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadSetMaxAllowedChildren(
+    _In_ otInstance *aInstance, 
+    uint8_t aMaxChildren
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_CHILDREN, aMaxChildren));
+}
+
+OTAPI 
+uint32_t 
+OTCALL
+otThreadGetChildTimeout(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_TIMEOUT, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otThreadSetChildTimeout(
+    _In_ otInstance *aInstance, 
+    uint32_t aTimeout
+    )
+{
+    if (aInstance == nullptr) return;
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_TIMEOUT, aTimeout);
+}
+
+OTAPI 
+const 
+uint8_t *
+OTCALL
+otLinkGetExtendedAddress(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otExtAddress *Result = (otExtAddress*)malloc(sizeof(otExtAddress));
+    if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_ADDRESS, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        Result = nullptr;
+    }
+    return (uint8_t*)Result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otLinkSetExtendedAddress(
+    _In_ otInstance *aInstance, 
+    const otExtAddress *aExtendedAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_ADDRESS, aExtendedAddress));
+}
+
+OTAPI 
+const uint8_t *
+OTCALL
+otThreadGetExtendedPanId(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otExtendedPanId *Result = (otExtendedPanId*)malloc(sizeof(otExtendedPanId));
+    if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_PANID, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        Result = nullptr;
+    }
+    return (uint8_t*)Result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadSetExtendedPanId(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtendedPanId
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_EXTENDED_PANID, (const otExtendedPanId*)aExtendedPanId));
+}
+
+OTAPI 
+void 
+OTCALL
+otLinkGetFactoryAssignedIeeeEui64(
+    _In_ otInstance *aInstance, 
+    _Out_ otExtAddress *aEui64
+)
+{
+    if (aInstance == nullptr) return;
+    (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_FACTORY_EUI64, aEui64);
+}
+
+OTAPI 
+void 
+OTCALL
+otLinkGetJoinerId(
+    _In_ otInstance *aInstance, 
+    _Out_ otExtAddress *aHashMacAddress
+    )
+{
+    if (aInstance == nullptr) return;
+    (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_HASH_MAC_ADDRESS, aHashMacAddress);
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadGetLeaderRloc(
+    _In_ otInstance *aInstance, 
+    _Out_ otIp6Address *aLeaderRloc
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_RLOC, aLeaderRloc));
+}
+
+OTAPI 
+otLinkModeConfig 
+OTCALL
+otThreadGetLinkMode(
+    _In_ otInstance *aInstance
+    )
+{
+    otLinkModeConfig Result = {0};
+    static_assert(sizeof(otLinkModeConfig) == 4, "The size of otLinkModeConfig should be 4 bytes");
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LINK_MODE, &Result);
+    return Result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadSetLinkMode(
+    _In_ otInstance *aInstance, 
+    otLinkModeConfig aConfig
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    static_assert(sizeof(otLinkModeConfig) == 4, "The size of otLinkModeConfig should be 4 bytes");
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_LINK_MODE, aConfig));
+}
+
+OTAPI 
+const otMasterKey *
+OTCALL
+otThreadGetMasterKey(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otMasterKey *Result = (otMasterKey*)malloc(sizeof(otMasterKey));
+    if (Result == nullptr) return nullptr;
+    if (QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MASTER_KEY, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        return nullptr;
+    }
+    return Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetMasterKey(
+    _In_ otInstance *aInstance, 
+    const otMasterKey *aKey
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MASTER_KEY, aKey));
+}
+
+OTAPI 
+const uint8_t *
+OTCALL
+otThreadGetPSKc(
+    _In_ otInstance *aInstance 
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    uint8_t *Result = (uint8_t*)malloc(sizeof(otPSKc));
+    if (Result == nullptr) return nullptr;
+    if (QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PSKC, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        return nullptr;
+    }
+    return Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetPSKc(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aPSKc 
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    
+    BYTE Buffer[sizeof(GUID) + sizeof(otPSKc)];
+    memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), aPSKc, sizeof(otPSKc));
+    
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_PSKC, Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+int8_t 
+OTCALL
+otLinkGetMaxTransmitPower(
+    _In_ otInstance *aInstance
+    )
+{
+    int8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otLinkSetMaxTransmitPower(
+    _In_ otInstance *aInstance, 
+    int8_t aPower
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER, aPower);
+}
+
+OTAPI
+const otIp6Address *
+OTCALL
+otThreadGetMeshLocalEid(
+    _In_ otInstance *aInstance
+    )
+{
+    otIp6Address *Result = (otIp6Address*)malloc(sizeof(otIp6Address));
+    if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_EID, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        Result = nullptr;
+    }
+    return Result;
+}
+
+OTAPI
+const uint8_t *
+OTCALL
+otThreadGetMeshLocalPrefix(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otMeshLocalPrefix *Result = (otMeshLocalPrefix*)malloc(sizeof(otMeshLocalPrefix));
+    if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        Result = nullptr;
+    }
+    return (uint8_t*)Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetMeshLocalPrefix(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aMeshLocalPrefix
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX, (const otMeshLocalPrefix*)aMeshLocalPrefix));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetNetworkDataLeader(
+    _In_ otInstance *aInstance, 
+    bool aStable, 
+    _Out_ uint8_t *aData, 
+    _Out_ uint8_t *aDataLength
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    UNREFERENCED_PARAMETER(aInstance);
+    UNREFERENCED_PARAMETER(aStable);
+    UNREFERENCED_PARAMETER(aData);
+    UNREFERENCED_PARAMETER(aDataLength);
+    return OT_ERROR_NOT_IMPLEMENTED; // TODO
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetNetworkDataLocal(
+    _In_ otInstance *aInstance, 
+    bool aStable, 
+    _Out_ uint8_t *aData, 
+    _Out_ uint8_t *aDataLength
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    UNREFERENCED_PARAMETER(aInstance);
+    UNREFERENCED_PARAMETER(aStable);
+    UNREFERENCED_PARAMETER(aData);
+    UNREFERENCED_PARAMETER(aDataLength);
+    return OT_ERROR_NOT_IMPLEMENTED; // TODO
+}
+
+OTAPI
+const char *
+OTCALL
+otThreadGetNetworkName(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otNetworkName *Result = (otNetworkName*)malloc(sizeof(otNetworkName));
+    if (Result && QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_NAME, Result) != ERROR_SUCCESS)
+    {
+        free(Result);
+        Result = nullptr;
+    }
+    return (char*)Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetNetworkName(
+    _In_ otInstance *aInstance, 
+    _In_ const char *aNetworkName
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    otNetworkName Buffer = {0};
+    strcpy_s(Buffer.m8, sizeof(Buffer), aNetworkName);
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_NAME, (const otNetworkName*)&Buffer));
+}
+
+OTAPI 
+otError 
+OTCALL
+otNetDataGetNextOnMeshPrefix(
+    _In_ otInstance *aInstance, 
+    _Inout_ otNetworkDataIterator *aIterator,
+    _Out_ otBorderRouterConfig *aConfig
+    )
+{
+    if (aInstance == nullptr || aConfig == nullptr) return OT_ERROR_INVALID_ARGS;
+    
+    BOOLEAN aLocal = FALSE;
+    PackedBuffer3<GUID,BOOLEAN,otNetworkDataIterator> InBuffer(aInstance->InterfaceGuid, aLocal, *aIterator);
+    BYTE OutBuffer[sizeof(uint8_t) + sizeof(otBorderRouterConfig)];
+
+    otError aError =
+        DwordToThreadError(
+            SendIOCTL(
+                aInstance->ApiHandle,
+                IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX,
+                &InBuffer, sizeof(InBuffer),
+                OutBuffer, sizeof(OutBuffer)));
+
+    if (aError == OT_ERROR_NONE)
+    {
+        memcpy(aIterator, OutBuffer, sizeof(uint8_t));
+        memcpy(aConfig, OutBuffer + sizeof(uint8_t), sizeof(otBorderRouterConfig));
+    }
+    else
+    {
+        ZeroMemory(aConfig, sizeof(otBorderRouterConfig));
+    }
+
+    return aError;
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterGetNextOnMeshPrefix(
+    _In_ otInstance *aInstance,
+    _Inout_ otNetworkDataIterator *aIterator,
+    _Out_ otBorderRouterConfig *aConfig
+    )
+{
+    if (aInstance == nullptr || aConfig == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    BOOLEAN aLocal = TRUE;
+    PackedBuffer3<GUID,BOOLEAN,otNetworkDataIterator> InBuffer(aInstance->InterfaceGuid, aLocal, *aIterator);
+    BYTE OutBuffer[sizeof(uint8_t) + sizeof(otBorderRouterConfig)];
+
+    otError aError = 
+        DwordToThreadError(
+            SendIOCTL(
+                aInstance->ApiHandle, 
+                IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX, 
+                &InBuffer, sizeof(InBuffer), 
+                OutBuffer, sizeof(OutBuffer)));
+
+    if (aError == OT_ERROR_NONE)
+    {
+        memcpy(aIterator, OutBuffer, sizeof(uint8_t));
+        memcpy(aConfig, OutBuffer + sizeof(uint8_t), sizeof(otBorderRouterConfig));
+    }
+    else
+    {
+        ZeroMemory(aConfig, sizeof(otBorderRouterConfig));
+    }
+
+    return aError;
+}
+
+OTAPI
+otPanId 
+OTCALL
+otLinkGetPanId(
+    _In_ otInstance *aInstance
+    )
+{
+    otPanId Result = {0};
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, &Result);
+    return Result;
+}
+
+OTAPI
+otError
+OTCALL
+otLinkSetPanId(
+    _In_ otInstance *aInstance, 
+    otPanId aPanId
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, aPanId));
+}
+
+OTAPI
+bool 
+OTCALL
+otThreadIsRouterRoleEnabled(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = {0};
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED, &Result);
+    return Result != FALSE;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetRouterRoleEnabled(
+    _In_ otInstance *aInstance, 
+    bool aEnabled
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED, (BOOLEAN)aEnabled);
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetPreferredRouterId(
+    _In_ otInstance *aInstance,
+    uint8_t aRouterId
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PAN_ID, aRouterId));
+}
+
+OTAPI
+otShortAddress 
+OTCALL
+otLinkGetShortAddress(
+    _In_ otInstance *aInstance
+    )
+{
+    otShortAddress Result = {0};
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_SHORT_ADDRESS, &Result);
+    return Result;
+}
+
+BOOL
+GetAdapterAddresses(
+    PIP_ADAPTER_ADDRESSES * ppIAA
+)
+{
+    PIP_ADAPTER_ADDRESSES pIAA = NULL;
+    DWORD len = 0;
+    DWORD flags;
+
+    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
+    if (GetAdaptersAddresses(AF_INET6, flags, NULL, NULL, &len) != ERROR_BUFFER_OVERFLOW)
+        return FALSE;
+
+    pIAA = (PIP_ADAPTER_ADDRESSES)malloc(len);
+    if (pIAA) {
+        GetAdaptersAddresses(AF_INET6, flags, NULL, pIAA, &len);
+        *ppIAA = pIAA;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+OTAPI
+const otNetifAddress *
+OTCALL
+otIp6GetUnicastAddresses(
+    _In_ otInstance *aInstance
+    )
+{
+    LogFuncEntry(API_DEFAULT);
+    if (aInstance == nullptr)
+    {
+        LogFuncExit(API_DEFAULT);
+        return nullptr;
+    }
+
+    // Put the current thead in the correct compartment
+    bool RevertCompartmentOnExit = false;
+    ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
+    if (OriginalCompartmentID != aInstance->CompartmentID)
+    {
+        DWORD dwError = ERROR_SUCCESS;
+        if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
+        {
+            LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
+            return nullptr;
+        }
+        RevertCompartmentOnExit = true;
+    }
+
+    otNetifAddress *addrs = nullptr;
+    ULONG AddrCount = 0;
+
+    // Query the current adapter addresses and format them in the proper output format
+    PIP_ADAPTER_ADDRESSES pIAAList;
+    if (GetAdapterAddresses(&pIAAList))
+    {
+        // Loop through all the interfaces
+        for (auto pIAA = pIAAList; pIAA != nullptr; pIAA = pIAA->Next) 
+        {
+            // Look for the right interface
+            if (pIAA->Ipv6IfIndex != aInstance->InterfaceIndex) continue;
+
+            // Look through all unicast addresses
+            for (auto pUnicastAddr = pIAA->FirstUnicastAddress; 
+                 pUnicastAddr != nullptr; 
+                 pUnicastAddr = pUnicastAddr->Next)
+            {
+                AddrCount++;
+            }
+
+            break;
+        }
+
+        // If we didn't find any addresses, just break out
+        if (AddrCount == 0) goto error;
+
+        // Allocate the addresses
+        addrs = (otNetifAddress*)malloc(AddrCount * sizeof(otNetifAddress));
+        if (addrs == nullptr)
+        {
+            LogWarning(API_DEFAULT, "Not enough memory to alloc otNetifAddress array");
+            goto error;
+        }
+        ZeroMemory(addrs, AddrCount * sizeof(otNetifAddress));
+
+        // Initialize the next pointers
+        for (ULONG i = 0; i < AddrCount; i++)
+        {
+            addrs[i].mNext = (i + 1 == AddrCount) ? nullptr : &addrs[i + 1];
+        }
+
+        AddrCount = 0;
+
+        // Loop through all the interfaces
+        for (auto pIAA = pIAAList; pIAA != nullptr; pIAA = pIAA->Next) 
+        {
+            // Look for the right interface
+            if (pIAA->Ipv6IfIndex != aInstance->InterfaceIndex) continue;
+
+            // Look through all unicast addresses
+            for (auto pUnicastAddr = pIAA->FirstUnicastAddress; 
+                 pUnicastAddr != nullptr; 
+                 pUnicastAddr = pUnicastAddr->Next)
+            {
+                LPSOCKADDR_IN6 pAddr = (LPSOCKADDR_IN6)pUnicastAddr->Address.lpSockaddr;
+
+                // Copy the necessary parameters
+                memcpy(&addrs[AddrCount].mAddress, &pAddr->sin6_addr, sizeof(pAddr->sin6_addr));
+                addrs[AddrCount].mPreferred = pUnicastAddr->PreferredLifetime != 0;
+                addrs[AddrCount].mValid = pUnicastAddr->ValidLifetime != 0;
+                addrs[AddrCount].mPrefixLength = pUnicastAddr->OnLinkPrefixLength;
+
+                AddrCount++;
+            }
+
+            break;
+        }
+
+    error:
+        free(pIAAList);
+    }
+    else
+    {
+        LogError(API_DEFAULT, "GetAdapterAddresses failed!");
+    }
+
+    // Revert the comparment if necessary
+    if (RevertCompartmentOnExit)
+    {
+        (VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
+    }
+    
+    LogFuncExitMsg(API_DEFAULT, "%d addrs", AddrCount);
+    return addrs;
+}
+
+OTAPI
+otError
+OTCALL
+otIp6AddUnicastAddress(
+    _In_ otInstance *aInstance, 
+    const otNetifAddress *aAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    // Put the current thead in the correct compartment
+    bool RevertCompartmentOnExit = false;
+    ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
+    if (OriginalCompartmentID != aInstance->CompartmentID)
+    {
+        DWORD dwError = ERROR_SUCCESS;
+        if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
+        {
+            LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
+            return OT_ERROR_FAILED;
+        }
+        RevertCompartmentOnExit = true;
+    }
+
+    MIB_UNICASTIPADDRESS_ROW newRow;
+    InitializeUnicastIpAddressEntry(&newRow);
+
+    newRow.InterfaceIndex = aInstance->InterfaceIndex;
+    newRow.InterfaceLuid = aInstance->InterfaceLuid;
+    newRow.Address.si_family = AF_INET6;
+    newRow.Address.Ipv6.sin6_family = AF_INET6;
+        
+    static_assert(sizeof(IN6_ADDR) == sizeof(otIp6Address), "Windows and OpenThread IPv6 Addr Structs must be same size");
+
+    memcpy(&newRow.Address.Ipv6.sin6_addr, &aAddress->mAddress, sizeof(IN6_ADDR));
+    newRow.OnLinkPrefixLength = aAddress->mPrefixLength;
+    newRow.PreferredLifetime = aAddress->mPreferred ? 0xffffffff : 0;
+    newRow.ValidLifetime = aAddress->mValid ? 0xffffffff : 0;
+    newRow.PrefixOrigin = IpPrefixOriginOther;  // Derived from network XPANID
+    newRow.SkipAsSource = FALSE;                // Allow automatic binding to this address (default)
+
+    if (IN6_IS_ADDR_LINKLOCAL(&newRow.Address.Ipv6.sin6_addr))
+    {
+        newRow.SuffixOrigin = IpSuffixOriginLinkLayerAddress;   // Derived from Extended MAC address
+    }
+    else
+    {
+        newRow.SuffixOrigin = IpSuffixOriginRandom;             // Was created randomly
+    }
+
+    DWORD dwError = CreateUnicastIpAddressEntry(&newRow);
+
+    // Revert the comparment if necessary
+    if (RevertCompartmentOnExit)
+    {
+        (VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
+    }
+
+    if (dwError != ERROR_SUCCESS)
+    {
+        LogError(API_DEFAULT, "CreateUnicastIpAddressEntry failed %!WINERROR!", dwError);
+        return OT_ERROR_FAILED;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+OTAPI
+otError
+OTCALL
+otIp6RemoveUnicastAddress(
+    _In_ otInstance *aInstance, 
+    const otIp6Address *aAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    // Put the current thead in the correct compartment
+    bool RevertCompartmentOnExit = false;
+    ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
+    if (OriginalCompartmentID != aInstance->CompartmentID)
+    {
+        DWORD dwError = ERROR_SUCCESS;
+        if ((dwError = SetCurrentThreadCompartmentId(aInstance->CompartmentID)) != ERROR_SUCCESS)
+        {
+            LogError(API_DEFAULT, "SetCurrentThreadCompartmentId failed, %!WINERROR!", dwError);
+            return OT_ERROR_FAILED;
+        }
+        RevertCompartmentOnExit = true;
+    }
+
+    MIB_UNICASTIPADDRESS_ROW deleteRow;
+    InitializeUnicastIpAddressEntry(&deleteRow);
+
+    deleteRow.InterfaceIndex = aInstance->InterfaceIndex;
+    deleteRow.InterfaceLuid = aInstance->InterfaceLuid;
+    deleteRow.Address.si_family = AF_INET6;
+
+    memcpy(&deleteRow.Address.Ipv6.sin6_addr, aAddress, sizeof(IN6_ADDR));
+    
+    DWORD dwError = DeleteUnicastIpAddressEntry(&deleteRow);
+
+    // Revert the comparment if necessary
+    if (RevertCompartmentOnExit)
+    {
+        (VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
+    }
+
+    if (dwError != ERROR_SUCCESS)
+    {
+        LogError(API_DEFAULT, "DeleteUnicastIpAddressEntry failed %!WINERROR!", dwError);
+        return OT_ERROR_FAILED;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+OTAPI
+otError 
+OTCALL
+otSetStateChangedCallback(
+    _In_ otInstance *aInstance, 
+    _In_ otStateChangedCallback aCallback, 
+    _In_ void *aContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    bool success = 
+        aInstance->ApiHandle->SetCallback(
+            aInstance->ApiHandle->StateChangedCallbacks,
+            aInstance->InterfaceGuid, aCallback, aContext
+            );
+    return success ? OT_ERROR_NONE : OT_ERROR_ALREADY;
+}
+
+OTAPI
+void
+OTCALL
+otRemoveStateChangeCallback(
+    _In_ otInstance *aInstance,
+    _In_ otStateChangedCallback /* aCallback */,
+    _In_ void *aContext
+    )
+{
+    if (aInstance == nullptr) return;
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->StateChangedCallbacks,
+        aInstance->InterfaceGuid, (otStateChangedCallback)nullptr, aContext
+        );
+}
+
+OTAPI
+otError
+OTCALL
+otDatasetGetActive(
+    _In_ otInstance *aInstance, 
+    _Out_ otOperationalDataset *aDataset
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_DATASET, aDataset));
+}
+
+OTAPI
+otError
+OTCALL
+otDatasetSetActive(
+    _In_ otInstance *aInstance, 
+    const otOperationalDataset *aDataset
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ACTIVE_DATASET, aDataset));
+}
+
+OTAPI
+otError
+OTCALL
+otDatasetGetPending(
+    _In_ otInstance *aInstance, 
+    _Out_ otOperationalDataset *aDataset
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PENDING_DATASET, aDataset));
+}
+
+OTAPI
+otError
+OTCALL
+otDatasetSetPending(
+    _In_ otInstance *aInstance, 
+    const otOperationalDataset *aDataset
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PENDING_DATASET, aDataset));
+}
+
+OTAPI 
+otError 
+OTCALL
+otDatasetSendMgmtActiveGet(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aTlvTypes, 
+    uint8_t aLength,
+    _In_opt_ const otIp6Address *aAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvTypes == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
+    if (aAddress) BufferSize += sizeof(otIp6Address);
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvTypes, aLength);
+    if (aAddress)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + aLength, BufferSize - sizeof(GUID) - sizeof(uint8_t) - aLength, aAddress, sizeof(otIp6Address));
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_ACTIVE_GET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otDatasetSendMgmtActiveSet(
+    _In_ otInstance *aInstance, 
+    const otOperationalDataset *aDataset, 
+    const uint8_t *aTlvs,
+    uint8_t aLength
+    )
+{
+    if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otOperationalDataset));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset) - sizeof(uint8_t), aTlvs, aLength);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_ACTIVE_SET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otDatasetSendMgmtPendingGet(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aTlvTypes, 
+    uint8_t aLength,
+    _In_opt_ const otIp6Address *aAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvTypes == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
+    if (aAddress) BufferSize += sizeof(otIp6Address);
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvTypes, aLength);
+    if (aAddress)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + aLength, BufferSize - sizeof(GUID) - sizeof(uint8_t) - aLength, aAddress, sizeof(otIp6Address));
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_PENDING_GET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otDatasetSendMgmtPendingSet(
+    _In_ otInstance *aInstance, 
+    const otOperationalDataset *aDataset, 
+    const uint8_t *aTlvs,
+    uint8_t aLength
+    )
+{
+    if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otOperationalDataset));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(otOperationalDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otOperationalDataset) - sizeof(uint8_t), aTlvs, aLength);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_PENDING_SET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+uint32_t 
+OTCALL
+otLinkGetPollPeriod(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_POLL_PERIOD, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otLinkSetPollPeriod(
+    _In_ otInstance *aInstance, 
+    uint32_t aPollPeriod
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_POLL_PERIOD, aPollPeriod);
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otThreadGetLocalLeaderWeight(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetLocalLeaderWeight(
+    _In_ otInstance *aInstance, 
+    uint8_t aWeight
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT, aWeight);
+}
+
+OTAPI 
+uint32_t 
+OTCALL
+otThreadGetLocalLeaderPartitionId(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otThreadSetLocalLeaderPartitionId(
+    _In_ otInstance *aInstance, 
+    uint32_t aPartitionId
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID, aPartitionId);
+}
+
+OTAPI 
+uint16_t 
+OTCALL 
+otThreadGetJoinerUdpPort(
+    _In_ otInstance *aInstance
+)
+{
+    uint16_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_UDP_PORT, &Result);
+    return Result;
+}
+
+OTAPI 
+otError 
+OTCALL 
+otThreadSetJoinerUdpPort(
+    _In_ otInstance *aInstance, 
+    uint16_t aJoinerUdpPort
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_UDP_PORT, aJoinerUdpPort));
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterAddOnMeshPrefix(
+    _In_ otInstance *aInstance, 
+    const otBorderRouterConfig *aConfig
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_BORDER_ROUTER, aConfig));
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterRemoveOnMeshPrefix(
+    _In_ otInstance *aInstance, 
+    const otIp6Prefix *aPrefix
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_BORDER_ROUTER, aPrefix));
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterAddRoute(
+    _In_ otInstance *aInstance, 
+    const otExternalRouteConfig *aConfig
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_EXTERNAL_ROUTE, aConfig));
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterRemoveRoute(
+    _In_ otInstance *aInstance, 
+    const otIp6Prefix *aPrefix
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_EXTERNAL_ROUTE, aPrefix));
+}
+
+OTAPI
+otError
+OTCALL
+otBorderRouterRegister(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_SEND_SERVER_DATA));
+}
+
+OTAPI
+uint32_t 
+OTCALL
+otThreadGetContextIdReuseDelay(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetContextIdReuseDelay(
+    _In_ otInstance *aInstance, 
+    uint32_t aDelay
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY, aDelay);
+}
+
+OTAPI
+uint32_t 
+OTCALL
+otThreadGetKeySequenceCounter(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetKeySequenceCounter(
+    _In_ otInstance *aInstance, 
+    uint32_t aKeySequenceCounter
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER, aKeySequenceCounter);
+}
+
+OTAPI
+uint32_t 
+OTCALL
+otThreadGetKeySwitchGuardTime(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetKeySwitchGuardTime(
+    _In_ otInstance *aInstance, 
+    uint32_t aKeySwitchGuardTime
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME, aKeySwitchGuardTime);
+}
+
+OTAPI
+uint8_t
+OTCALL
+otThreadGetNetworkIdTimeout(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetNetworkIdTimeout(
+    _In_ otInstance *aInstance, 
+    uint8_t aTimeout
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT, aTimeout);
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otThreadGetRouterUpgradeThreshold(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD, &Result);
+    return Result;
+}
+
+OTAPI
+void 
+OTCALL
+otThreadSetRouterUpgradeThreshold(
+    _In_ otInstance *aInstance, 
+    uint8_t aThreshold
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD, aThreshold);
+}
+
+OTAPI 
+uint8_t 
+OTCALL
+otThreadGetRouterDowngradeThreshold(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otThreadSetRouterDowngradeThreshold(
+    _In_ otInstance *aInstance, 
+    uint8_t aThreshold
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD, aThreshold);
+}
+
+OTAPI 
+uint8_t 
+OTCALL
+otThreadGetRouterSelectionJitter(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER, &Result);
+    return Result;
+}
+
+OTAPI 
+void 
+OTCALL
+otThreadSetRouterSelectionJitter(
+    _In_ otInstance *aInstance, 
+    uint8_t aRouterJitter
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER, aRouterJitter);
+}
+
+OTAPI
+otError
+OTCALL
+otThreadReleaseRouterId(
+    _In_ otInstance *aInstance, 
+    uint8_t aRouterId
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_RELEASE_ROUTER_ID, aRouterId));
+}
+
+OTAPI
+otError
+OTCALL
+otLinkAddWhitelist(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_MAC_WHITELIST, (const otExtAddress*)aExtAddr));
+}
+
+OTAPI
+otError
+OTCALL
+otLinkAddWhitelistRssi(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr, 
+    int8_t aRssi
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    
+    PackedBuffer3<GUID,otExtAddress,int8_t> Buffer(aInstance->InterfaceGuid, *(otExtAddress*)aExtAddr, aRssi);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ADD_MAC_WHITELIST, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI
+void 
+OTCALL
+otLinkRemoveWhitelist(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_MAC_WHITELIST, (const otExtAddress*)aExtAddr);
+}
+
+OTAPI
+otError
+OTCALL
+otLinkGetWhitelistEntry(
+    _In_ otInstance *aInstance, 
+    uint8_t aIndex, 
+    _Out_ otMacWhitelistEntry *aEntry
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENTRY, &aIndex, aEntry));
+}
+
+OTAPI
+void 
+OTCALL
+otLinkClearWhitelist(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CLEAR_MAC_WHITELIST);
+}
+
+OTAPI
+void 
+OTCALL
+otLinkSetWhitelistEnabled(
+    _In_ otInstance *aInstance,
+    bool aEnabled
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED, (BOOLEAN)aEnabled);
+}
+
+OTAPI
+bool 
+OTCALL
+otLinkIsWhitelistEnabled(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED, &Result);
+    return Result != FALSE;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadBecomeDetached(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_DETACHED));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadBecomeChild(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_CHILD));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadBecomeRouter(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_ROUTER));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadBecomeLeader(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, (uint8_t)OT_DEVICE_ROLE_LEADER));
+}
+
+OTAPI
+otError
+OTCALL
+otLinkAddBlacklist(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_ADD_MAC_BLACKLIST, (const otExtAddress*)aExtAddr));
+}
+
+OTAPI
+void 
+OTCALL
+otLinkRemoveBlacklist(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_REMOVE_MAC_BLACKLIST, (const otExtAddress*)aExtAddr);
+}
+
+OTAPI
+otError
+OTCALL
+otLinkGetBlacklistEntry(
+    _In_ otInstance *aInstance, 
+    uint8_t aIndex, 
+    _Out_ otMacBlacklistEntry *aEntry
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENTRY, &aIndex, aEntry));
+}
+
+OTAPI
+void 
+OTCALL
+otLinkClearBlacklist(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_CLEAR_MAC_BLACKLIST);
+}
+
+OTAPI
+void 
+OTCALL
+otLinkSetBlacklistEnabled(
+    _In_ otInstance *aInstance,
+    bool aEnabled
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED, (BOOLEAN)aEnabled);
+}
+
+OTAPI
+bool 
+OTCALL
+otLinkIsBlacklistEnabled(
+    _In_ otInstance *aInstance
+    )
+{
+    BOOLEAN Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED, &Result);
+    return Result != FALSE;
+}
+
+OTAPI 
+otError 
+OTCALL
+otLinkGetAssignLinkQuality(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aExtAddr, 
+    _Out_ uint8_t *aLinkQuality
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY, (otExtAddress*)aExtAddr, aLinkQuality));
+}
+
+OTAPI 
+void 
+OTCALL
+otLinkSetAssignLinkQuality(
+    _In_ otInstance *aInstance,
+    const uint8_t *aExtAddr, 
+    uint8_t aLinkQuality
+    )
+{
+    if (aInstance == nullptr) return;
+    PackedBuffer3<GUID,otExtAddress,uint8_t> Buffer(aInstance->InterfaceGuid, *(otExtAddress*)aExtAddr, aLinkQuality);
+    (void)SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY, &Buffer, sizeof(Buffer), nullptr, 0);
+}
+
+OTAPI 
+void 
+OTCALL
+otInstanceReset(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_PLATFORM_RESET);
+}
+
+OTAPI 
+void 
+OTCALL
+otInstanceFactoryReset(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_FACTORY_RESET);
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetChildInfoById(
+    _In_ otInstance *aInstance, 
+    uint16_t aChildId, 
+    _Out_ otChildInfo *aChildInfo
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_INFO_BY_ID, &aChildId, aChildInfo));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetChildInfoByIndex(
+    _In_ otInstance *aInstance, 
+    uint8_t aChildIndex, 
+    _Out_ otChildInfo *aChildInfo
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_CHILD_INFO_BY_INDEX, &aChildIndex, aChildInfo));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetNextNeighborInfo(
+    _In_ otInstance *aInstance,
+    _Inout_ otNeighborInfoIterator *aIterator,
+    _Out_ otNeighborInfo *aInfo
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    UNREFERENCED_PARAMETER(aIterator);
+    UNREFERENCED_PARAMETER(aInfo);
+    return OT_ERROR_NOT_IMPLEMENTED; // TODO
+}
+
+OTAPI
+otDeviceRole 
+OTCALL
+otThreadGetDeviceRole(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = OT_DEVICE_ROLE_DISABLED;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_DEVICE_ROLE, &Result);
+    return (otDeviceRole)Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetEidCacheEntry(
+    _In_ otInstance *aInstance, 
+    uint8_t aIndex, 
+    _Out_ otEidCacheEntry *aEntry
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_EID_CACHE_ENTRY, &aIndex, aEntry));
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetLeaderData(
+    _In_ otInstance *aInstance, 
+    _Out_ otLeaderData *aLeaderData
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_DATA, aLeaderData));
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otThreadGetLeaderRouterId(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_ROUTER_ID, &Result);
+    return Result;
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otThreadGetLeaderWeight(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_LEADER_WEIGHT, &Result);
+    return Result;
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otNetDataGetVersion(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_NETWORK_DATA_VERSION, &Result);
+    return Result;
+}
+
+OTAPI
+uint32_t 
+OTCALL
+otThreadGetPartitionId(
+    _In_ otInstance *aInstance
+    )
+{
+    uint32_t Result = 0xFFFFFFFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARTITION_ID, &Result);
+    return Result;
+}
+
+OTAPI
+uint16_t 
+OTCALL
+otThreadGetRloc16(
+    _In_ otInstance *aInstance
+    )
+{
+    uint16_t Result = 0xFFFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_RLOC16, &Result);
+    return Result;
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otThreadGetRouterIdSequence(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_ID_SEQUENCE, &Result);
+    return Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadGetRouterInfo(
+    _In_ otInstance *aInstance, 
+    uint16_t aRouterId, 
+    _Out_ otRouterInfo *aRouterInfo
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_ROUTER_INFO, &aRouterId, aRouterInfo));
+}
+
+OTAPI 
+otError 
+OTCALL
+otThreadGetParentInfo(
+    _In_ otInstance *aInstance, 
+    _Out_ otRouterInfo *aParentInfo
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    static_assert(sizeof(otRouterInfo) == 20, "The size of otRouterInfo should be 20 bytes");
+    return DwordToThreadError(QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_INFO, aParentInfo));
+}
+
+OTAPI
+uint8_t 
+OTCALL
+otNetDataGetStableVersion(
+    _In_ otInstance *aInstance
+    )
+{
+    uint8_t Result = 0xFF;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_STABLE_NETWORK_DATA_VERSION, &Result);
+    return Result;
+}
+
+OTAPI
+const otMacCounters*
+OTCALL
+otLinkGetCounters(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return nullptr;
+
+    otMacCounters* aCounters = (otMacCounters*)malloc(sizeof(otMacCounters));
+    if (aCounters)
+    {
+        if (ERROR_SUCCESS != QueryIOCTL(aInstance, IOCTL_OTLWF_OT_MAC_COUNTERS, aCounters))
+        {
+            free(aCounters);
+            aCounters = nullptr;
+        }
+    }
+    return aCounters;
+}
+
+OTAPI
+void
+OTCALL
+otMessageGetBufferInfo(
+    _In_ otInstance *,
+    _Out_ otBufferInfo *aBufferInfo
+    )
+{
+    // Not supported on Windows
+    ZeroMemory(aBufferInfo, sizeof(otBufferInfo));
+}
+
+OTAPI
+bool 
+OTCALL
+otIsIp6AddressEqual(
+    const otIp6Address *a, 
+    const otIp6Address *b
+    )
+{
+    return memcmp(a->mFields.m8, b->mFields.m8, sizeof(otIp6Address)) == 0;
+}
+
+OTAPI
+otError 
+OTCALL
+otIp6AddressFromString(
+    const char *str, 
+    otIp6Address *address
+    )
+{
+    otError error = OT_ERROR_NONE;
+    uint8_t *dst = reinterpret_cast<uint8_t *>(address->mFields.m8);
+    uint8_t *endp = reinterpret_cast<uint8_t *>(address->mFields.m8 + 15);
+    uint8_t *colonp = NULL;
+    uint16_t val = 0;
+    uint8_t count = 0;
+    bool first = true;
+    char ch;
+    uint8_t d;
+
+    memset(address->mFields.m8, 0, 16);
+
+    dst--;
+
+    for (;;)
+    {
+        ch = *str++;
+        d = ch & 0xf;
+
+        if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'))
+        {
+            d += 9;
+        }
+        else if (ch == ':' || ch == '\0' || ch == ' ')
+        {
+            if (count)
+            {
+                if (dst + 2 > endp)
+                {
+                    error = OT_ERROR_PARSE;
+                    goto exit;
+                }
+                *(dst + 1) = static_cast<uint8_t>(val >> 8);
+                *(dst + 2) = static_cast<uint8_t>(val);
+                dst += 2;
+                count = 0;
+                val = 0;
+            }
+            else if (ch == ':')
+            {
+                if (!(colonp == nullptr || first))
+                {
+                    error = OT_ERROR_PARSE;
+                    goto exit;
+                }
+                colonp = dst;
+            }
+
+            if (ch == '\0' || ch == ' ')
+            {
+                break;
+            }
+
+            continue;
+        }
+        else
+        {
+            if (!('0' <= ch && ch <= '9'))
+            {
+                error = OT_ERROR_PARSE;
+                goto exit;
+            }
+        }
+
+        first = false;
+        val = static_cast<uint16_t>((val << 4) | d);
+        if (!(++count <= 4))
+        {
+            error = OT_ERROR_PARSE;
+            goto exit;
+        }
+    }
+
+    while (colonp && dst > colonp)
+    {
+        *endp-- = *dst--;
+    }
+
+    while (endp > dst)
+    {
+        *endp-- = 0;
+    }
+
+exit:
+    return error;
+}
+
+OTAPI 
+uint8_t 
+OTCALL
+otIp6PrefixMatch(
+    const otIp6Address *aFirst, 
+    const otIp6Address *aSecond
+    )
+{
+    uint8_t rval = 0;
+    uint8_t diff;
+
+    for (uint8_t i = 0; i < sizeof(otIp6Address); i++)
+    {
+        diff = aFirst->mFields.m8[i] ^ aSecond->mFields.m8[i];
+
+        if (diff == 0)
+        {
+            rval += 8;
+        }
+        else
+        {
+            while ((diff & 0x80) == 0)
+            {
+                rval++;
+                diff <<= 1;
+            }
+
+            break;
+        }
+    }
+
+    return rval;
+}
+
+OTAPI
+const char *
+OTCALL
+otThreadErrorToString(
+    otError aError
+    )
+{
+    const char *retval;
+
+    switch (aError)
+    {
+    case OT_ERROR_NONE:
+        retval = "None";
+        break;
+
+    case OT_ERROR_FAILED:
+        retval = "Failed";
+        break;
+
+    case OT_ERROR_DROP:
+        retval = "Drop";
+        break;
+
+    case OT_ERROR_NO_BUFS:
+        retval = "NoBufs";
+        break;
+
+    case OT_ERROR_NO_ROUTE:
+        retval = "NoRoute";
+        break;
+
+    case OT_ERROR_BUSY:
+        retval = "Busy";
+        break;
+
+    case OT_ERROR_PARSE:
+        retval = "Parse";
+        break;
+
+    case OT_ERROR_INVALID_ARGS:
+        retval = "InvalidArgs";
+        break;
+
+    case OT_ERROR_SECURITY:
+        retval = "Security";
+        break;
+
+    case OT_ERROR_ADDRESS_QUERY:
+        retval = "AddressQuery";
+        break;
+
+    case OT_ERROR_NO_ADDRESS:
+        retval = "NoAddress";
+        break;
+
+    case OT_ERROR_ABORT:
+        retval = "Abort";
+        break;
+
+    case OT_ERROR_NOT_IMPLEMENTED:
+        retval = "NotImplemented";
+        break;
+
+    case OT_ERROR_INVALID_STATE:
+        retval = "InvalidState";
+        break;
+
+    case OT_ERROR_NO_ACK:
+        retval = "NoAck";
+        break;
+
+    case OT_ERROR_CHANNEL_ACCESS_FAILURE:
+        retval = "ChannelAccessFailure";
+        break;
+
+    case OT_ERROR_DETACHED:
+        retval = "Detached";
+        break;
+
+    case OT_ERROR_FCS:
+        retval = "FcsErr";
+        break;
+
+    case OT_ERROR_NO_FRAME_RECEIVED:
+        retval = "NoFrameReceived";
+        break;
+
+    case OT_ERROR_UNKNOWN_NEIGHBOR:
+        retval = "UnknownNeighbor";
+        break;
+
+    case OT_ERROR_INVALID_SOURCE_ADDRESS:
+        retval = "InvalidSourceAddress";
+        break;
+
+    case OT_ERROR_WHITELIST_FILTERED:
+        retval = "WhitelistFiltered";
+        break;
+
+    case OT_ERROR_DESTINATION_ADDRESS_FILTERED:
+        retval = "DestinationAddressFiltered";
+        break;
+
+    case OT_ERROR_NOT_FOUND:
+        retval = "NotFound";
+        break;
+
+    case OT_ERROR_ALREADY:
+        retval = "Already";
+        break;
+
+    case OT_ERROR_BLACKLIST_FILTERED:
+        retval = "BlacklistFiltered";
+        break;
+
+    case OT_ERROR_IP6_ADDRESS_CREATION_FAILURE:
+        retval = "Ipv6AddressCreationFailure";
+        break;
+
+    case OT_ERROR_NOT_CAPABLE:
+        retval = "NotCapable";
+        break;
+
+    case OT_ERROR_RESPONSE_TIMEOUT:
+        retval = "ResponseTimeout";
+        break;
+
+    case OT_ERROR_DUPLICATED:
+        retval = "Duplicated";
+        break;
+
+    case OT_ERROR_GENERIC:
+        retval = "GenericError";
+        break;
+
+    default:
+        retval = "UnknownErrorType";
+        break;
+    }
+
+    return retval;
+}
+
+OTAPI 
+otError 
+OTCALL 
+otThreadSendDiagnosticGet(
+    _In_ otInstance *aInstance, 
+    const otIp6Address *aDestination, 
+    const uint8_t aTlvTypes[],
+    uint8_t aCount
+    )
+{
+    if (aInstance == nullptr || aDestination == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvTypes == nullptr && aCount != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t) + aCount;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDestination, sizeof(otIp6Address));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address), BufferSize - sizeof(GUID) - sizeof(otIp6Address), &aCount, sizeof(aCount));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otIp6Address) - sizeof(uint8_t), aTlvTypes, aCount);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_GET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL 
+otThreadSendDiagnosticReset(
+    _In_ otInstance *aInstance, 
+    const otIp6Address *aDestination, 
+    const uint8_t aTlvTypes[],
+    uint8_t aCount
+    )
+{
+    if (aInstance == nullptr || aDestination == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvTypes == nullptr && aCount != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t) + aCount;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDestination, sizeof(otIp6Address));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address), BufferSize - sizeof(GUID) - sizeof(otIp6Address), &aCount, sizeof(aCount));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otIp6Address) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otIp6Address) - sizeof(uint8_t), aTlvTypes, aCount);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_RESET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL
+otCommissionerStart(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_COMMISIONER_START));
+}
+
+OTAPI 
+otError 
+OTCALL
+otCommissionerAddJoiner(
+    _In_ otInstance *aInstance, 
+    const otExtAddress *aExtAddress, 
+    const char *aPSKd,
+    uint32_t aTimeout
+    )
+{
+    if (aInstance == nullptr || aPSKd == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    size_t aPSKdLength = strnlen(aPSKd, OPENTHREAD_PSK_MAX_LENGTH + 1);
+    if (aPSKdLength > OPENTHREAD_PSK_MAX_LENGTH)
+    {
+        return OT_ERROR_INVALID_ARGS;
+    }
+
+    uint8_t aExtAddressValid = aExtAddress ? 1 : 0;
+    
+    const ULONG BufferLength = sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + (ULONG)aPSKdLength + 1 + sizeof(aTimeout);
+    BYTE Buffer[sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + OPENTHREAD_PSK_MAX_LENGTH + 1 + sizeof(aTimeout)] = {0};
+    memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), &aExtAddressValid, sizeof(aExtAddressValid));
+    if (aExtAddressValid)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t), aExtAddress, sizeof(otExtAddress));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t) - sizeof(otExtAddress), aPSKd, aPSKdLength);
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress) + aPSKdLength + 1, sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t) - sizeof(otExtAddress) - aPSKdLength - 1, &aTimeout, sizeof(aTimeout));
+    
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_ADD_JOINER, Buffer, BufferLength, nullptr, 0));
+}
+
+OTAPI 
+otError 
+OTCALL
+otCommissionerRemoveJoiner(
+    _In_ otInstance *aInstance, 
+    const otExtAddress *aExtAddress
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    uint8_t aExtAddressValid = aExtAddress ? 1 : 0;
+    
+    BYTE Buffer[sizeof(GUID) + sizeof(uint8_t) + sizeof(otExtAddress)] = {0};
+    memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), &aExtAddressValid, sizeof(aExtAddressValid));
+    if (aExtAddressValid)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), sizeof(Buffer) - sizeof(GUID) - sizeof(uint8_t), aExtAddress, sizeof(otExtAddress));
+
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_REMOVE_JOINER, Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+otError 
+OTCALL
+otCommissionerSetProvisioningUrl(
+    _In_ otInstance *aInstance,
+    const char *aProvisioningUrl
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    size_t aProvisioningUrlLength = aProvisioningUrl ? strnlen(aProvisioningUrl, OPENTHREAD_PROV_URL_MAX_LENGTH + 1) : 0;
+    if (aProvisioningUrlLength > OPENTHREAD_PROV_URL_MAX_LENGTH)
+    {
+        return OT_ERROR_INVALID_ARGS;
+    }
+    
+    const ULONG BufferLength = sizeof(GUID) + (ULONG)aProvisioningUrlLength + 1;
+    BYTE Buffer[sizeof(GUID) + OPENTHREAD_PROV_URL_MAX_LENGTH + 1] = {0};
+    memcpy_s(Buffer, sizeof(Buffer), &aInstance->InterfaceGuid, sizeof(GUID));
+    if (aProvisioningUrlLength > 0)
+        memcpy_s(Buffer + sizeof(GUID), sizeof(Buffer) - sizeof(GUID), aProvisioningUrl, aProvisioningUrlLength);
+    
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_PROVISIONING_URL, Buffer, BufferLength, nullptr, 0));
+}
+
+OTAPI
+otError
+OTCALL
+otCommissionerAnnounceBegin(
+    otInstance *aInstance,
+    uint32_t aChannelMask,
+    uint8_t aCount,
+    uint16_t aPeriod,
+    const otIp6Address *aAddress
+    )
+{
+    if (aInstance == nullptr || aAddress == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    PackedBuffer5<GUID,uint32_t,uint8_t,uint16_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aChannelMask, aCount, aPeriod, *aAddress);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISIONER_ANNOUNCE_BEGIN, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+otError 
+OTCALL
+otCommissionerStop(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_COMMISIONER_STOP));
+}
+
+OTAPI
+otError 
+OTCALL
+otCommissionerEnergyScan(
+    _In_ otInstance *aInstance, 
+    uint32_t aChannelMask, 
+    uint8_t aCount, 
+    uint16_t aPeriod,
+    uint16_t aScanDuration, 
+    const otIp6Address *aAddress,
+    _In_ otCommissionerEnergyReportCallback aCallback, 
+    _In_ void *aContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->CommissionerEnergyReportCallbacks,
+        aInstance->InterfaceGuid, aCallback, aContext
+        );
+
+    PackedBuffer6<GUID,uint32_t,uint8_t,uint16_t,uint16_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aChannelMask, aCount, aPeriod, aScanDuration, *aAddress);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISSIONER_ENERGY_SCAN, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI
+otError 
+OTCALL
+otCommissionerPanIdQuery(
+    _In_ otInstance *aInstance, 
+    uint16_t aPanId, 
+    uint32_t aChannelMask,
+    const otIp6Address *aAddress,
+    _In_ otCommissionerPanIdConflictCallback aCallback, 
+    _In_ void *aContext
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->CommissionerPanIdConflictCallbacks,
+        aInstance->InterfaceGuid, aCallback, aContext
+        );
+
+    PackedBuffer4<GUID,uint16_t,uint32_t,otIp6Address> Buffer(aInstance->InterfaceGuid, aPanId, aChannelMask, *aAddress);
+    return DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_COMMISSIONER_PANID_QUERY, &Buffer, sizeof(Buffer), nullptr, 0));
+}
+
+OTAPI 
+otError 
+OTCALL 
+otCommissionerSendMgmtGet(
+    _In_ otInstance *aInstance, 
+    const uint8_t *aTlvs, 
+    uint8_t aLength
+)
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(uint8_t) + aLength;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(uint8_t), aTlvs, aLength);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_GET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI 
+otError 
+OTCALL 
+otCommissionerSendMgmtSet(
+    _In_ otInstance *aInstance,
+    const otCommissioningDataset *aDataset,
+    const uint8_t *aTlvs,
+    uint8_t aLength
+    )
+{
+    if (aInstance == nullptr || aDataset == nullptr) return OT_ERROR_INVALID_ARGS;
+    if (aTlvs == nullptr && aLength != 0) return OT_ERROR_INVALID_ARGS;
+    
+    DWORD BufferSize = sizeof(GUID) + sizeof(otCommissioningDataset) + sizeof(uint8_t) + aLength;
+    PBYTE Buffer = (PBYTE)malloc(BufferSize);
+    if (Buffer == nullptr) return OT_ERROR_NO_BUFS;
+
+    memcpy_s(Buffer, BufferSize, &aInstance->InterfaceGuid, sizeof(GUID));
+    memcpy_s(Buffer + sizeof(GUID), BufferSize - sizeof(GUID), aDataset, sizeof(otCommissioningDataset));
+    memcpy_s(Buffer + sizeof(GUID) + sizeof(otCommissioningDataset), BufferSize - sizeof(GUID) - sizeof(otCommissioningDataset), &aLength, sizeof(aLength));
+    if (aLength > 0)
+        memcpy_s(Buffer + sizeof(GUID) + sizeof(otCommissioningDataset) + sizeof(uint8_t), BufferSize - sizeof(GUID) - sizeof(otCommissioningDataset) - sizeof(uint8_t), aTlvs, aLength);
+    
+    otError result = 
+        DwordToThreadError(SendIOCTL(aInstance->ApiHandle, IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_SET, Buffer, BufferSize, nullptr, 0));
+
+    free(Buffer);
+    return result;
+}
+
+OTAPI
+uint16_t
+OTCALL
+otCommissionerGetSessionId(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return 0;
+    // TODO
+    return 0;
+}
+
+OTAPI 
+otError 
+OTCALL
+otJoinerStart(
+    _In_ otInstance *aInstance,
+    _Null_terminated_ const char *aPSKd,
+    _Null_terminated_ const char *aProvisioningUrl,
+    _Null_terminated_ const char *aVendorName,
+    _Null_terminated_ const char *aVendorModel,
+    _Null_terminated_ const char *aVendorSwVersion,
+    _Null_terminated_ const char *aVendorData,
+    _In_ otJoinerCallback aCallback,
+    _In_ void *aCallbackContext
+    )
+{
+    if (aInstance == nullptr || aPSKd == nullptr) return OT_ERROR_INVALID_ARGS;
+
+    otCommissionConfig config = {0};
+
+    size_t aPSKdLength = strlen(aPSKd);
+    size_t aProvisioningUrlLength = aProvisioningUrl == nullptr ? 0 : strlen(aProvisioningUrl);
+    size_t aVendorNameLength = aVendorName == nullptr ? 0 : strlen(aVendorName);
+    size_t aVendorModelLength = aVendorModel == nullptr ? 0 : strlen(aVendorModel);
+    size_t aVendorSwVersionLength = aVendorSwVersion == nullptr ? 0 : strlen(aVendorSwVersion);
+    size_t aVendorDataLength = aVendorData == nullptr ? 0 : strlen(aVendorData);
+
+    if (aPSKdLength > OPENTHREAD_PSK_MAX_LENGTH ||
+        aProvisioningUrlLength > OPENTHREAD_PROV_URL_MAX_LENGTH ||
+        aVendorNameLength > OPENTHREAD_VENDOR_NAME_MAX_LENGTH ||
+        aVendorModelLength > OPENTHREAD_VENDOR_MODEL_MAX_LENGTH ||
+        aVendorSwVersionLength > OPENTHREAD_VENDOR_SW_VERSION_MAX_LENGTH ||
+        aVendorDataLength > OPENTHREAD_VENDOR_DATA_MAX_LENGTH)
+    {
+        return OT_ERROR_INVALID_ARGS;
+    }
+
+    memcpy_s(config.PSKd, sizeof(config.PSKd), aPSKd, aPSKdLength);
+    memcpy_s(config.ProvisioningUrl, sizeof(config.ProvisioningUrl), aProvisioningUrl, aProvisioningUrlLength);
+    memcpy_s(config.VendorName, sizeof(config.VendorName), aVendorName, aVendorNameLength);
+    memcpy_s(config.VendorModel, sizeof(config.VendorModel), aVendorModel, aVendorModelLength);
+    memcpy_s(config.VendorSwVersion, sizeof(config.VendorSwVersion), aVendorSwVersion, aVendorSwVersionLength);
+    memcpy_s(config.VendorData, sizeof(config.VendorData), aVendorData, aVendorDataLength);
+
+    aInstance->ApiHandle->SetCallback(
+        aInstance->ApiHandle->JoinerCallbacks,
+        aInstance->InterfaceGuid, aCallback, aCallbackContext
+        );
+
+    auto ret = DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_START, (const otCommissionConfig*)&config));
+
+    if (ret != OT_ERROR_NONE)
+    {
+        aInstance->ApiHandle->SetCallback(
+            aInstance->ApiHandle->JoinerCallbacks,
+            aInstance->InterfaceGuid, (otJoinerCallback)nullptr, (PVOID)nullptr
+            );
+    }
+
+    return ret;
+}
+
+OTAPI 
+otError 
+OTCALL
+otJoinerStop(
+    _In_ otInstance *aInstance
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_JOINER_STOP));
+}
+
+OTAPI
+int8_t
+OTCALL
+otThreadGetParentPriority(
+    _In_ otInstance *aInstance
+)
+{
+    int8_t Result = 0;
+    if (aInstance) (void)QueryIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_PRIORITY, &Result);
+    return Result;
+}
+
+OTAPI
+otError
+OTCALL
+otThreadSetParentPriority(
+    _In_ otInstance *aInstance,
+    int8_t aParentPriority
+    )
+{
+    if (aInstance == nullptr) return OT_ERROR_INVALID_ARGS;
+    return DwordToThreadError(SetIOCTL(aInstance, IOCTL_OTLWF_OT_PARENT_PRIORITY, aParentPriority));
+}
diff --git a/examples/drivers/windows/otApi/precomp.h b/examples/drivers/windows/otApi/precomp.h
new file mode 100644
index 0000000..0308eb4
--- /dev/null
+++ b/examples/drivers/windows/otApi/precomp.h
@@ -0,0 +1,34 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#pragma warning(disable:28301)  // No annotations for first declaration of *
+
+#include <windows.h>
+#include <winnt.h>
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <IPHlpApi.h>
+#include <mstcpip.h>
+#include <new>
+#include <vector>
+#include <tuple>
+
+// Define to export necessary functions
+#define OTDLL
+#define OTAPI EXTERN_C __declspec(dllexport)
+
+#include <openthread/openthread.h>
+#include <openthread/border_router.h>
+#include <openthread/dataset_ftd.h>
+#include <openthread/thread_ftd.h>
+#include <openthread/commissioner.h>
+#include <openthread/joiner.h>
+#include <openthread/platform/logging-windows.h>
+
+#include <winioctl.h>
+#include <otLwfIoctl.h>
+#include <rtlrefcount.h>
diff --git a/examples/drivers/windows/otCli/main.cpp b/examples/drivers/windows/otCli/main.cpp
new file mode 100644
index 0000000..7b73377
--- /dev/null
+++ b/examples/drivers/windows/otCli/main.cpp
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+#include <openthread/openthread.h>
+#include <openthread/cli.h>
+#include <openthread/platform/uart.h>
+#include <openthread/platform/misc.h>
+
+bool skipNextLine = false;
+
+int main(int argc, char *argv[])
+{
+    otCliUartInit(NULL);
+
+    char cmd[1024] = "\n";
+    otPlatUartReceived((uint8_t*)cmd, 1);
+
+    for (;;)
+    {
+        cmd[0] = 0;
+        if (NULL == fgets(cmd, sizeof(cmd), stdin))
+            continue;
+
+        size_t cmdLen = strlen(cmd);
+        if (cmdLen >= sizeof(cmd)) cmdLen = sizeof(cmd);
+
+        if (strncmp(cmd, "exit", 4) == 0) 
+            break;
+
+        skipNextLine = true;
+        otPlatUartReceived((uint8_t*)cmd, (uint16_t)cmdLen);
+    }
+
+    return NO_ERROR;
+}
+
+EXTERN_C otError otPlatUartEnable(void)
+{
+    return OT_ERROR_NONE;
+}
+
+EXTERN_C otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
+{
+    otError error = OT_ERROR_NONE;
+
+    if (!skipNextLine)
+    {
+        for (uint16_t i = 0; i < aBufLength; i++)
+            fputc(aBuf[i], stdout);
+    }
+
+    if (aBuf[aBufLength - 1] == '\n')
+        skipNextLine = false;
+
+    otPlatUartSendDone();
+
+    return error;
+}
+
+EXTERN_C void otPlatWakeHost(void)
+{
+
+}
diff --git a/examples/drivers/windows/otLwf/address.c b/examples/drivers/windows/otLwf/address.c
new file mode 100644
index 0000000..98abaa1
--- /dev/null
+++ b/examples/drivers/windows/otLwf/address.c
@@ -0,0 +1,507 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "address.tmh"
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN 
+otLwfOnAddressAdded(
+    _In_ PMS_FILTER pFilter, 
+    _In_ const otNetifAddress* Addr,
+    _In_ BOOLEAN UpdateWindows
+    )
+{
+    if (pFilter->otCachedAddrCount >= OT_MAX_ADDRESSES)
+    {
+        LogError(DRIVER_DEFAULT, "Failing to add new address as we have reached our max!");
+        return FALSE;
+    }
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! adding address: %!IPV6ADDR! (%u-bit prefix)", 
+        &pFilter->InterfaceGuid, 
+        (PIN6_ADDR)&Addr->mAddress,
+        Addr->mPrefixLength
+        );
+
+    // Update local cache
+    memcpy(pFilter->otCachedAddr + pFilter->otCachedAddrCount, Addr, sizeof(IN6_ADDR));
+    pFilter->otCachedAddrCount++;
+
+    // If this is link local, cache it as our link local address
+    if (IN6_IS_ADDR_LINKLOCAL((PIN6_ADDR)&Addr->mAddress))
+    {
+        memcpy(&pFilter->otLinkLocalAddr, Addr, sizeof(IN6_ADDR));
+    }
+        
+    // Update Windows if necessary
+    if (UpdateWindows)
+    {
+        NTSTATUS status;
+        MIB_UNICASTIPADDRESS_ROW newRow;
+        MIB_IPFORWARD_ROW2 newRouteRow;
+        COMPARTMENT_ID OriginalCompartmentID;
+        InitializeUnicastIpAddressEntry(&newRow);
+        InitializeIpForwardEntry(&newRouteRow); 
+
+        newRow.InterfaceIndex = pFilter->InterfaceIndex;
+        newRow.InterfaceLuid = pFilter->InterfaceLuid;
+        newRow.Address.si_family = AF_INET6;
+        newRow.Address.Ipv6.sin6_family = AF_INET6;
+        
+        static_assert(sizeof(IN6_ADDR) == sizeof(otIp6Address), "Windows and OpenThread IPv6 Addr Structs must be same size");
+
+        memcpy(&newRow.Address.Ipv6.sin6_addr, &Addr->mAddress, sizeof(IN6_ADDR));
+        newRow.OnLinkPrefixLength = Addr->mPrefixLength;
+        newRow.PreferredLifetime = Addr->mPreferred ? 0xffffffff : 0;
+        newRow.ValidLifetime = Addr->mValid ? 0xffffffff : 0;
+        newRow.PrefixOrigin = IpPrefixOriginOther;  // Derived from network XPANID
+        newRow.SkipAsSource = FALSE;                // Allow automatic binding to this address (default)
+
+        if (IN6_IS_ADDR_LINKLOCAL(&newRow.Address.Ipv6.sin6_addr))
+        {
+            newRow.SuffixOrigin = IpSuffixOriginLinkLayerAddress;   // Derived from Extended MAC address
+        }
+        else
+        {
+            newRow.SuffixOrigin = IpSuffixOriginRandom;             // Was created randomly
+        }
+
+        // Make sure we are in the right compartment
+        (VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID);
+
+        status = CreateUnicastIpAddressEntry(&newRow);
+        //NT_ASSERT(NT_SUCCESS(status));
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "CreateUnicastIpAddressEntry failed %!STATUS!", status);
+        }
+
+        newRouteRow.InterfaceIndex = pFilter->InterfaceIndex;
+        newRouteRow.InterfaceLuid = pFilter->InterfaceLuid;
+        newRouteRow.DestinationPrefix.Prefix.si_family = AF_INET6;
+        newRouteRow.DestinationPrefix.PrefixLength = 0;
+
+        status = CreateIpForwardEntry2(&newRouteRow);
+        if (!NT_SUCCESS(status) && status != STATUS_DUPLICATE_OBJECTID)
+        {
+            LogVerbose(DRIVER_DEFAULT, "CreateIpForwardEntry2 failed %!STATUS!", status);
+        }
+
+        // Revert back to original compartment
+        otLwfRevertCompartment(OriginalCompartmentID);
+    }
+
+    return TRUE;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID 
+otLwfOnAddressRemoved(
+    _In_ PMS_FILTER pFilter, 
+    _In_ ULONG CachedIndex,
+    _In_ BOOLEAN UpdateWindows
+    )
+{
+    // Cache address before we delete local cache
+    IN6_ADDR Addr = pFilter->otCachedAddr[CachedIndex];
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! removing address: %!IPV6ADDR!", &pFilter->InterfaceGuid, &Addr);
+    
+    NT_ASSERT(pFilter->otCachedAddrCount != 0);
+    NT_ASSERT(CachedIndex < pFilter->otCachedAddrCount);
+
+    // Remove the cached entry
+    if (CachedIndex + 1 != pFilter->otCachedAddrCount)
+        memmove(pFilter->otCachedAddr + CachedIndex, 
+                pFilter->otCachedAddr + CachedIndex + 1, 
+                (pFilter->otCachedAddrCount - CachedIndex - 1) * sizeof(IN6_ADDR)
+                );
+    pFilter->otCachedAddrCount--;
+
+    // Update Windows if necessary
+    if (UpdateWindows)
+    {
+        MIB_UNICASTIPADDRESS_ROW deleteRow;
+        COMPARTMENT_ID OriginalCompartmentID;
+        InitializeUnicastIpAddressEntry(&deleteRow);
+
+        deleteRow.InterfaceIndex = pFilter->InterfaceIndex;
+        deleteRow.InterfaceLuid = pFilter->InterfaceLuid;
+        deleteRow.Address.si_family = AF_INET6;
+
+        deleteRow.Address.Ipv6.sin6_addr = Addr;
+
+        // Make sure we are in the right compartment
+        (VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID);
+    
+        // Best effort remove address from TCPIP
+        (VOID)DeleteUnicastIpAddressEntry(&deleteRow);
+
+        // Revert back to original compartment
+        otLwfRevertCompartment(OriginalCompartmentID);
+    }
+}
+
+int 
+otLwfFindCachedAddrIndex(
+    _In_ PMS_FILTER pFilter, 
+    _In_ PIN6_ADDR addr
+    )
+{
+    for (ULONG i = 0; i < pFilter->otCachedAddrCount; i++)
+        if (memcmp(pFilter->otCachedAddr + i, addr, sizeof(IN6_ADDR)) == 0)
+            return (int)i;
+    return -1;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS 
+otLwfInitializeAddresses(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    
+    PMIB_UNICASTIPADDRESS_TABLE pMibUnicastAddressTable = NULL;
+    COMPARTMENT_ID              OriginalCompartmentID;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    pFilter->otCachedAddrCount = 0;
+
+    // Make sure we are in the right compartment
+    (VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID);
+
+    // Query the table for the current compartment
+    status = GetUnicastIpAddressTable(AF_INET6, &pMibUnicastAddressTable);
+
+    // Revert the compartment, now that we have the table
+    otLwfRevertCompartment(OriginalCompartmentID);
+
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "GetUnicastIpAddressTable failed, %!STATUS!", status);
+        goto error;
+    }
+    
+    // Iterate through the addresses and delete (best effort) the ones for our interface
+    for (ULONG Index = 0; Index < pMibUnicastAddressTable->NumEntries; Index++)
+    {
+        MIB_UNICASTIPADDRESS_ROW* row = &pMibUnicastAddressTable->Table[Index];
+
+        if ((0 == memcmp(&row->InterfaceLuid, &pFilter->InterfaceLuid, sizeof(NET_LUID))))
+        {
+            LogInfo(DRIVER_DEFAULT, "Caching initial address: %!IPV6ADDR!", &row->Address.Ipv6.sin6_addr);
+            memcpy(pFilter->otCachedAddr + pFilter->otCachedAddrCount, &row->Address.Ipv6.sin6_addr, sizeof(IN6_ADDR));
+            pFilter->otCachedAddrCount++;
+        }
+    }
+
+error:
+    
+    if (NULL != pMibUnicastAddressTable)
+    {
+        FreeMibTable(pMibUnicastAddressTable);
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+// Callback from Windows TCPIP stack when an address change occurs
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+NETIOAPI_API_ 
+otLwfAddressChangeCallback(
+    _In_ PVOID CallerContext,
+    _In_opt_ PMIB_UNICASTIPADDRESS_ROW Row,
+    _In_ MIB_NOTIFICATION_TYPE NotificationType
+    )
+{
+    PMS_FILTER pFilter = (PMS_FILTER)CallerContext;
+    if (Row == NULL || pFilter == NULL) return;
+
+    // Ignore notifications that aren't for our interface
+    if (Row->InterfaceIndex != pFilter->InterfaceIndex) return;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "%p (%u) %!IPV6ADDR!", pFilter, NotificationType, &Row->Address.Ipv6.sin6_addr);
+
+    // Since we don't pass in the initial flag, we shouldn't get this type
+    NT_ASSERT(NotificationType != MibInitialNotification);
+
+    // Make sure we can reference the interface
+    if (ExAcquireRundownProtection(&pFilter->ExternalRefs))
+    {
+        if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+        {
+            // Queue up the event for processing
+            otLwfEventProcessingIndicateAddressChange(
+                pFilter,
+                NotificationType,
+                &Row->Address.Ipv6.sin6_addr
+                );
+        }
+        else
+        {
+            NT_ASSERT(FALSE); // Need to add support for this in tunnel mode
+        }
+
+        // Release reference on the interface
+        ExReleaseRundownProtection(&pFilter->ExternalRefs);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Callback on the OpenThread thread for processing an address change
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingAddressChanged(
+    _In_ PMS_FILTER             pFilter,
+    _In_ MIB_NOTIFICATION_TYPE  NotificationType,
+    _In_ PIN6_ADDR              pAddr
+    )
+{
+    LogFuncEntryMsg(DRIVER_DEFAULT, "%p (%u)", pFilter, NotificationType);
+
+    if (NotificationType == MibAddInstance ||
+        NotificationType == MibParameterNotification)
+    {
+        MIB_UNICASTIPADDRESS_ROW Row;
+        InitializeUnicastIpAddressEntry(&Row);
+
+        Row.Address.si_family = AF_INET6;
+        Row.Address.Ipv6.sin6_addr = *pAddr;
+        Row.InterfaceIndex = pFilter->InterfaceIndex;
+        Row.InterfaceLuid = pFilter->InterfaceLuid;
+
+        NTSTATUS status = GetUnicastIpAddressEntry(&Row);
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "GetUnicastIpAddressEntry failed, %!STATUS!", status);
+        }
+        else
+        {
+            otNetifAddress otAddr = {0};
+            memcpy(&otAddr.mAddress, pAddr, sizeof(IN6_ADDR));
+            otAddr.mPreferred = Row.PreferredLifetime != 0;
+            otAddr.mPrefixLength = Row.OnLinkPrefixLength;
+            otAddr.mValid = Row.ValidLifetime != 0;
+
+            BOOLEAN ShouldDelete = FALSE;
+            BOOLEAN AddedToCache = FALSE;
+            BOOLEAN IsCached = otLwfFindCachedAddrIndex(pFilter, pAddr) != -1;
+
+            // Ignore link local addresses
+            if (IN6_IS_ADDR_LINKLOCAL(pAddr) && !IsCached)
+            {
+                ShouldDelete = TRUE;
+                goto add_complete;
+            }
+
+            // Add to the cache if this is a new address
+            if (NotificationType == MibAddInstance && !IsCached)
+            {
+                AddedToCache = otLwfOnAddressAdded(pFilter, &otAddr, FALSE);
+                if (AddedToCache == FALSE)
+                {
+                    ShouldDelete = TRUE;
+                    goto add_complete;
+                }
+            }
+
+            // Update OpenThread if we don't have this cached or it is being updated
+            if (!IsCached/* || NotificationType == MibParameterNotification*/)
+            {
+                LogInfo(DRIVER_DEFAULT, "Filter %p trying to add/update address: %!IPV6ADDR!", pFilter, pAddr);
+
+                // Add (or update) the address to OpenThread
+                otError otError = otIp6AddUnicastAddress(pFilter->otCtx, &otAddr);
+                if (otError != OT_ERROR_NONE)
+                {
+                    LogError(DRIVER_DEFAULT, "otIp6AddUnicastAddress failed, %!otError!", otError);
+                    ShouldDelete = otError == OT_ERROR_NO_BUFS ? TRUE : FALSE;
+                }
+            }
+
+        add_complete:
+
+            // Remove it from TCPIP if necessary
+            if (ShouldDelete)
+            {
+                LogInfo(DRIVER_DEFAULT, "Filter %p deleting recently added address: %!IPV6ADDR!", pFilter, pAddr);
+
+                // Best effort remove address from TCPIP
+                (VOID)DeleteUnicastIpAddressEntry(&Row);
+            }
+        }
+    }
+    else if (NotificationType == MibDeleteInstance)
+    {
+        // Look for the address in our cache
+        int index = otLwfFindCachedAddrIndex(pFilter, pAddr);
+
+        // If it's not already deleted from our cache, then Windows
+        // is deleting the adddress and we need to update OpenThread.
+        if (index != -1)
+        {
+            // Update our cache
+            otLwfOnAddressRemoved(pFilter, (ULONG)index, FALSE);
+            
+            LogInfo(DRIVER_DEFAULT, "Filter %p trying to remove address: %!IPV6ADDR!", pFilter, pAddr);
+
+            // Find the correct address from OpenThread to remove (best effort)
+            (void)otIp6RemoveUnicastAddress(pFilter->otCtx, (otIp6Address*)pAddr);
+        }
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID 
+otLwfRadioAddressesUpdated(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    ULONG FoundInOpenThread = 0; // Bit field
+    ULONG OriginalCacheLength = pFilter->otCachedAddrCount;
+    
+    NT_ASSERT(pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE);
+
+    const otNetifAddress* addr = otIp6GetUnicastAddresses(pFilter->otCtx);
+
+    // Process the addresses
+    while (addr)
+    {
+        int index = otLwfFindCachedAddrIndex(pFilter, (PIN6_ADDR)&addr->mAddress);
+        if (index == -1)
+        {
+            otLwfOnAddressAdded(pFilter, addr, TRUE);
+        }
+        else
+        {
+            NT_ASSERT(index < 8 * sizeof(FoundInOpenThread));
+            FoundInOpenThread |= 1 << index;
+        }
+        addr = addr->mNext;
+    }
+
+    // Look for missing addresses and mark them as removed
+    for (int i = OriginalCacheLength - 1; i >= 0; i--)
+    {
+        if ((FoundInOpenThread & (1 << i)) == 0)
+        {
+            otLwfOnAddressRemoved(pFilter, (ULONG)i, TRUE);
+        }
+    }
+    
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID 
+otLwfTunAddressesUpdated(
+    _In_ PMS_FILTER pFilter,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len,
+    _Out_ uint32_t *aNotifFlags
+    )
+{
+    ULONG FoundInOpenThread = 0; // Bit field
+    ULONG OriginalCacheLength = pFilter->otCachedAddrCount;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    NT_ASSERT (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE);
+
+    *aNotifFlags = 0;
+
+    while (value_data_len > 0)
+    {
+        const uint8_t *entry_ptr = NULL;
+        spinel_size_t entry_len = 0;
+
+        spinel_ssize_t len = spinel_datatype_unpack(value_data_ptr, value_data_len, "d", &entry_ptr, &entry_len);
+        if (len < 1) break;
+
+        {
+            PIN6_ADDR pAddr = NULL;
+            otNetifAddress addr = { { 0 }, 0, 1, 1, 0, 0, 0, NULL };
+            uint32_t preferredLifetime = 0xFFFFFFFF;
+            uint32_t validLifetime = 0xFFFFFFFF;
+
+            spinel_datatype_unpack(
+                entry_ptr, 
+                entry_len, 
+                "6CLL", 
+                &pAddr, 
+                &addr.mPrefixLength, 
+                &validLifetime,
+                &preferredLifetime);
+
+            addr.mPreferred = preferredLifetime != 0;
+            addr.mValid = validLifetime != 0;
+
+            if (pAddr)
+            {
+                int index = otLwfFindCachedAddrIndex(pFilter, pAddr);
+                if (index == -1)
+                {
+                    memcpy_s(&addr.mAddress, sizeof(addr.mAddress), pAddr, sizeof(IN6_ADDR));
+                    otLwfOnAddressAdded(pFilter, &addr, TRUE);
+                    *aNotifFlags |= OT_CHANGED_IP6_ADDRESS_ADDED;
+                }
+                else
+                {
+                    NT_ASSERT(index < 8 * sizeof(FoundInOpenThread));
+                    FoundInOpenThread |= 1 << index;
+                }
+            }
+        }
+
+        value_data_len -= len;
+        value_data_ptr += len;
+    }
+
+    // Look for missing addresses and mark them as removed
+    for (int i = OriginalCacheLength - 1; i >= 0; i--)
+    {
+        if ((FoundInOpenThread & (1 << i)) == 0)
+        {
+            otLwfOnAddressRemoved(pFilter, (ULONG)i, TRUE);
+            *aNotifFlags |= OT_CHANGED_IP6_ADDRESS_REMOVED;
+        }
+    }
+    
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/otLwf/alarm.c b/examples/drivers/windows/otLwf/alarm.c
new file mode 100644
index 0000000..f087d48
--- /dev/null
+++ b/examples/drivers/windows/otLwf/alarm.c
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the alarm functions required for the OpenThread library.
+ */
+
+#include "precomp.h"
+#include "alarm.tmh"
+
+uint32_t 
+otPlatAlarmGetNow()
+{
+    // Return number of 'ticks'
+    LARGE_INTEGER PerformanceCounter = KeQueryPerformanceCounter(NULL);
+
+    // Multiply by 1000 ms/sec and divide by 'ticks'/sec to get ms
+    return (uint32_t)(PerformanceCounter.QuadPart * 1000 / FilterPerformanceFrequency.QuadPart);
+}
+
+void 
+otPlatAlarmStop(
+    _In_ otInstance *otCtx
+    )
+{
+    LogVerbose(DRIVER_DEFAULT, "otPlatAlarmStop");
+    otLwfEventProcessingIndicateNewWaitTime(otCtxToFilter(otCtx), (ULONG)(-1));
+}
+
+void 
+otPlatAlarmStartAt(
+    _In_ otInstance *otCtx, 
+    uint32_t now, 
+    uint32_t waitTime
+    )
+{
+    UNREFERENCED_PARAMETER(now);
+    LogVerbose(DRIVER_DEFAULT, "otPlatAlarmStartAt %u ms", waitTime);
+    otLwfEventProcessingIndicateNewWaitTime(otCtxToFilter(otCtx), waitTime);
+}
diff --git a/examples/drivers/windows/otLwf/command.c b/examples/drivers/windows/otLwf/command.c
new file mode 100644
index 0000000..1168ebe
--- /dev/null
+++ b/examples/drivers/windows/otLwf/command.c
@@ -0,0 +1,1426 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the functions for sending/receiving Spinel commands to the miniport.
+ */
+
+#include "precomp.h"
+#include "command.tmh"
+
+typedef struct _SPINEL_CMD_HANDLER_ENTRY
+{
+    LIST_ENTRY          Link;
+    volatile LONG       RefCount;
+    SPINEL_CMD_HANDLER *Handler;
+    PVOID               Context;
+    spinel_tid_t        TransactionId;
+} SPINEL_CMD_HANDLER_ENTRY;
+
+void AddEntryRef(SPINEL_CMD_HANDLER_ENTRY *pEntry) { InterlockedIncrement(&pEntry->RefCount); }
+void ReleaseEntryRef(SPINEL_CMD_HANDLER_ENTRY *pEntry) 
+{ 
+    if (InterlockedDecrement(&pEntry->RefCount) == 0)
+    {
+        FILTER_FREE_MEM(pEntry);
+    }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfCmdInitialize(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+    NTSTATUS NtStatus = STATUS_SUCCESS;
+    uint32_t MajorVersion = 0;
+    uint32_t MinorVersion = 0;
+    uint32_t InterfaceType = 0;
+
+    NET_BUFFER_LIST_POOL_PARAMETERS PoolParams =
+    {
+        { NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1 },
+        NDIS_PROTOCOL_ID_DEFAULT,
+        TRUE,
+        0,
+        'lbNC', // CNbl
+        0
+    };
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    do
+    {
+        pFilter->cmdTIDsInUse = 0;
+        pFilter->cmdNextTID = 1;
+        pFilter->cmdResetReason = OT_PLAT_RESET_REASON_POWER_ON;
+
+        NdisAllocateSpinLock(&pFilter->cmdLock);
+        InitializeListHead(&pFilter->cmdHandlers);
+
+        KeInitializeEvent(
+            &pFilter->cmdResetCompleteEvent,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+
+        // Enable rundown protection
+        ExReInitializeRundownProtection(&pFilter->cmdRundown);
+
+        // Create the NDIS pool for creating the SendNetBufferList
+        pFilter->cmdNblPool = NdisAllocateNetBufferListPool(pFilter->FilterHandle, &PoolParams);
+        if (pFilter->cmdNblPool == NULL)
+        {
+            Status = NDIS_STATUS_RESOURCES;
+            LogWarning(DRIVER_DEFAULT, "Failed to create NetBufferList pool for Spinel commands");
+            break;
+        }
+
+        // Query the interface type to make sure it is a Thread device
+#ifdef COMMAND_INIT_RETRY
+        pFilter->cmdInitTryCount = 0;
+        while (pFilter->cmdInitTryCount < 10)
+        {
+            NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_PROTOCOL_VERSION, "ii", &MajorVersion, &MinorVersion);
+            if (!NT_SUCCESS(NtStatus))
+            {
+                pFilter->cmdInitTryCount++;
+                NdisMSleep(100);
+                continue;
+            }
+            break;
+        }
+        if (pFilter->cmdInitTryCount >= 10)
+        {
+#else
+        NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_PROTOCOL_VERSION, "ii", &MajorVersion, &MinorVersion);
+        if (!NT_SUCCESS(NtStatus))
+        {
+#endif
+            Status = NDIS_STATUS_NOT_SUPPORTED;
+            LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_PROTOCOL_VERSION, %!STATUS!", NtStatus);
+            break;
+        }
+        if (MajorVersion != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR ||
+            MinorVersion < 3) // TODO - Remove this minor version check with the next major version update
+        {
+            Status = NDIS_STATUS_NOT_SUPPORTED;
+            LogError(DRIVER_DEFAULT, "Protocol Version Mismatch! OsVer: %d.%d DeviceVer: %d.%d",
+                     SPINEL_PROTOCOL_VERSION_THREAD_MAJOR, SPINEL_PROTOCOL_VERSION_THREAD_MINOR,
+                     MajorVersion, MinorVersion);
+            break;
+        }
+
+        NtStatus = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_INTERFACE_TYPE, SPINEL_DATATYPE_UINT_PACKED_S, &InterfaceType);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            Status = NDIS_STATUS_NOT_SUPPORTED;
+            LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_INTERFACE_TYPE, %!STATUS!", NtStatus);
+            break;
+        }
+        if (InterfaceType != SPINEL_PROTOCOL_TYPE_THREAD)
+        {
+            Status = NDIS_STATUS_NOT_SUPPORTED;
+            LogError(DRIVER_DEFAULT, "SPINEL_PROP_INTERFACE_TYPE is invalid, %d", InterfaceType);
+            break;
+        }
+
+        NtStatus = otLwfCmdResetDevice(pFilter, FALSE);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+    } while (FALSE);
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    // Clean up on failure
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        otLwfCmdUninitialize(pFilter);
+    }
+
+    return Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfCmdUninitialize(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    // Release and wait for run down. This will block waiting for any pending sends to complete
+    ExWaitForRundownProtectionRelease(&pFilter->cmdRundown);
+
+    // Use the NBL Pool variable as a flag for initialization
+    if (pFilter->cmdNblPool)
+    {
+        // Clean up any pending handlers
+        PLIST_ENTRY Link = pFilter->cmdHandlers.Flink;
+        while (Link != &pFilter->cmdHandlers)
+        {
+            SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link);
+            Link = Link->Flink;
+
+            if (pEntry->Handler)
+            {
+                pEntry->Handler(pFilter, pEntry->Context, 0, 0, NULL, 0);
+            }
+
+            ReleaseEntryRef(pEntry);
+        }
+        InitializeListHead(&pFilter->cmdHandlers);
+
+        // Free NBL Pool
+        NdisFreeNetBufferPool(pFilter->cmdNblPool);
+        pFilter->cmdNblPool = NULL;
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+//
+// Receive Spinel Encoded Command
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfCmdProcess(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ UINT command,
+    _In_reads_bytes_(cmd_data_len) const uint8_t* cmd_data_ptr,
+    _In_ spinel_size_t cmd_data_len
+    )
+{
+    uint8_t Header;
+    spinel_prop_key_t key;
+    uint8_t* value_data_ptr = NULL;
+    spinel_size_t value_data_len = 0;
+
+    // Make sure it's an expected command
+    if (command < SPINEL_CMD_PROP_VALUE_IS || command > SPINEL_CMD_PROP_VALUE_REMOVED)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Recieved unhandled command, %u", command);
+        return;
+    }
+
+    // Decode the key and data
+    if (spinel_datatype_unpack(cmd_data_ptr, cmd_data_len, "CiiD", &Header, NULL, &key, &value_data_ptr, &value_data_len) == -1)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Failed to unpack command key & data");
+        return;
+    }
+
+    // Get the transaction ID
+    if (SPINEL_HEADER_GET_TID(Header) == 0)
+    {
+        // Handle out of band last status locally
+        if (command == SPINEL_CMD_PROP_VALUE_IS && key == SPINEL_PROP_LAST_STATUS)
+        {
+            // Check if this is a reset
+            spinel_status_t status = SPINEL_STATUS_OK;
+            spinel_datatype_unpack(value_data_ptr, value_data_len, "i", &status);
+
+            if ((status >= SPINEL_STATUS_RESET__BEGIN) && (status <= SPINEL_STATUS_RESET__END))
+            {
+                LogInfo(DRIVER_DEFAULT, "Interface %!GUID! was reset (status %d).", &pFilter->InterfaceGuid, status);
+                pFilter->cmdResetReason = status - SPINEL_STATUS_RESET__BEGIN;
+                KeSetEvent(&pFilter->cmdResetCompleteEvent, IO_NO_INCREMENT, FALSE);
+
+                // TODO - Should this be passed on to Thread or Tunnel logic?
+            }
+        }
+        else if (ExAcquireRundownProtection(&pFilter->ExternalRefs))
+        {
+            // If this is a 'Value Is' command, process it for notification of state changes.
+            if (command == SPINEL_CMD_PROP_VALUE_IS)
+            {
+                if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+                {
+                    otLwfThreadValueIs(pFilter, DispatchLevel, key, value_data_ptr, value_data_len);
+                }
+                else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
+                {
+                    otLwfTunValueIs(pFilter, DispatchLevel, key, value_data_ptr, value_data_len);
+                }
+            }
+            else if (command == SPINEL_CMD_PROP_VALUE_INSERTED)
+            {
+                if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+                {
+                    otLwfThreadValueInserted(pFilter, DispatchLevel, key, value_data_ptr, value_data_len);
+                }
+                else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
+                {
+                    otLwfTunValueInserted(pFilter, DispatchLevel, key, value_data_ptr, value_data_len);
+                }
+            }
+
+            ExReleaseRundownProtection(&pFilter->ExternalRefs);
+        }
+    }
+    // If there was a transaction ID, then look for the corresponding command handler
+    else
+    {
+        PLIST_ENTRY Link;
+        SPINEL_CMD_HANDLER_ENTRY* Handler = NULL;
+
+        FILTER_ACQUIRE_LOCK(&pFilter->cmdLock, DispatchLevel);
+
+        // Search for matching handlers for this command
+        Link = pFilter->cmdHandlers.Flink;
+        while (Link != &pFilter->cmdHandlers)
+        {
+            SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link);
+            Link = Link->Flink;
+
+            if (SPINEL_HEADER_GET_TID(Header) == pEntry->TransactionId)
+            {
+                // Remove from the main list
+                RemoveEntryList(&pEntry->Link);
+
+                // Cache the handler
+                Handler = pEntry;
+
+                // Remove the transaction ID from the 'in use' bit field
+                pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId);
+
+                break;
+            }
+        }
+
+        FILTER_RELEASE_LOCK(&pFilter->cmdLock, DispatchLevel);
+
+        // TODO - Set event
+
+        // Process the handler we found, outside the lock
+        if (Handler)
+        {
+            // Call the handler function
+            Handler->Handler(pFilter, Handler->Context, command, key, value_data_ptr, value_data_len);
+
+            // Free the entry
+            ReleaseEntryRef(Handler);
+        }
+    }
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfCmdRecveive(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_reads_bytes_(BufferLength) const PUCHAR Buffer,
+    _In_ ULONG BufferLength
+    )
+{
+    uint8_t Header;
+    UINT Command;
+
+    // Unpack the header from the buffer
+    if (spinel_datatype_unpack(Buffer, BufferLength, "Ci", &Header, &Command) <= 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Failed to unpack header and command");
+        return;
+    }
+
+    // Validate the header
+    if ((Header & SPINEL_HEADER_FLAG) != SPINEL_HEADER_FLAG)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Recieved unrecognized frame, header=0x%x", Header);
+        return;
+    }
+
+    // We only support IID zero for now
+    if (SPINEL_HEADER_GET_IID(Header) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Recieved unsupported IID, %u", SPINEL_HEADER_GET_IID(Header));
+        return;
+    }
+
+    // Process the received command
+    otLwfCmdProcess(pFilter, DispatchLevel, Command, Buffer, BufferLength);
+}
+
+//
+// Send Async Spinel Encoded Command
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+spinel_tid_t
+otLwfCmdGetNextTID(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    spinel_tid_t TID = 0;
+    while (TID == 0)
+    {
+        NdisAcquireSpinLock(&pFilter->cmdLock);
+
+        if (((1 << pFilter->cmdNextTID) & pFilter->cmdTIDsInUse) == 0)
+        {
+            TID = pFilter->cmdNextTID;
+            pFilter->cmdNextTID = SPINEL_GET_NEXT_TID(pFilter->cmdNextTID);
+            pFilter->cmdTIDsInUse |= (1 << TID);
+        }
+
+        NdisReleaseSpinLock(&pFilter->cmdLock);
+
+        if (TID == 0)
+        {
+            // TODO - Wait for event
+        }
+    }
+    return TID;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+otLwfCmdAddHandler(
+    _In_ PMS_FILTER pFilter,
+    _In_ SPINEL_CMD_HANDLER_ENTRY *pEntry
+    )
+{
+    // Get the next transaction ID. This call will block if there are
+    // none currently available.
+    pEntry->TransactionId = otLwfCmdGetNextTID(pFilter);
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "tid=%u", (ULONG)pEntry->TransactionId);
+    
+    NdisAcquireSpinLock(&pFilter->cmdLock);
+    
+    // Add to the handlers list
+    AddEntryRef(pEntry);
+    InsertTailList(&pFilter->cmdHandlers, &pEntry->Link);
+    
+    NdisReleaseSpinLock(&pFilter->cmdLock);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdEncodeAndSendAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ spinel_tid_t tid,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    _In_opt_ va_list args
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNET_BUFFER_LIST NetBufferList = NULL;
+    PNET_BUFFER NetBuffer = NULL;
+    ULONG NetBufferLength = 0;
+    PUCHAR DataBuffer = NULL;
+    spinel_ssize_t PackedLength;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Cmd=%u Key=%u tid=%u", (ULONG)Command, (ULONG)Key, (ULONG)tid);
+
+    NetBufferList =
+        NdisAllocateNetBufferAndNetBufferList(
+            pFilter->cmdNblPool,     // PoolHandle
+            0,                              // ContextSize
+            0,                              // ContextBackFill
+            NULL,                           // MdlChain
+            0,                              // DataOffset
+            0                               // DataLength
+            );
+    if (NetBufferList == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogWarning(DRIVER_DEFAULT, "Failed to create command NetBufferList");
+        goto exit;
+    }
+        
+    // Initialize NetBuffer fields
+    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
+    NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL;
+    NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_DATA_LENGTH(NetBuffer) = 0;
+    NET_BUFFER_DATA_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_FIRST_MDL(NetBuffer) = NULL;
+
+    // Calculate length of NetBuffer
+    NetBufferLength = 16 + MaxDataLength;
+    if (NetBufferLength < 64) NetBufferLength = 64;
+    
+    // Allocate the NetBuffer for NetBufferList
+    if (NdisRetreatNetBufferDataStart(NetBuffer, NetBufferLength, 0, NULL) != NDIS_STATUS_SUCCESS)
+    {
+        NetBuffer = NULL;
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogError(DRIVER_DEFAULT, "Failed to allocate NB for command NetBufferList, %u bytes", NetBufferLength);
+        goto exit;
+    }
+
+    // Get the pointer to the data buffer
+    DataBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, NetBufferLength, NULL, 1, 0);
+    NT_ASSERT(DataBuffer);
+    
+    // Save the true NetBuffer length in the protocol reserved
+    NetBuffer->ProtocolReserved[0] = (PVOID)NetBufferLength;
+    NetBuffer->DataLength = 0;
+    
+    // Save the transaction ID in the protocol reserved
+    NetBuffer->ProtocolReserved[1] = (PVOID)tid;
+
+    // Pack the header, command and key
+    PackedLength = 
+        spinel_datatype_pack(
+            DataBuffer, 
+            NetBufferLength, 
+            "Cii", 
+            SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | tid, 
+            Command, 
+            Key);
+    if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+
+    NetBuffer->DataLength += (ULONG)PackedLength;
+
+    // Pack the data (if any)
+    if (pack_format)
+    {
+        PackedLength = 
+            spinel_datatype_vpack(
+                DataBuffer + NetBuffer->DataLength, 
+                NetBufferLength - NetBuffer->DataLength, 
+                pack_format, 
+                args);
+        if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto exit;
+        }
+
+        NetBuffer->DataLength += (ULONG)PackedLength;
+    }
+
+    // Grab a ref for rundown protection
+    if (!ExAcquireRundownProtection(&pFilter->cmdRundown))
+    {
+        status = STATUS_DEVICE_NOT_READY;
+        LogWarning(DRIVER_DEFAULT, "Failed to acquire rundown protection");
+        goto exit;
+    }
+
+    // Send the NBL down
+    NdisFSendNetBufferLists(
+        pFilter->FilterHandle, 
+        NetBufferList, 
+        NDIS_DEFAULT_PORT_NUMBER, 
+        0);
+
+    // Clear local variable because we don't own the NBL any more
+    NetBufferList = NULL;
+
+exit:
+
+    if (NetBufferList)
+    {
+        if (NetBuffer)
+        {
+            NetBuffer->DataLength = (ULONG)(ULONG_PTR)NetBuffer->ProtocolReserved[0];
+            NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL);
+        }
+        NdisFreeNetBufferList(NetBufferList);
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdResetDevice(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN fAsync
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    KeResetEvent(&pFilter->cmdResetCompleteEvent);
+
+    NTSTATUS status = otLwfCmdEncodeAndSendAsync(pFilter, SPINEL_CMD_RESET, 0, 0, 0, NULL, NULL);
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Failed to send SPINEL_CMD_RESET, %!STATUS!", status);
+    }
+    else if (!fAsync)
+    {
+        // Create the relative (negative) time to wait for 5 seconds
+        LARGE_INTEGER Timeout;
+        Timeout.QuadPart = -5000 * 10000;
+
+        status = KeWaitForSingleObject(&pFilter->cmdResetCompleteEvent, Executive, KernelMode, FALSE, &Timeout);
+        if (status != STATUS_SUCCESS)
+        {
+            LogError(DRIVER_DEFAULT, "Failed waiting for reset complete, %!STATUS!", status);
+            status = STATUS_DEVICE_BUSY;
+        }
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSendAsyncV(
+    _In_ PMS_FILTER pFilter,
+    _In_opt_ SPINEL_CMD_HANDLER *Handler,
+    _In_opt_ PVOID HandlerContext,
+    _Out_opt_ spinel_tid_t *pTid,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    _In_opt_ va_list args
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    SPINEL_CMD_HANDLER_ENTRY *pEntry = NULL;
+
+    if (pTid) *pTid = 0;
+
+    // Create the handler entry and add it to the list
+    if (Handler)
+    {
+        pEntry = FILTER_ALLOC_MEM(pFilter->FilterHandle, sizeof(SPINEL_CMD_HANDLER_ENTRY));
+        if (pEntry == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            LogWarning(DRIVER_DEFAULT, "Failed to allocate handler entry");
+            goto exit;
+        }
+
+        pEntry->RefCount = 1;
+        pEntry->Handler = Handler;
+        pEntry->Context = HandlerContext;
+
+        otLwfCmdAddHandler(pFilter, pEntry);
+
+        if (pTid) *pTid = pEntry->TransactionId;
+    }
+    
+    status = otLwfCmdEncodeAndSendAsync(pFilter, Command, Key, pEntry ? pEntry->TransactionId : 0, MaxDataLength, pack_format, args);
+
+    // Remove the handler entry from the list
+    if (!NT_SUCCESS(status) && pEntry)
+    {
+        NdisAcquireSpinLock(&pFilter->cmdLock);
+    
+        // Remove from the main list
+        RemoveEntryList(&pEntry->Link);
+
+        // Remove the transaction ID from the 'in use' bit field
+        pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId);
+
+        NdisReleaseSpinLock(&pFilter->cmdLock);
+
+        // TODO - Set event
+
+        ReleaseEntryRef(pEntry);
+    }
+
+exit:
+
+    if (pEntry) ReleaseEntryRef(pEntry);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSendAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_opt_ SPINEL_CMD_HANDLER *Handler,
+    _In_opt_ PVOID HandlerContext,
+    _Out_opt_ spinel_tid_t *pTid,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    ...
+    )
+{
+    va_list args;
+    va_start(args, pack_format);
+    NTSTATUS status = 
+        otLwfCmdSendAsyncV(pFilter, Handler, HandlerContext, pTid, Command, Key, MaxDataLength, pack_format, args);
+    va_end(args);
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+BOOLEAN
+otLwfCmdCancel(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_tid_t tid
+    )
+{
+    PLIST_ENTRY Link;
+    SPINEL_CMD_HANDLER_ENTRY* Handler = NULL;
+    BOOLEAN Found = FALSE;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "tid=%u", (ULONG)tid);
+
+    FILTER_ACQUIRE_LOCK(&pFilter->cmdLock, DispatchLevel);
+    
+    // Search for matching handlers for this transaction ID
+    Link = pFilter->cmdHandlers.Flink;
+    while (Link != &pFilter->cmdHandlers)
+    {
+        SPINEL_CMD_HANDLER_ENTRY* pEntry = CONTAINING_RECORD(Link, SPINEL_CMD_HANDLER_ENTRY, Link);
+        Link = Link->Flink;
+
+        if (tid == pEntry->TransactionId)
+        {
+            // Remove from the main list
+            RemoveEntryList(&pEntry->Link);
+
+            // Save handler to cancel outside lock
+            Handler = pEntry;
+            Found = TRUE;
+
+            // Remove the transaction ID from the 'in use' bit field
+            pFilter->cmdTIDsInUse &= ~(1 << pEntry->TransactionId);
+
+            break;
+        }
+    }
+    
+    FILTER_RELEASE_LOCK(&pFilter->cmdLock, DispatchLevel);
+
+    if (Handler)
+    {
+        // Call the handler function
+        Handler->Handler(pFilter, Handler->Context, 0, 0, NULL, 0);
+
+        // Free the entry
+        ReleaseEntryRef(Handler);
+    }
+
+    LogFuncExitMsg(DRIVER_DEFAULT, "Found=%u", Found);
+
+    return Found;
+}
+
+//
+// Send Packet/Frame
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfCmdSendIp6PacketAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ PNET_BUFFER IpNetBuffer,
+    _In_ BOOLEAN Secured
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNET_BUFFER_LIST NetBufferList = NULL;
+    PNET_BUFFER NetBuffer = NULL;
+    ULONG NetBufferLength = 0;
+    PUCHAR DataBuffer = NULL;
+    PUCHAR IpDataBuffer = NULL;
+    spinel_ssize_t PackedLength;
+    IPV6_HEADER* v6Header;
+
+    NetBufferList =
+        NdisAllocateNetBufferAndNetBufferList(
+            pFilter->cmdNblPool,            // PoolHandle
+            0,                              // ContextSize
+            0,                              // ContextBackFill
+            NULL,                           // MdlChain
+            0,                              // DataOffset
+            0                               // DataLength
+            );
+    if (NetBufferList == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogWarning(DRIVER_DEFAULT, "Failed to create command NetBufferList");
+        goto exit;
+    }
+        
+    // Initialize NetBuffer fields
+    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
+    NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL;
+    NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_DATA_LENGTH(NetBuffer) = 0;
+    NET_BUFFER_DATA_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_FIRST_MDL(NetBuffer) = NULL;
+
+    // Calculate length of NetBuffer
+    NetBufferLength = 20 + IpNetBuffer->DataLength;
+    if (NetBufferLength < 64) NetBufferLength = 64;
+    
+    // Allocate the NetBuffer for NetBufferList
+    if (NdisRetreatNetBufferDataStart(NetBuffer, NetBufferLength, 0, NULL) != NDIS_STATUS_SUCCESS)
+    {
+        NetBuffer = NULL;
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogError(DRIVER_DEFAULT, "Failed to allocate NB for command NetBufferList, %u bytes", NetBufferLength);
+        goto exit;
+    }
+
+    // Get the pointer to the data buffer for the header data
+    DataBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, NetBufferLength, NULL, 1, 0);
+    NT_ASSERT(DataBuffer);
+    
+    // Save the true NetBuffer length in the protocol reserved
+    NetBuffer->ProtocolReserved[0] = (PVOID)NetBufferLength;
+    NetBuffer->DataLength = 0;
+
+    // Pack the header, command and key
+    PackedLength = 
+        spinel_datatype_pack(
+            DataBuffer, 
+            NetBufferLength, 
+            "Cii", 
+            (spinel_tid_t)(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0), 
+            (UINT)SPINEL_CMD_PROP_VALUE_SET, 
+            (Secured ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE));
+    if (PackedLength < 0 || PackedLength + NetBuffer->DataLength > NetBufferLength)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto exit;
+    }
+
+    NT_ASSERT(PackedLength >= 3);
+    NetBuffer->DataLength += (ULONG)PackedLength;
+    
+    // Copy over the data length
+    DataBuffer[NetBuffer->DataLength+1] = (((USHORT)IpNetBuffer->DataLength) >> 8) & 0xff;
+    DataBuffer[NetBuffer->DataLength]   = (((USHORT)IpNetBuffer->DataLength) >> 0) & 0xff;
+    NetBuffer->DataLength += 2;
+    
+    v6Header = (IPV6_HEADER*)(DataBuffer + NetBuffer->DataLength);
+
+    // Copy the IP packet data
+    IpDataBuffer = (PUCHAR)NdisGetDataBuffer(IpNetBuffer, IpNetBuffer->DataLength, v6Header, 1, 0);
+    if (IpDataBuffer != (PUCHAR)v6Header)
+    {
+        RtlCopyMemory(v6Header, IpDataBuffer, IpNetBuffer->DataLength);
+    }
+
+    NetBuffer->DataLength += IpNetBuffer->DataLength;
+
+    // Grab a ref for rundown protection
+    if (!ExAcquireRundownProtection(&pFilter->cmdRundown))
+    {
+        status = STATUS_DEVICE_NOT_READY;
+        LogWarning(DRIVER_DEFAULT, "Failed to acquire rundown protection");
+        goto exit;
+    }
+                                            
+    LogVerbose(DRIVER_DATA_PATH, "Filter: %p, IP6_SEND: %p : %!IPV6ADDR! => %!IPV6ADDR! (%u bytes)", 
+                pFilter, NetBufferList, &v6Header->SourceAddress, &v6Header->DestinationAddress, 
+                NET_BUFFER_DATA_LENGTH(IpNetBuffer));
+
+    // Send the NBL down
+    NdisFSendNetBufferLists(
+        pFilter->FilterHandle, 
+        NetBufferList, 
+        NDIS_DEFAULT_PORT_NUMBER, 
+        DispatchLevel ? NDIS_SEND_FLAGS_DISPATCH_LEVEL : 0);
+
+    // Clear local variable because we don't own the NBL any more
+    NetBufferList = NULL;
+
+exit:
+
+    if (NetBufferList)
+    {
+        if (NetBuffer)
+        {
+            NetBuffer->DataLength = (ULONG)(ULONG_PTR)NetBuffer->ProtocolReserved[0];
+            NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL);
+        }
+        NdisFreeNetBufferList(NetBufferList);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfCmdSendMacFrameComplete(
+    _In_ PMS_FILTER pFilter,
+    _In_ PVOID Context,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength
+    )
+{
+    UNREFERENCED_PARAMETER(Context);
+
+    pFilter->otLastTransmitError = OT_ERROR_ABORT;
+
+    if (Data && Command == SPINEL_CMD_PROP_VALUE_IS)
+    {
+        if (Key == SPINEL_PROP_LAST_STATUS)
+        {
+            spinel_status_t spinel_status = SPINEL_STATUS_OK;
+            spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status);
+            if (packed_len > 0)
+            {
+                if (spinel_status == SPINEL_STATUS_OK)
+                {
+                    pFilter->otLastTransmitError = OT_ERROR_NONE;
+                    (void)spinel_datatype_unpack(
+                        Data + packed_len,
+                        DataLength - (spinel_size_t)packed_len,
+                        "b",
+                        &pFilter->otLastTransmitFramePending);
+                }
+                else
+                {
+                    pFilter->otLastTransmitError = SpinelStatusToThreadError(spinel_status);
+                }
+            }
+        }
+    }
+
+    // Set the completion event
+    KeSetEvent(&pFilter->SendNetBufferListComplete, IO_NO_INCREMENT, FALSE);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfCmdSendMacFrameAsync(
+    _In_ PMS_FILTER     pFilter,
+    _In_ otRadioFrame*  Packet
+    )
+{
+    // Reset the completion event
+    KeResetEvent(&pFilter->SendNetBufferListComplete);
+    pFilter->SendPending = TRUE;
+
+    NTSTATUS status =
+        otLwfCmdSendAsync(
+            pFilter,
+            otLwfCmdSendMacFrameComplete,
+            NULL,
+            NULL,
+            SPINEL_CMD_PROP_VALUE_SET,
+            SPINEL_PROP_STREAM_RAW,
+            Packet->mLength + 20,
+            SPINEL_DATATYPE_DATA_WLEN_S
+            SPINEL_DATATYPE_UINT8_S
+            SPINEL_DATATYPE_INT8_S,
+            Packet->mPsdu,
+            (uint32_t)Packet->mLength,
+            Packet->mChannel,
+            Packet->mPower
+            );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_STREAM_RAW failed, %!STATUS!", status);
+        pFilter->otLastTransmitError = OT_ERROR_ABORT;
+        KeSetEvent(&pFilter->SendNetBufferListComplete, IO_NO_INCREMENT, FALSE);
+    }
+}
+
+//
+// Send Synchronous Spinel Encoded Command
+//
+
+typedef struct _SPINEL_GET_PROP_CONTEXT
+{
+    KEVENT              CompletionEvent;
+    spinel_prop_key_t   Key;
+    PVOID              *DataBuffer;
+    const char*         Format;
+    va_list             Args;
+    NTSTATUS            Status;
+} SPINEL_GET_PROP_CONTEXT;
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfGetPropHandler(
+    _In_ PMS_FILTER pFilter,
+    _In_ PVOID Context,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength
+    )
+{
+    SPINEL_GET_PROP_CONTEXT* CmdContext = (SPINEL_GET_PROP_CONTEXT*)Context;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key);
+
+    UNREFERENCED_PARAMETER(pFilter);
+    
+    if (Data == NULL)
+    {
+        CmdContext->Status = STATUS_CANCELLED;
+    }
+    else if (Command != SPINEL_CMD_PROP_VALUE_IS)
+    {
+        CmdContext->Status = STATUS_INVALID_PARAMETER;
+    }
+    else if (Key == SPINEL_PROP_LAST_STATUS)
+    {
+        spinel_status_t spinel_status = SPINEL_STATUS_OK;
+        spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status);
+        if (packed_len < 0 || (ULONG)packed_len > DataLength)
+        {
+            CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+        else
+        {
+            otError errorCode = SpinelStatusToThreadError(spinel_status);
+            LogVerbose(DRIVER_DEFAULT, "Get key=%u failed with %!otError!", CmdContext->Key, errorCode);
+            CmdContext->Status = ThreadErrorToNtstatus(errorCode);
+        }
+    }
+    else if (Key == CmdContext->Key)
+    {
+        if (CmdContext->DataBuffer)
+        {
+            *CmdContext->DataBuffer = FILTER_ALLOC_MEM(pFilter->FilterHandle, DataLength);
+            if (*CmdContext->DataBuffer == NULL)
+            {
+                CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES;
+                DataLength = 0;
+            }
+            else
+            {
+                memcpy(*CmdContext->DataBuffer, Data, DataLength);
+                Data = (uint8_t*)*CmdContext->DataBuffer;
+            }
+        }
+
+        spinel_ssize_t packed_len = spinel_datatype_vunpack(Data, DataLength, CmdContext->Format, CmdContext->Args);
+        if (packed_len < 0 || (ULONG)packed_len > DataLength)
+        {
+            CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+        else
+        {
+            CmdContext->Status = STATUS_SUCCESS;
+        }
+    }
+    else
+    {
+        CmdContext->Status = STATUS_INVALID_PARAMETER;
+    }
+
+    // Set the completion event
+    KeSetEvent(&CmdContext->CompletionEvent, 0, FALSE);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdGetProp(
+    _In_ PMS_FILTER pFilter,
+    _Out_opt_ PVOID *DataBuffer,
+    _In_ spinel_prop_key_t Key,
+    _In_ const char *pack_format, 
+    ...
+    )
+{
+    NTSTATUS status;
+    LARGE_INTEGER WaitTimeout;
+    spinel_tid_t tid;
+
+    // Create the context structure
+    SPINEL_GET_PROP_CONTEXT Context;
+    KeInitializeEvent(&Context.CompletionEvent, SynchronizationEvent, FALSE);
+    Context.Key = Key;
+    Context.DataBuffer = DataBuffer;
+    Context.Format = pack_format;
+    Context.Status = STATUS_SUCCESS;
+    va_start(Context.Args, pack_format);
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key);
+
+    // Send the request transaction
+    status = 
+        otLwfCmdSendAsyncV(
+            pFilter, 
+            otLwfGetPropHandler, 
+            &Context,
+            &tid,
+            SPINEL_CMD_PROP_VALUE_GET, 
+            Key, 
+            0, 
+            NULL,
+            NULL);
+    if (NT_SUCCESS(status))
+    {
+        // Set a 1 second wait timeout
+        WaitTimeout.QuadPart = -1000 * 10000;
+
+        // Wait for the response
+        if (KeWaitForSingleObject(
+                &Context.CompletionEvent,
+                Executive,
+                KernelMode,
+                FALSE,
+                &WaitTimeout) != STATUS_SUCCESS)
+        {
+            if (!otLwfCmdCancel(pFilter, FALSE, tid))
+            {
+                KeWaitForSingleObject(
+                    &Context.CompletionEvent,
+                    Executive,
+                    KernelMode,
+                    FALSE,
+                    NULL);
+            }
+        }
+    }
+    else
+    {
+        Context.Status = status;
+    }
+    
+    va_end(Context.Args);
+
+    LogFuncExitNT(DRIVER_DEFAULT, Context.Status);
+
+    return Context.Status;
+}
+
+typedef struct _SPINEL_SET_PROP_CONTEXT
+{
+    KEVENT              CompletionEvent;
+    UINT                ExpectedResultCommand;
+    spinel_prop_key_t   Key;
+    NTSTATUS            Status;
+} SPINEL_SET_PROP_CONTEXT;
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfSetPropHandler(
+    _In_ PMS_FILTER pFilter,
+    _In_ PVOID Context,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength
+    )
+{
+    SPINEL_SET_PROP_CONTEXT* CmdContext = (SPINEL_SET_PROP_CONTEXT*)Context;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Key=%u", (ULONG)Key);
+
+    UNREFERENCED_PARAMETER(pFilter);
+    
+    if (Data == NULL)
+    {
+        CmdContext->Status = STATUS_CANCELLED;
+    }
+    else if (Command == SPINEL_CMD_PROP_VALUE_IS && Key == SPINEL_PROP_LAST_STATUS)
+    {
+        spinel_status_t spinel_status = SPINEL_STATUS_OK;
+        spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status);
+        if (packed_len < 0 || (ULONG)packed_len > DataLength)
+        {
+            CmdContext->Status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+        else
+        {
+            otError errorCode = SpinelStatusToThreadError(spinel_status);
+            LogVerbose(DRIVER_DEFAULT, "Set key=%u failed with %!otError!", CmdContext->Key, errorCode);
+            CmdContext->Status = ThreadErrorToNtstatus(errorCode);
+        }
+    }
+    else if (Command != CmdContext->ExpectedResultCommand)
+    {
+        NT_ASSERT(FALSE);
+        CmdContext->Status = STATUS_INVALID_PARAMETER;
+    }
+    else if (Key == CmdContext->Key)
+    {
+        CmdContext->Status = STATUS_SUCCESS;
+    }
+    else
+    {
+        NT_ASSERT(FALSE);
+        CmdContext->Status = STATUS_INVALID_PARAMETER;
+    }
+
+    // Set the completion event
+    KeSetEvent(&CmdContext->CompletionEvent, 0, FALSE);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSetPropV(
+    _In_ PMS_FILTER pFilter,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    _In_opt_ va_list args
+    )
+{
+    NTSTATUS status;
+    LARGE_INTEGER WaitTimeout;
+    spinel_tid_t tid;
+
+    // Create the context structure
+    SPINEL_SET_PROP_CONTEXT Context;
+    KeInitializeEvent(&Context.CompletionEvent, SynchronizationEvent, FALSE);
+    Context.Key = Key;
+    Context.Status = STATUS_SUCCESS;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Cmd=%u Key=%u", Command, (ULONG)Key);
+
+    if (Command == SPINEL_CMD_PROP_VALUE_SET)
+    {
+        Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_IS;
+    }
+    else if (Command == SPINEL_CMD_PROP_VALUE_INSERT)
+    {
+        Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_INSERTED;
+    }
+    else if (Command == SPINEL_CMD_PROP_VALUE_REMOVE)
+    {
+        Context.ExpectedResultCommand = SPINEL_CMD_PROP_VALUE_REMOVED;
+    }
+    else
+    {
+        ASSERT(FALSE);
+    }
+
+    // Send the request transaction
+    status = 
+        otLwfCmdSendAsyncV(
+            pFilter, 
+            otLwfSetPropHandler, 
+            &Context, 
+            &tid,
+            Command,
+            Key, 
+            8, 
+            pack_format,
+            args);
+    if (NT_SUCCESS(status))
+    {
+        // Set a 1 second wait timeout
+        WaitTimeout.QuadPart = -1000 * 10000;
+
+        // Wait for the response
+        if (KeWaitForSingleObject(
+                &Context.CompletionEvent,
+                Executive,
+                KernelMode,
+                FALSE,
+                &WaitTimeout) != STATUS_SUCCESS)
+        {
+            if (!otLwfCmdCancel(pFilter, FALSE, tid))
+            {
+                KeWaitForSingleObject(
+                    &Context.CompletionEvent,
+                    Executive,
+                    KernelMode,
+                    FALSE,
+                    NULL);
+            }
+        }
+    }
+    else
+    {
+        Context.Status = status;
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, Context.Status);
+
+    return Context.Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSetProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    ...
+    )
+{
+    va_list args;
+    va_start(args, pack_format);
+    NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_SET, Key, pack_format, args);
+    va_end(args);
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdInsertProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    ...
+    )
+{
+    va_list args;
+    va_start(args, pack_format);
+    NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_INSERT, Key, pack_format, args);
+    va_end(args);
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdRemoveProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    ...
+    )
+{
+    va_list args;
+    va_start(args, pack_format);
+    NTSTATUS status = otLwfCmdSetPropV(pFilter, SPINEL_CMD_PROP_VALUE_REMOVE, Key, pack_format, args);
+    va_end(args);
+    return status;
+}
+
+//
+// General Spinel Helpers
+//
+
+otError
+SpinelStatusToThreadError(
+    spinel_status_t error
+    )
+{
+    otError ret;
+
+    switch (error)
+    {
+    case SPINEL_STATUS_OK:
+        ret = OT_ERROR_NONE;
+        break;
+
+    case SPINEL_STATUS_FAILURE:
+        ret = OT_ERROR_FAILED;
+        break;
+
+    case SPINEL_STATUS_DROPPED:
+        ret = OT_ERROR_DROP;
+        break;
+
+    case SPINEL_STATUS_NOMEM:
+        ret = OT_ERROR_NO_BUFS;
+        break;
+
+    case SPINEL_STATUS_BUSY:
+        ret = OT_ERROR_BUSY;
+        break;
+
+    case SPINEL_STATUS_PARSE_ERROR:
+        ret = OT_ERROR_PARSE;
+        break;
+
+    case SPINEL_STATUS_INVALID_ARGUMENT:
+        ret = OT_ERROR_INVALID_ARGS;
+        break;
+
+    case SPINEL_STATUS_UNIMPLEMENTED:
+        ret = OT_ERROR_NOT_IMPLEMENTED;
+        break;
+
+    case SPINEL_STATUS_INVALID_STATE:
+        ret = OT_ERROR_INVALID_STATE;
+        break;
+
+    case SPINEL_STATUS_NO_ACK:
+        ret = OT_ERROR_NO_ACK;
+        break;
+
+    case SPINEL_STATUS_CCA_FAILURE:
+        ret = OT_ERROR_CHANNEL_ACCESS_FAILURE;
+        break;
+
+    case SPINEL_STATUS_ALREADY:
+        ret = OT_ERROR_ALREADY;
+        break;
+
+    case SPINEL_STATUS_ITEM_NOT_FOUND:
+        ret = OT_ERROR_NOT_FOUND;
+        break;
+
+    default:
+        if (error >= SPINEL_STATUS_STACK_NATIVE__BEGIN && error <= SPINEL_STATUS_STACK_NATIVE__END)
+        {
+            ret = (otError)(error - SPINEL_STATUS_STACK_NATIVE__BEGIN);
+        }
+        else
+        {
+            ret = OT_ERROR_FAILED;
+        }
+        break;
+    }
+
+    return ret;
+}
+
+BOOLEAN
+try_spinel_datatype_unpack(
+    const uint8_t *data_in,
+    spinel_size_t data_len,
+    const char *pack_format,
+    ...
+    )
+{
+    va_list args;
+    va_start(args, pack_format);
+    spinel_ssize_t packed_len = spinel_datatype_vunpack(data_in, data_len, pack_format, args);
+    va_end(args);
+
+    return !(packed_len < 0 || (spinel_size_t)packed_len > data_len);
+}
diff --git a/examples/drivers/windows/otLwf/command.h b/examples/drivers/windows/otLwf/command.h
new file mode 100644
index 0000000..2a73d8d
--- /dev/null
+++ b/examples/drivers/windows/otLwf/command.h
@@ -0,0 +1,204 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the functions for sending/receiving Spinel commands to the miniport.
+ */
+
+#ifndef _COMMAND_H_
+#define _COMMAND_H_
+
+//
+// Initialization
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfCmdInitialize(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfCmdUninitialize(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdResetDevice(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN fAsync
+    );
+
+//
+// Receive Spinel Encoded Command
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfCmdRecveive(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_reads_bytes_(BufferLength) const PUCHAR Buffer,
+    _In_ ULONG BufferLength
+    );
+
+//
+// Send Async Spinel Encoded Command
+//
+
+typedef
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+(SPINEL_CMD_HANDLER)(
+    _In_ PMS_FILTER pFilter,
+    _In_ PVOID Context,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSendAsyncV(
+    _In_ PMS_FILTER pFilter,
+    _In_opt_ SPINEL_CMD_HANDLER *Handler,
+    _In_opt_ PVOID HandlerContext,
+    _Out_opt_ spinel_tid_t *pTid,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    _In_opt_ va_list args
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSendAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_opt_ SPINEL_CMD_HANDLER *Handler,
+    _In_opt_ PVOID HandlerContext,
+    _Out_opt_ spinel_tid_t *pTid,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    ...
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+BOOLEAN
+otLwfCmdCancel(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_tid_t tid
+    );
+
+//
+// Send Packet/Frame
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfCmdSendIp6PacketAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ PNET_BUFFER IpNetBuffer,
+    _In_ BOOLEAN Secured
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfCmdSendMacFrameAsync(
+    _In_ PMS_FILTER pFilter,
+    _In_ otRadioFrame* Packet
+    );
+
+//
+// Send Synchronous Spinel Encoded Command
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdGetProp(
+    _In_ PMS_FILTER pFilter,
+    _Out_opt_ PVOID *DataBuffer,
+    _In_ spinel_prop_key_t Key,
+    _In_ const char *pack_format, 
+    ...
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdSetProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format, 
+    ...
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdInsertProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    ...
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfCmdRemoveProp(
+    _In_ PMS_FILTER pFilter,
+    _In_ spinel_prop_key_t Key,
+    _In_opt_ const char *pack_format,
+    ...
+    );
+
+//
+// General Spinel Helpers
+//
+
+otError
+SpinelStatusToThreadError(
+    spinel_status_t error
+    );
+
+BOOLEAN
+try_spinel_datatype_unpack(
+    const uint8_t *data_in,
+    spinel_size_t data_len,
+    const char *pack_format,
+    ...
+    );
+
+#endif  //_COMMAND_H_
diff --git a/examples/drivers/windows/otLwf/datapath.c b/examples/drivers/windows/otLwf/datapath.c
new file mode 100644
index 0000000..82ab6c6
--- /dev/null
+++ b/examples/drivers/windows/otLwf/datapath.c
@@ -0,0 +1,672 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the functions required for handling NetBufferLists in
+ *  the data path.
+ */
+
+#include "precomp.h"
+#include "datapath.tmh"
+
+#ifdef LOG_BUFFERS
+
+__forceinline CHAR ToHex(CHAR n)
+{
+    if (n > 9) return 'A' + (n - 10);
+    else       return '0' + n;
+}
+
+#define otLogLineLength 32
+
+// Helper to log a buffer
+void
+otLogBuffer(
+    _In_reads_bytes_(BufferLength) PUCHAR Buffer,
+    _In_                           ULONG  BufferLength
+    )
+{
+    ULONG index = 0;
+    while (index < BufferLength)
+    {
+        CHAR szBuffer[otLogLineLength * 4] = "  ";
+        PCHAR buf = szBuffer + 2;
+        for (ULONG i = 0; i < otLogLineLength && i + index < BufferLength; i++)
+        {
+            buf[0] = ToHex(Buffer[i + index] >> 4);
+            buf[1] = ToHex(Buffer[i + index] & 0x0F);
+            buf[2] = ' ';
+            buf += 3;
+        }
+        buf[0] = 0;
+
+        LogVerbose(DRIVER_DATA_PATH, "%s", szBuffer);
+        index += otLogLineLength;
+    }
+}
+
+#endif
+
+_Use_decl_annotations_
+VOID
+FilterSendNetBufferListsComplete(
+    NDIS_HANDLE         FilterModuleContext,
+    PNET_BUFFER_LIST    NetBufferLists,
+    ULONG               SendCompleteFlags
+    )
+/*++
+
+Routine Description:
+
+    Send complete handler
+
+    This routine is invoked whenever the lower layer is finished processing
+    sent NET_BUFFER_LISTs.  If the filter does not need to be involved in the
+    send path, you should remove this routine and the FilterSendNetBufferLists
+    routine.  NDIS will pass along send packets on behalf of your filter more
+    efficiently than the filter can.
+
+Arguments:
+
+    FilterModuleContext     - our filter context
+    NetBufferLists          - a chain of NBLs that are being returned to you
+    SendCompleteFlags       - flags (see documentation)
+
+--*/
+{
+    PMS_FILTER         pFilter = (PMS_FILTER)FilterModuleContext;
+    
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p, NBL: %p %!STATUS!", FilterModuleContext, NetBufferLists, NetBufferLists->Status);
+
+    PNET_BUFFER_LIST NBL = NetBufferLists;
+    while (NBL)
+    {
+        PNET_BUFFER_LIST NextNBL = NBL->Next;
+        PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB(NBL);
+
+        // Cancel command if we failed to send the NBL
+        if (!NT_SUCCESS(NBL->Status))
+        {
+            spinel_tid_t tid = (spinel_tid_t)(ULONG_PTR)NetBuffer->ProtocolReserved[1];
+            if (tid != 0)
+            {
+#ifdef COMMAND_INIT_RETRY
+                NT_ASSERT(pFilter->cmdInitTryCount < 9 || NBL->Status != NDIS_STATUS_PAUSED);
+#endif
+                otLwfCmdCancel(pFilter, NDIS_TEST_SEND_COMPLETE_AT_DISPATCH_LEVEL(SendCompleteFlags), tid);
+            }
+        }
+
+        NetBuffer->DataLength = (ULONG)(ULONG_PTR)NetBuffer->ProtocolReserved[0];
+        NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL);
+        NdisFreeNetBufferList(NBL);
+
+        // Release the command rundown protection
+        ExReleaseRundownProtection(&pFilter->cmdRundown);
+
+        NBL = NextNBL;
+    }
+
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+_Use_decl_annotations_
+VOID
+FilterSendNetBufferLists(
+    NDIS_HANDLE         FilterModuleContext,
+    PNET_BUFFER_LIST    NetBufferLists,
+    NDIS_PORT_NUMBER    PortNumber,
+    ULONG               SendFlags
+    )
+/*++
+
+Routine Description:
+
+    Send Net Buffer List handler
+    This function is an optional function for filter drivers. If provided, NDIS
+    will call this function to transmit a linked list of NetBuffers, described by a
+    NetBufferList, over the network. If this handler is NULL, NDIS will skip calling
+    this filter when sending a NetBufferList and will call the next lower
+    driver in the stack.  A filter that doesn't provide a FilerSendNetBufferList
+    handler can not originate a send on its own.
+
+Arguments:
+
+    FilterModuleContext     - our filter context area
+    NetBufferLists          - a List of NetBufferLists to send
+    PortNumber              - Port Number to which this send is targeted
+    SendFlags               - specifies if the call is at DISPATCH_LEVEL
+
+--*/
+{
+    PMS_FILTER          pFilter = (PMS_FILTER)FilterModuleContext;
+    BOOLEAN             DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags);
+
+    UNREFERENCED_PARAMETER(PortNumber);
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p, NBL: %p", FilterModuleContext, NetBufferLists);
+
+    // Try to grab a ref on the data path first, to make sure we are allowed
+    if (!ExAcquireRundownProtection(&pFilter->ExternalRefs))
+    {
+        LogVerbose(DRIVER_DEFAULT, "Failing SendNetBufferLists because data path isn't active.");
+
+        // Ignore any NBLs we get if we aren't active (can't get a ref)
+        PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+        while (CurrNbl)
+        {
+            NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_PAUSED;
+            CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+        }
+        NdisFSendNetBufferListsComplete(
+            pFilter->FilterHandle,
+            NetBufferLists,
+            DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0
+            );
+    }
+    else
+    {
+        if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+        {
+            // Indicate a new NBL to process on our worker thread
+            otLwfEventProcessingIndicateNewNetBufferLists(
+                pFilter,
+                DispatchLevel,
+                NetBufferLists
+                );
+        }
+        else
+        {
+            PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+            while (CurrNbl)
+            {
+                PNET_BUFFER CurrNb = NET_BUFFER_LIST_FIRST_NB(CurrNbl);
+                while (CurrNb)
+                {
+                    otLwfCmdSendIp6PacketAsync(pFilter, DispatchLevel, CurrNb, TRUE);
+                    CurrNb = NET_BUFFER_NEXT_NB(CurrNb);
+                }
+
+                NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_SUCCESS;
+                CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+            }
+
+            NdisFSendNetBufferListsComplete(
+                pFilter->FilterHandle,
+                NetBufferLists,
+                DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0
+                );
+        }
+
+        // Release the data path ref now
+        ExReleaseRundownProtection(&pFilter->ExternalRefs);
+    }
+    
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+_Use_decl_annotations_
+VOID
+FilterCancelSendNetBufferLists(
+    NDIS_HANDLE             FilterModuleContext,
+    PVOID                   CancelId
+    )
+/*++
+
+Routine Description:
+
+    This function cancels any NET_BUFFER_LISTs pended in the filter and then
+    calls the NdisFCancelSendNetBufferLists to propagate the cancel operation.
+
+    If your driver does not queue any send NBLs, you may omit this routine.
+    NDIS will propagate the cancelation on your behalf more efficiently.
+
+Arguments:
+
+    FilterModuleContext      - our filter context area.
+    CancelId                 - an identifier for all NBLs that should be dequeued
+
+*/
+{
+    PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p, CancelId: %p", FilterModuleContext, CancelId);
+
+    // Only cancel if we are 'Thread on Host', otherwise we do everything inline
+    if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+    {
+        otLwfEventProcessingIndicateNetBufferListsCancelled(pFilter, CancelId);
+    }
+
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+_Use_decl_annotations_
+VOID
+FilterReturnNetBufferLists(
+    NDIS_HANDLE         FilterModuleContext,
+    PNET_BUFFER_LIST    NetBufferLists,
+    ULONG               ReturnFlags
+    )
+/*++
+
+Routine Description:
+
+    FilterReturnNetBufferLists handler.
+    FilterReturnNetBufferLists is an optional function. If provided, NDIS calls
+    FilterReturnNetBufferLists to return the ownership of one or more NetBufferLists
+    and their embedded NetBuffers to the filter driver. If this handler is NULL, NDIS
+    will skip calling this filter when returning NetBufferLists to the underlying
+    miniport and will call the next lower driver in the stack. A filter that doesn't
+    provide a FilterReturnNetBufferLists handler cannot originate a receive indication
+    on its own.
+
+Arguments:
+
+    FilterInstanceContext       - our filter context area
+    NetBufferLists              - a linked list of NetBufferLists that this
+                                  filter driver indicated in a previous call to
+                                  NdisFIndicateReceiveNetBufferLists
+    ReturnFlags                 - flags specifying if the caller is at DISPATCH_LEVEL
+
+--*/
+{
+    PMS_FILTER          pFilter = (PMS_FILTER)FilterModuleContext;
+
+    UNREFERENCED_PARAMETER(ReturnFlags);
+    
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p, NBL: %p", pFilter, NetBufferLists);
+
+    PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+    while (CurrNbl)
+    {
+        if (!NT_SUCCESS(CurrNbl->Status))
+        {
+            LogVerbose(DRIVER_DATA_PATH, "NBL failed on return: %!STATUS!", CurrNbl->Status);
+        }
+
+        PNET_BUFFER_LIST NblToFree = CurrNbl;
+        PNET_BUFFER NbToFree = NET_BUFFER_LIST_FIRST_NB(NblToFree);
+
+        CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+        NET_BUFFER_LIST_NEXT_NBL(NblToFree) = NULL;
+
+        NdisAdvanceNetBufferDataStart(NbToFree, NET_BUFFER_DATA_LENGTH(NbToFree), TRUE, NULL);
+        NdisFreeNetBufferList(NblToFree);
+    }
+    
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+_Use_decl_annotations_
+VOID
+FilterReceiveNetBufferLists(
+    NDIS_HANDLE         FilterModuleContext,
+    PNET_BUFFER_LIST    NetBufferLists,
+    NDIS_PORT_NUMBER    PortNumber,
+    ULONG               NumberOfNetBufferLists,
+    ULONG               ReceiveFlags
+    )
+/*++
+
+Routine Description:
+
+    FilerReceiveNetBufferLists is an optional function for filter drivers.
+    If provided, this function processes receive indications made by underlying
+    NIC or lower level filter drivers. This function  can also be called as a
+    result of loopback. If this handler is NULL, NDIS will skip calling this
+    filter when processing a receive indication and will call the next higher
+    driver in the stack. A filter that doesn't provide a
+    FilterReceiveNetBufferLists handler cannot provide a
+    FilterReturnNetBufferLists handler and cannot a initiate an original receive
+    indication on its own.
+
+Arguments:
+
+    FilterModuleContext      - our filter context area.
+    NetBufferLists           - a linked list of NetBufferLists
+    PortNumber               - Port on which the receive is indicated
+    ReceiveFlags             -
+
+N.B.: It is important to check the ReceiveFlags in NDIS_TEST_RECEIVE_CANNOT_PEND.
+    This controls whether the receive indication is an synchronous or
+    asynchronous function call.
+
+--*/
+{
+
+    PMS_FILTER  pFilter = (PMS_FILTER)FilterModuleContext;
+    BOOLEAN     DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags);
+
+    UNREFERENCED_PARAMETER(PortNumber);
+    UNREFERENCED_PARAMETER(NumberOfNetBufferLists);
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p, NBL: %p", FilterModuleContext, NetBufferLists);
+
+    // Iterate through each NBL/NB and grab the data as a contiguous buffer to
+    // indicate to the Spinel command layer.
+    PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+    while (CurrNbl)
+    {
+        PNET_BUFFER CurrNb = NET_BUFFER_LIST_FIRST_NB(CurrNbl);
+        while (CurrNb)
+        {
+            PUCHAR Buffer = (PUCHAR)NdisGetDataBuffer(CurrNb, CurrNb->DataLength, NULL, 1, 0);
+            if (Buffer == NULL)
+            {
+                Buffer = (PUCHAR)FILTER_ALLOC_MEM(pFilter->FilterHandle, CurrNb->DataLength);
+                if (Buffer != NULL)
+                {
+                    PUCHAR _Buffer = (PUCHAR)NdisGetDataBuffer(CurrNb, CurrNb->DataLength, Buffer, 1, 0);
+                    NT_ASSERT(_Buffer == Buffer);
+                    if (_Buffer)
+                    {
+                        otLwfCmdRecveive(pFilter, DispatchLevel, Buffer, CurrNb->DataLength);
+                    }
+                    FILTER_FREE_MEM(Buffer);
+                }
+            }
+            else
+            {
+                otLwfCmdRecveive(pFilter, DispatchLevel, Buffer, CurrNb->DataLength);
+            }
+            CurrNb = NET_BUFFER_NEXT_NB(CurrNb);
+        }
+
+        NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_SUCCESS;
+        CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+    }
+
+    if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags))
+    {
+        NdisFReturnNetBufferLists(
+            pFilter->FilterHandle,
+            NetBufferLists,
+            DispatchLevel ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0
+            );
+    }
+
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+// Callback received from OpenThread when it has an IPv6 packet ready for
+// delivery to TCPIP.
+void 
+otLwfReceiveIp6DatagramCallback(
+    _In_ otMessage *aMessage,
+    _In_ void *aContext
+    )
+{
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    uint16_t messageLength = otMessageGetLength(aMessage);
+    PNET_BUFFER_LIST NetBufferList = NULL;
+    PNET_BUFFER NetBuffer = NULL;
+    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+    PUCHAR DataBuffer = NULL;
+    int BytesRead = 0;
+    IPV6_HEADER* v6Header;
+
+#ifdef FORCE_SYNCHRONOUS_RECEIVE
+    KIRQL irql;
+#endif
+
+    // Create the NetBufferList
+    NetBufferList =
+        NdisAllocateNetBufferAndNetBufferList(
+            pFilter->cmdNblPool,            // PoolHandle
+            0,                              // ContextSize
+            0,                              // ContextBackFill
+            NULL,                           // MdlChain
+            0,                              // DataOffset
+            0                               // DataLength
+            );
+    if (NetBufferList == NULL)
+    {
+        LogWarning(DRIVER_DEFAULT, "Failed to create Recv NetBufferList");
+        goto error;
+    }
+
+    // Set the flag to indicate its a IPv6 packet
+    NdisSetNblFlag(NetBufferList, NDIS_NBL_FLAGS_IS_IPV6);
+    NET_BUFFER_LIST_INFO(NetBufferList, NetBufferListFrameType) =
+        UlongToPtr(RtlUshortByteSwap(ETHERNET_TYPE_IPV6));
+
+    // Initialize NetBuffer fields
+    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
+    NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL;
+    NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_DATA_LENGTH(NetBuffer) = 0;
+    NET_BUFFER_DATA_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_FIRST_MDL(NetBuffer) = NULL;
+
+    // Allocate the NetBuffer for SendNetBufferList
+    Status = NdisRetreatNetBufferDataStart(NetBuffer, messageLength, 0, NULL);
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        NdisFreeNetBufferList(NetBufferList);
+        LogError(DRIVER_DEFAULT, "Failed to allocate NB for Recv NetBufferList, %!NDIS_STATUS!", Status);
+        goto error;
+    }
+
+    // Get the data buffer to write to
+    DataBuffer = NdisGetDataBuffer(NetBuffer, messageLength, NULL, 1, 0);
+    NT_ASSERT(DataBuffer);
+    if (DataBuffer == NULL)
+    {
+        NdisAdvanceNetBufferDataStart(NetBuffer, messageLength, TRUE, NULL);
+        NdisFreeNetBufferList(NetBufferList);
+        LogError(DRIVER_DEFAULT, "Failed to get contiguous data buffer for Recv NetBufferList");
+        goto error;
+    }
+
+    // Read the bytes to the buffer
+    BytesRead = otMessageRead(aMessage, 0, DataBuffer, messageLength);
+    NT_ASSERT(BytesRead == (int)messageLength);
+    if (BytesRead != (int)messageLength)
+    {
+        NdisAdvanceNetBufferDataStart(NetBuffer, messageLength, TRUE, NULL);
+        NdisFreeNetBufferList(NetBufferList);
+        LogError(DRIVER_DEFAULT, "Failed to read message buffer for Recv NetBufferList");
+        goto error;
+    }
+
+    v6Header = (IPV6_HEADER*)DataBuffer;
+    
+    // Filter messages to addresses we expose
+    if (!IN6_IS_ADDR_MULTICAST(&v6Header->DestinationAddress) &&
+        otLwfFindCachedAddrIndex(pFilter, &v6Header->DestinationAddress) == -1)
+    {
+        NdisAdvanceNetBufferDataStart(NetBuffer, messageLength, TRUE, NULL);
+        NdisFreeNetBufferList(NetBufferList);
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p dropping internal address message.", pFilter);
+        goto error;
+    }
+
+    // Filter internal Thread messages
+    if (v6Header->NextHeader == IPPROTO_UDP &&
+        messageLength >= sizeof(IPV6_HEADER) + sizeof(UDPHeader) &&
+        memcmp(&pFilter->otLinkLocalAddr, &v6Header->DestinationAddress, sizeof(IN6_ADDR)) == 0)
+    {
+        // Check for MLE message
+        UDPHeader* UdpHeader = (UDPHeader*)(v6Header + 1);
+        if (UdpHeader->DestinationPort == UdpHeader->SourcePort &&
+            UdpHeader->DestinationPort == RtlUshortByteSwap(19788)) // MLE Port
+        {
+            NdisAdvanceNetBufferDataStart(NetBuffer, messageLength, TRUE, NULL);
+            NdisFreeNetBufferList(NetBufferList);
+            LogVerbose(DRIVER_DATA_PATH, "Filter: %p dropping MLE message.", pFilter);
+            goto error;
+        }
+    }
+
+    LogVerbose(DRIVER_DATA_PATH, "Filter: %p, IP6_RECV: %p : %!IPV6ADDR! => %!IPV6ADDR! (%u bytes)", 
+               pFilter, NetBufferList, &v6Header->SourceAddress, &v6Header->DestinationAddress,
+               messageLength);
+
+#ifdef LOG_BUFFERS
+    otLogBuffer(DataBuffer, messageLength);
+#endif
+
+#ifdef FORCE_SYNCHRONOUS_RECEIVE
+    irql = KfRaiseIrql(DISPATCH_LEVEL);
+
+    if (messageLength == 248) // Magic length used for TAEF test packets
+    {
+        DbgBreakPoint();
+    }
+#endif
+
+    // Indicate the NBL up
+    NdisFIndicateReceiveNetBufferLists(
+        pFilter->FilterHandle,
+        NetBufferList,
+        NDIS_DEFAULT_PORT_NUMBER,
+        1,
+#ifdef FORCE_SYNCHRONOUS_RECEIVE
+        NDIS_RECEIVE_FLAGS_RESOURCES | NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL
+#else
+        0
+#endif
+        );
+    
+#ifdef FORCE_SYNCHRONOUS_RECEIVE
+    KeLowerIrql(irql);
+    FilterReturnNetBufferLists(pFilter, NetBufferList, 0);
+#endif
+
+error:
+
+    otMessageFree(aMessage);
+}
+
+// Called in response to receiving a Spinel Ip6 packet command
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void 
+otLwfTunReceiveIp6Packet(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ BOOLEAN Secure,
+    _In_reads_bytes_(BufferLength) const uint8_t* Buffer,
+    _In_ UINT BufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNET_BUFFER_LIST NetBufferList = NULL;
+    PNET_BUFFER NetBuffer = NULL;
+    PUCHAR DataBuffer = NULL;
+    IPV6_HEADER* v6Header;
+
+    UNREFERENCED_PARAMETER(Secure); // TODO - What should we do with unsecured packets?
+
+    NetBufferList =
+        NdisAllocateNetBufferAndNetBufferList(
+            pFilter->cmdNblPool,            // PoolHandle
+            0,                              // ContextSize
+            0,                              // ContextBackFill
+            NULL,                           // MdlChain
+            0,                              // DataOffset
+            0                               // DataLength
+            );
+    if (NetBufferList == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogWarning(DRIVER_DEFAULT, "Failed to create command NetBufferList");
+        goto exit;
+    }
+
+    // Set the flag to indicate its a IPv6 packet
+    NdisSetNblFlag(NetBufferList, NDIS_NBL_FLAGS_IS_IPV6);
+    NET_BUFFER_LIST_INFO(NetBufferList, NetBufferListFrameType) =
+        UlongToPtr(RtlUshortByteSwap(ETHERNET_TYPE_IPV6));
+
+    // Initialize NetBuffer fields
+    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
+    NET_BUFFER_CURRENT_MDL(NetBuffer) = NULL;
+    NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_DATA_LENGTH(NetBuffer) = 0;
+    NET_BUFFER_DATA_OFFSET(NetBuffer) = 0;
+    NET_BUFFER_FIRST_MDL(NetBuffer) = NULL;
+
+    // Allocate the NetBuffer for NetBufferList
+    if (NdisRetreatNetBufferDataStart(NetBuffer, BufferLength, 0, NULL) != NDIS_STATUS_SUCCESS)
+    {
+        NetBuffer = NULL;
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogError(DRIVER_DEFAULT, "Failed to allocate NB for command NetBufferList, %u bytes", BufferLength);
+        goto exit;
+    }
+
+    // Get the pointer to the data buffer for the header data
+    DataBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, BufferLength, NULL, 1, 0);
+    NT_ASSERT(DataBuffer);
+    
+    // Copy the data over
+    RtlCopyMemory(DataBuffer, Buffer, BufferLength);
+
+    v6Header = (IPV6_HEADER*)DataBuffer;
+
+    // Filter messages to addresses we expose
+    if (!IN6_IS_ADDR_MULTICAST(&v6Header->DestinationAddress) &&
+        otLwfFindCachedAddrIndex(pFilter, &v6Header->DestinationAddress) == -1)
+    {
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p dropping internal address message.", pFilter);
+        goto exit;
+    }
+
+    LogVerbose(DRIVER_DATA_PATH, "Filter: %p, IP6_RECV: %p : %!IPV6ADDR! => %!IPV6ADDR! (%u bytes)", 
+               pFilter, NetBufferList, &v6Header->SourceAddress, &v6Header->DestinationAddress,
+               BufferLength);
+
+#ifdef LOG_BUFFERS
+    otLogBuffer(DataBuffer, BufferLength);
+#endif
+
+    // Send the NBL down
+    NdisFIndicateReceiveNetBufferLists(
+        pFilter->FilterHandle, 
+        NetBufferList, 
+        NDIS_DEFAULT_PORT_NUMBER,
+        1,
+        DispatchLevel ? NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL : 0);
+
+    // Clear local variable because we don't own the NBL any more
+    NetBufferList = NULL;
+
+exit:
+
+    if (NetBufferList)
+    {
+        if (NetBuffer)
+        {
+            NdisAdvanceNetBufferDataStart(NetBuffer, NetBuffer->DataLength, TRUE, NULL);
+        }
+        NdisFreeNetBufferList(NetBufferList);
+    }
+}
diff --git a/examples/drivers/windows/otLwf/device.c b/examples/drivers/windows/otLwf/device.c
new file mode 100644
index 0000000..81188b1
--- /dev/null
+++ b/examples/drivers/windows/otLwf/device.c
@@ -0,0 +1,690 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "device.tmh"
+
+// IoControl Device Object from IoCreateDeviceSecure
+PDEVICE_OBJECT IoDeviceObject = NULL;
+
+// Global context for device control callbacks
+POTLWF_DEVICE_EXTENSION FilterDeviceExtension = NULL;
+
+/*
+
+Powershell script to generate security desciptors:
+
+$sddl = "D:P(A;;GA;;;SY)(A;;GA;;;NS)(A;;GA;;;BA)(A;;GA;;;WD)(A;;GA;;;S-1-15-3-3)"
+$blob = ([wmiclass]"Win32_SecurityDescriptorHelper").SDDLToBinarySD($sddl).BinarySD
+$string = [BitConverter]::ToString($blob)
+$string = $string -replace '-', ''
+$string = $string -replace '(..)(..)(..)(..)', '0x$4$3$2$1, '
+$string -replace '(.{10}, .{10}, .{10}, .{10},) ', "$&`n"
+
+*/
+const unsigned long c_sdThreadLwf[] =
+{
+    0x90040001, 0x00000000, 0x00000000, 0x00000000,
+    0x00000014, 0x00740002, 0x00000005, 0x00140000,
+    0x10000000, 0x00000101, 0x05000000, 0x00000012,
+    0x00140000, 0x10000000, 0x00000101, 0x05000000,
+    0x00000014, 0x00180000, 0x10000000, 0x00000201,
+    0x05000000, 0x00000020, 0x00000220, 0x00140000,
+    0x10000000, 0x00000101, 0x01000000, 0x00000000,
+    0x00180000, 0x10000000, 0x00000201, 0x0F000000,
+    0x00000003, 0x00000003
+};
+
+_No_competing_thread_
+INITCODE
+NDIS_STATUS
+otLwfRegisterDevice(
+    VOID
+    )
+{
+    NTSTATUS                        Status = NDIS_STATUS_SUCCESS;
+    UNICODE_STRING                  DeviceName;
+    UNICODE_STRING                  DeviceLinkUnicodeString;
+    PDEVICE_OBJECT                  DeviceObject;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    NT_ASSERT(FilterDeviceExtension == NULL);
+
+    NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING);
+    NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING);
+
+    Status = IoCreateDeviceSecure(FilterDriverObject,                     // DriverObject
+                                  sizeof(OTLWF_DEVICE_EXTENSION),         // DeviceExtension
+                                  &DeviceName,                            // DeviceName
+                                  FILE_DEVICE_NETWORK,                    // DeviceType
+                                  FILE_DEVICE_SECURE_OPEN,                // DeviceCharacteristics
+                                  FALSE,                                  // Exclusive
+                                  &SDDL_DEVOBJ_KERNEL_ONLY,               // security attributes
+                                  NULL,                                   // security override device class
+                                  &DeviceObject);                         // DeviceObject
+
+    if (NT_SUCCESS(Status))
+    {
+        DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+        Status = IoCreateSymbolicLink(&DeviceLinkUnicodeString, &DeviceName);
+
+        if (!NT_SUCCESS(Status))
+        {
+            LogError(DRIVER_DEFAULT, "IoCreateSymbolicLink failed, %!STATUS!", Status);
+            IoDeleteDevice(DeviceObject);
+        }
+        else
+        {
+            FilterDeviceExtension = (POTLWF_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+            RtlZeroMemory(FilterDeviceExtension, sizeof(OTLWF_DEVICE_EXTENSION));
+
+            FilterDeviceExtension->Signature = 'FTDR';
+            FilterDeviceExtension->Handle = FilterDriverHandle;
+
+            NdisAllocateSpinLock(&FilterDeviceExtension->Lock);
+            InitializeListHead(&FilterDeviceExtension->ClientList);
+
+            #pragma push
+            #pragma warning(disable:28168) // The function 'otLwfDispatch' does not have a _Dispatch_type_ annotation matching dispatch table position *
+            FilterDriverObject->MajorFunction[IRP_MJ_CREATE] = otLwfDispatch;
+            FilterDriverObject->MajorFunction[IRP_MJ_CLEANUP] = otLwfDispatch;
+            FilterDriverObject->MajorFunction[IRP_MJ_CLOSE] = otLwfDispatch;
+            FilterDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = otLwfDeviceIoControl;
+            #pragma pop
+
+            HANDLE fileHandle;
+            Status = ObOpenObjectByPointer(DeviceObject,
+                                           OBJ_KERNEL_HANDLE,
+                                           NULL,
+                                           WRITE_DAC,
+                                           0,
+                                           KernelMode,
+                                           &fileHandle);
+            if (NT_SUCCESS(Status))
+            {
+                Status = ZwSetSecurityObject(fileHandle, 
+                                             DACL_SECURITY_INFORMATION, 
+                                             (PSECURITY_DESCRIPTOR)c_sdThreadLwf);
+
+                if (!NT_SUCCESS(Status))
+                {
+                    LogError(DRIVER_DEFAULT, "ZwSetSecurityObject failed, %!STATUS!", Status);
+                }
+
+                ZwClose(fileHandle);
+            }
+            else
+            {
+                LogError(DRIVER_DEFAULT, "ObOpenObjectByPointer failed, %!STATUS!", Status);
+            }
+
+            IoDeviceObject = DeviceObject;
+        }
+    }
+    else
+    {
+        LogError(DRIVER_DEFAULT, "IoCreateDeviceSecure failed, %!STATUS!", Status);
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, Status);
+
+    return (NDIS_STATUS)Status;
+}
+
+PIRP
+otLwfDeviceClientCleanup(
+    POTLWF_DEVICE_CLIENT DeviceClient
+    )
+{
+    PIRP IrpToCancel = NULL;
+
+    // Clean the FileObject context
+    DeviceClient->FileObject->FsContext2 = NULL;
+
+    // Release pending IRP
+    if (DeviceClient->PendingNotificationIRP)
+    {
+        IrpToCancel = DeviceClient->PendingNotificationIRP;
+        DeviceClient->PendingNotificationIRP = NULL;
+    }
+
+    // Free all pending notifications
+    NT_ASSERT(DeviceClient->NotificationSize <= OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT);
+    for (UCHAR i = 0; i < DeviceClient->NotificationSize; i++)
+    {
+        UCHAR index = (DeviceClient->NotificationOffset + i) % OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT;
+        otLwfReleaseNotification(DeviceClient->PendingNotifications[index]);
+    }
+
+    return IrpToCancel;
+}
+
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfDeregisterDevice(
+    VOID
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    if (IoDeviceObject != NULL)
+    {
+        NT_ASSERT(FilterDeviceExtension);
+        NdisFreeSpinLock(&FilterDeviceExtension->Lock);
+        
+        // Clean up all pending clients
+        PLIST_ENTRY Link = FilterDeviceExtension->ClientList.Flink;
+        while (Link != &FilterDeviceExtension->ClientList)
+        {
+            POTLWF_DEVICE_CLIENT DeviceClient = CONTAINING_RECORD(Link, OTLWF_DEVICE_CLIENT, Link);
+            PIRP IrpToCancel = NULL;
+
+            // Set next link
+            Link = Link->Flink;
+            
+            // Make sure to clean up any left overs from the device client
+            IrpToCancel = otLwfDeviceClientCleanup(DeviceClient);
+
+            // Complete the pending IRP since we are shutting down
+            if (IrpToCancel)
+            {
+                // Before we are allowed to complete the pending IRP, we must remove the cancel routine
+                KIRQL irql;
+                IoAcquireCancelSpinLock(&irql);
+                IoSetCancelRoutine(IrpToCancel, NULL);
+                IoReleaseCancelSpinLock(irql);
+
+                IrpToCancel->IoStatus.Status = STATUS_CANCELLED;
+                IrpToCancel->IoStatus.Information = 0;
+                IoCompleteRequest(IrpToCancel, IO_NO_INCREMENT);
+            }
+
+            // Remove the device client from the list
+            RemoveEntryList(&DeviceClient->Link);
+
+            // Delete the device client
+            NdisFreeMemory(DeviceClient, 0, 0);
+        }
+
+        IoDeleteDevice(IoDeviceObject);
+    }
+
+    IoDeviceObject = NULL;
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_Use_decl_annotations_
+NTSTATUS
+otLwfDispatch(
+    PDEVICE_OBJECT       DeviceObject,
+    PIRP                 Irp
+    )
+{
+    PIO_STACK_LOCATION      IrpStack;
+    NTSTATUS                Status = STATUS_SUCCESS;
+    PIRP                    IrpToCancel = NULL;
+    POTLWF_DEVICE_CLIENT    DeviceClient = NULL;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+    NdisAcquireSpinLock(&FilterDeviceExtension->Lock);
+
+    switch (IrpStack->MajorFunction)
+    {
+        case IRP_MJ_CREATE:
+            LogInfo(DRIVER_IOCTL, "Client %p attached.", IrpStack->FileObject);
+
+            if (FilterDeviceExtension->ClientListSize >= OTLWF_MAX_CLIENTS)
+            {
+                LogError(DRIVER_IOCTL, "Already have max clients!");
+                Status = STATUS_TOO_MANY_SESSIONS;
+                break;
+            }
+
+            DeviceClient = FILTER_ALLOC_DEVICE_CLIENT();
+            if (DeviceClient)
+            {
+                RtlZeroMemory(DeviceClient, sizeof(OTLWF_DEVICE_CLIENT));
+                DeviceClient->FileObject = IrpStack->FileObject;
+
+                NT_ASSERT(IrpStack->FileObject->FsContext2 == NULL);
+                IrpStack->FileObject->FsContext2 = DeviceClient;
+                
+                // Insert into the client list
+                InsertTailList(&FilterDeviceExtension->ClientList, &DeviceClient->Link);
+                FilterDeviceExtension->ClientListSize++;
+            }
+            else
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+            }
+            break;
+
+        case IRP_MJ_CLEANUP:
+            LogInfo(DRIVER_IOCTL, "Client %p cleaning up.", IrpStack->FileObject);
+
+            DeviceClient = (POTLWF_DEVICE_CLIENT)IrpStack->FileObject->FsContext2;
+
+            // Make sure to clean up any left overs from the device client
+            IrpToCancel = otLwfDeviceClientCleanup(DeviceClient);
+
+            // Remove the device client from the list
+            RemoveEntryList(&DeviceClient->Link);
+            FilterDeviceExtension->ClientListSize--;
+
+            // Delete the device client
+            NdisFreeMemory(DeviceClient, 0, 0);
+            break;
+
+        case IRP_MJ_CLOSE:
+            LogInfo(DRIVER_IOCTL, "Client %p detatched.", IrpStack->FileObject);
+            break;
+
+        default:
+            break;
+    }
+
+    NdisReleaseSpinLock(&FilterDeviceExtension->Lock);
+
+    // Cancel the pending notification IRP if set
+    if (IrpToCancel)
+    {
+        // Complete the pending IRP
+        IrpToCancel->IoStatus.Status = STATUS_CANCELLED;
+        IrpToCancel->IoStatus.Information = 0;
+        IoCompleteRequest(IrpToCancel, IO_NO_INCREMENT);
+    }
+
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    LogFuncExitNT(DRIVER_IOCTL, Status);
+
+    return Status;
+}
+
+_Use_decl_annotations_
+NTSTATUS
+otLwfDeviceIoControl(
+    PDEVICE_OBJECT        DeviceObject,
+    PIRP                  Irp
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    BOOLEAN CompleteIRP = TRUE;
+
+    PVOID               IoBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+    PIO_STACK_LOCATION  IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    ULONG               InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+    ULONG               OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+    ULONG               IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+    ULONG               FuncCode = (IoControlCode >> 2) & 0xFFF;
+
+    LogFuncEntryMsg(DRIVER_IOCTL, "%p", IrpSp->FileObject);
+
+#if DBG
+    ASSERT(((POTLWF_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Signature == 'FTDR');
+#else
+    UNREFERENCED_PARAMETER(DeviceObject);
+#endif
+
+    // We only allow PASSIVE_LEVEL calls
+    if (KeGetCurrentIrql() > PASSIVE_LEVEL)
+    {
+        LogWarning(DRIVER_IOCTL, "FilterDeviceIoControl called higher than PASSIVE.");
+        status = STATUS_NOT_SUPPORTED;
+        RtlZeroMemory(IoBuffer, OutputBufferLength);
+        OutputBufferLength = 0;
+        goto error;
+    }
+
+    if (FuncCode >= MIN_OTLWF_IOCTL_FUNC_CODE && FuncCode <= MAX_OTLWF_IOCTL_FUNC_CODE)
+    {
+        CompleteIRP = FALSE;
+        status = otLwfIoCtlOpenThreadControl(Irp);
+        goto error;
+    }
+
+    // Check the IoControlCode to determine which IOCTL we are processing
+    switch (IoControlCode)
+    {
+        case IOCTL_OTLWF_QUERY_NOTIFICATION:
+            CompleteIRP = FALSE;
+            status = otLwfQueryNextNotification(Irp);
+            break;
+
+        case IOCTL_OTLWF_ENUMERATE_DEVICES:
+            status =
+                otLwfIoCtlEnumerateInterfaces(
+                    IoBuffer, InputBufferLength,
+                    IoBuffer, &OutputBufferLength
+                    );
+            break;
+
+        case IOCTL_OTLWF_QUERY_DEVICE:
+            status =
+                otLwfIoCtlQueryInterface(
+                    IoBuffer, InputBufferLength,
+                    IoBuffer, &OutputBufferLength
+                    );
+            break;
+
+        default:
+            status = STATUS_NOT_IMPLEMENTED;
+            RtlZeroMemory(IoBuffer, OutputBufferLength);
+            OutputBufferLength = 0;
+            break;
+    }
+
+error:
+
+    if (CompleteIRP)
+    {
+        Irp->IoStatus.Status = status;
+        Irp->IoStatus.Information = OutputBufferLength;
+
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    LogFuncExitNT(DRIVER_IOCTL, status);
+
+    return status;
+}
+
+_Use_decl_annotations_
+PMS_FILTER
+otLwfFindAndRefInterface(
+    _In_ PGUID  InterfaceGuid
+    )
+{
+    PMS_FILTER pOutput = NULL;
+
+    NdisAcquireSpinLock(&FilterListLock);
+
+    for (PLIST_ENTRY Link = FilterModuleList.Flink; Link != &FilterModuleList; Link = Link->Flink)
+    {
+        PMS_FILTER pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
+
+        if (pFilter->State == FilterRunning &&
+            memcmp(InterfaceGuid, &pFilter->InterfaceGuid, sizeof(GUID)) == 0)
+        {
+            if (ExAcquireRundownProtection(&pFilter->ExternalRefs))
+            {
+                pOutput = pFilter;
+            }
+            break;
+        }
+    }
+
+    NdisReleaseSpinLock(&FilterListLock);
+
+    return pOutput;
+}
+
+//
+// Notification Functions
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfReleaseNotification(
+    _In_ PFILTER_NOTIFICATION_ENTRY NotifEntry
+    )
+{
+    if (RtlDecrementReferenceCount(&NotifEntry->RefCount))
+    {
+        NdisFreeMemory(NotifEntry, 0, 0);
+    }
+}
+
+// Indicates a new notification
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfIndicateNotification(
+    _In_ PFILTER_NOTIFICATION_ENTRY NotifEntry
+    )
+{
+    PIRP IrpsToComplete[OTLWF_MAX_CLIENTS] = {0};
+    UCHAR IrpOffset = 0;
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    // Initialize with a local ref
+    NotifEntry->RefCount = 1;
+
+    if (FilterDeviceExtension == NULL) goto error;
+
+    NdisAcquireSpinLock(&FilterDeviceExtension->Lock);
+
+    // Pend the notification for each client
+    PLIST_ENTRY Link = FilterDeviceExtension->ClientList.Flink;
+    while (Link != &FilterDeviceExtension->ClientList)
+    {
+        POTLWF_DEVICE_CLIENT DeviceClient = CONTAINING_RECORD(Link, OTLWF_DEVICE_CLIENT, Link);
+
+        // Set next link
+        Link = Link->Flink;
+        
+        KIRQL irql;
+        IoAcquireCancelSpinLock(&irql);
+
+        // If there are other pending notifications or we don't have a pending IRP saved
+        // then just go ahead and add the notification to the list
+        NT_ASSERT(DeviceClient->NotificationSize <= OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT);
+        if (DeviceClient->NotificationSize != 0 ||
+            DeviceClient->PendingNotificationIRP == NULL
+            )
+        {
+            // Calculate the next index
+            UCHAR Index = (DeviceClient->NotificationOffset + DeviceClient->NotificationSize) % OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT;
+
+            // Add additional ref to the notif
+            RtlIncrementReferenceCount(&NotifEntry->RefCount);
+
+            // If we are at the max already, release the oldest
+            if (DeviceClient->NotificationSize == OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT)
+            {
+                LogWarning(DRIVER_IOCTL, "Dropping old notification!");
+                otLwfReleaseNotification(DeviceClient->PendingNotifications[DeviceClient->NotificationOffset]);
+                DeviceClient->NotificationOffset = (DeviceClient->NotificationOffset + 1) % OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT;
+            }
+            else
+            {
+                DeviceClient->NotificationSize++;
+            }
+
+            // Copy the notification to the next space
+            DeviceClient->PendingNotifications[Index] = NotifEntry;
+        }
+        else
+        {
+            // Before we are allowed to complete the pending IRP, we must remove the cancel routine
+            IoSetCancelRoutine(DeviceClient->PendingNotificationIRP, NULL);
+
+            IrpsToComplete[IrpOffset] = DeviceClient->PendingNotificationIRP;
+            IrpOffset++;
+
+            DeviceClient->PendingNotificationIRP = NULL;
+        }
+        
+        // Release the cancel spin lock
+        IoReleaseCancelSpinLock(irql);
+    }
+
+    NdisReleaseSpinLock(&FilterDeviceExtension->Lock);
+
+    // Complete any IRPs now, outside the lock
+    for (UCHAR i = 0; i < IrpOffset; i++)
+    {
+        PIRP IrpToComplete = IrpsToComplete[i];
+
+        // Copy the notification payload
+        PVOID IoBuffer = IrpToComplete->AssociatedIrp.SystemBuffer;
+        memcpy(IoBuffer, &NotifEntry->Notif, sizeof(OTLWF_NOTIFICATION));
+        IrpToComplete->IoStatus.Information = sizeof(OTLWF_NOTIFICATION);
+
+        // Complete the IRP
+        IrpToComplete->IoStatus.Status = STATUS_SUCCESS;
+        IoCompleteRequest(IrpToComplete, IO_NO_INCREMENT);
+    }
+
+error:
+    
+    // Release local ref on the notification
+    otLwfReleaseNotification(NotifEntry);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+DRIVER_CANCEL otLwfQueryNotificationCancelled;
+
+_Use_decl_annotations_
+VOID
+otLwfQueryNotificationCancelled(
+    _Inout_ PDEVICE_OBJECT DeviceObject,
+    _Inout_ _IRQL_uses_cancel_ struct _IRP *Irp
+)
+{
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    LogFuncEntry(DRIVER_IOCTL);
+    
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    POTLWF_DEVICE_CLIENT DeviceClient = (POTLWF_DEVICE_CLIENT)IrpSp->FileObject->FsContext2;
+
+    if (DeviceClient)
+    {
+        DeviceClient->PendingNotificationIRP = NULL;
+    }
+
+    IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+    Irp->IoStatus.Status = STATUS_CANCELLED;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+// Queries the next notification
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfQueryNextNotification(
+    _In_ PIRP Irp
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    POTLWF_DEVICE_CLIENT DeviceClient = NULL;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = NULL;
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+    ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+    // Validate we have a big enough buffer
+    if (OutputBufferLength < sizeof(OTLWF_NOTIFICATION))
+    {
+        RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, OutputBufferLength);
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto error;
+    }
+    
+    DeviceClient = (POTLWF_DEVICE_CLIENT)IrpSp->FileObject->FsContext2;
+    if (DeviceClient == NULL)
+    {
+        status = STATUS_DEVICE_NOT_READY;
+        goto error;
+    }
+
+    NdisAcquireSpinLock(&FilterDeviceExtension->Lock);
+
+    // Check to see if there are any notifications available
+    if (DeviceClient->NotificationSize == 0)
+    {
+        // Set the cancel routine
+        IoSetCancelRoutine(Irp, otLwfQueryNotificationCancelled);
+
+        // Mark the Irp as pending
+        IoMarkIrpPending(Irp);
+
+        // Save the IRP to complete later, when we have a notification
+        DeviceClient->PendingNotificationIRP = Irp;
+    }
+    else
+    {
+        // Get the notification
+        NotifEntry = DeviceClient->PendingNotifications[DeviceClient->NotificationOffset];
+        DeviceClient->PendingNotifications[DeviceClient->NotificationOffset] = NULL;
+
+        // Increment the offset and decrement the size
+        DeviceClient->NotificationOffset = (DeviceClient->NotificationOffset + 1) % OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT;
+        DeviceClient->NotificationSize--;
+    }
+
+    NdisReleaseSpinLock(&FilterDeviceExtension->Lock);
+
+    // If we found a notification, complete the IRP with it
+    if (NotifEntry)
+    {
+        // Copy the notification payload
+        PVOID IoBuffer = Irp->AssociatedIrp.SystemBuffer;
+        memcpy(IoBuffer, &NotifEntry->Notif, sizeof(OTLWF_NOTIFICATION));
+        Irp->IoStatus.Information = sizeof(OTLWF_NOTIFICATION);
+
+        // Free the notification
+        otLwfReleaseNotification(NotifEntry);
+    }
+    else
+    {
+        // Otherwise, set status to indicate we are pending the IRP
+        status = STATUS_PENDING;
+    }
+
+error:
+
+    // Complete the IRP if we aren't pending
+    if (status != STATUS_PENDING)
+    {
+        Irp->IoStatus.Status = status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    LogFuncExitNT(DRIVER_IOCTL, status);
+
+    return status;
+}
diff --git a/examples/drivers/windows/otLwf/device.h b/examples/drivers/windows/otLwf/device.h
new file mode 100644
index 0000000..5bba716
--- /dev/null
+++ b/examples/drivers/windows/otLwf/device.h
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the functions for managing the device IOCTL interface.
+ */
+
+#ifndef _DEVICE_H
+#define _DEVICE_H
+
+//
+// The filter needs to handle IOCTRLs
+//
+#define LINKNAME_STRING             L"\\DosDevices\\otLwf"
+#define NTDEVICE_STRING             L"\\Device\\otLwf"
+
+// The maximum number of simultaneous clients supported
+#define OTLWF_MAX_CLIENTS   10
+
+// The maximum number of notifications allowed to be pended, per client
+#define OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT  100
+
+// Context for IO Device Control callbacks
+typedef struct _OTLWF_DEVICE_EXTENSION
+{
+    ULONG           Signature;
+    NDIS_HANDLE     Handle;
+
+    NDIS_SPIN_LOCK  Lock;
+    _Guarded_by_(Lock)
+    LIST_ENTRY      ClientList;
+    ULONG           ClientListSize;
+
+} OTLWF_DEVICE_EXTENSION, *POTLWF_DEVICE_EXTENSION;
+
+// Notification structure
+typedef struct _FILTER_NOTIFICATION_ENTRY
+{
+    RTL_REFERENCE_COUNT RefCount;
+    OTLWF_NOTIFICATION  Notif;
+
+} FILTER_NOTIFICATION_ENTRY, *PFILTER_NOTIFICATION_ENTRY;
+
+// Tag for allocating notification structures 'TNtf
+#define FILTER_NOTIF_ALLOC_TAG 'ftNT'
+
+// Helper to allocate a new notification entry
+#define FILTER_ALLOC_NOTIF(_pFilter) \
+    (PFILTER_NOTIFICATION_ENTRY)NdisAllocateMemoryWithTagPriority(_pFilter->FilterHandle, sizeof(FILTER_NOTIFICATION_ENTRY), FILTER_NOTIF_ALLOC_TAG, NormalPoolPriority)
+
+// Context for IO Device Control clients
+typedef struct _OTLWF_DEVICE_CLIENT
+{
+    LIST_ENTRY                  Link;
+    PFILE_OBJECT                FileObject;
+    PIRP                        PendingNotificationIRP;
+    PFILTER_NOTIFICATION_ENTRY  PendingNotifications[OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT];
+    UCHAR                       NotificationOffset;
+    UCHAR                       NotificationSize;
+
+} OTLWF_DEVICE_CLIENT, *POTLWF_DEVICE_CLIENT;
+
+// Helper to allocate a new Device Control client
+#define FILTER_ALLOC_DEVICE_CLIENT() \
+    (POTLWF_DEVICE_CLIENT)NdisAllocateMemoryWithTagPriority(FilterDeviceExtension->Handle, sizeof(OTLWF_DEVICE_CLIENT), FILTER_NOTIF_ALLOC_TAG, NormalPoolPriority)
+
+static_assert(
+    (1 << (sizeof(UCHAR) * 8)) > OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT, 
+    "Type of NotificationOffset must be big enough for OTLWF_MAX_PENDING_NOTIFICATIONS_PER_CLIENT"
+    );
+
+// Global context for device control callbacks
+extern POTLWF_DEVICE_EXTENSION FilterDeviceExtension;
+
+//
+// Function prototypes
+//
+
+// Registers for Io Control callbacks
+_No_competing_thread_
+INITCODE
+NDIS_STATUS
+otLwfRegisterDevice(
+    VOID
+    );
+
+// Unregisters for Io Control Callbacks
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfDeregisterDevice(
+    VOID
+    );
+
+// Callback for general control IRPs
+DRIVER_DISPATCH otLwfDispatch;
+
+// Callback for IOCTLs
+DRIVER_DISPATCH otLwfDeviceIoControl;
+
+// Attempts to find and add a reference to the Thread interface
+_IRQL_requires_max_(PASSIVE_LEVEL)
+PMS_FILTER
+otLwfFindAndRefInterface(
+    _In_ PGUID  InterfaceGuid
+    );
+
+//
+// Notification Type and Functions
+//
+
+// Indicates a new notification
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfIndicateNotification(
+    _In_ PFILTER_NOTIFICATION_ENTRY NotifEntry
+    );
+
+// Queries the next notification
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfQueryNextNotification(
+    _In_ PIRP Irp
+    );
+
+// Release a ref on the notification
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfReleaseNotification(
+    _In_ PFILTER_NOTIFICATION_ENTRY NotifEntry
+    );
+
+#endif // _DEVICE_H
diff --git a/examples/drivers/windows/otLwf/driver.c b/examples/drivers/windows/otLwf/driver.c
new file mode 100644
index 0000000..f4210e1
--- /dev/null
+++ b/examples/drivers/windows/otLwf/driver.c
@@ -0,0 +1,242 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "driver.tmh"
+
+//
+// Global variables
+//
+
+// Global Driver Object from DriverEntry
+PDRIVER_OBJECT      FilterDriverObject = NULL;
+
+// NDIS Filter handle from NdisFRegisterFilterDriver
+NDIS_HANDLE         FilterDriverHandle = NULL;
+
+// Global list of THREAD_FILTER instances
+NDIS_SPIN_LOCK      FilterListLock;
+LIST_ENTRY          FilterModuleList;
+
+// Cached performance frequency of the system
+LARGE_INTEGER       FilterPerformanceFrequency;
+
+INITCODE
+_Use_decl_annotations_
+NTSTATUS
+DriverEntry(
+    _In_ PDRIVER_OBJECT     DriverObject,
+    _In_ PUNICODE_STRING    RegistryPath
+    )
+/*++
+
+Routine Description:
+
+    First entry point to be called, when this driver is loaded.
+    Register with NDIS as a filter driver and create a device
+    for communication with user-mode.
+
+Arguments:
+
+    DriverObject - pointer to the system's driver object structure
+    for this driver
+
+    RegistryPath - system's registry path for this driver
+
+Return Value:
+
+    STATUS_SUCCESS if all initialization is successful, STATUS_XXX
+    error code if not.
+
+--*/
+{
+    NDIS_STATUS Status;
+
+    // Initialize WPP logging
+    WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+    // Save global DriverObject
+    FilterDriverObject = DriverObject;
+
+    // Set the driver unload handler
+    DriverObject->DriverUnload = DriverUnload;
+
+    // Cache performance counter frequency
+    (VOID)KeQueryPerformanceCounter(&FilterPerformanceFrequency);
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Registry: %S", RegistryPath->Buffer);
+
+    do
+    {
+        NDIS_FILTER_DRIVER_CHARACTERISTICS  FChars = 
+        {
+            {
+                NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS,
+#if NDIS_SUPPORT_NDIS61
+                NDIS_FILTER_CHARACTERISTICS_REVISION_2,
+#else
+                NDIS_FILTER_CHARACTERISTICS_REVISION_1,
+#endif
+                sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS)
+            },
+            NDIS_FILTER_MAJOR_VERSION,
+            NDIS_FILTER_MINOR_VERSION,
+            1,
+            0,
+            0,
+            RTL_CONSTANT_STRING(FILTER_FRIENDLY_NAME),
+            RTL_CONSTANT_STRING(FILTER_UNIQUE_NAME),
+            RTL_CONSTANT_STRING(FILTER_SERVICE_NAME),
+
+            NULL,
+            NULL,
+            FilterAttach,
+            FilterDetach,
+            FilterRestart,
+            FilterPause,
+            FilterSendNetBufferLists,
+            FilterSendNetBufferListsComplete,
+            FilterCancelSendNetBufferLists,
+            FilterReceiveNetBufferLists,
+            FilterReturnNetBufferLists,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            FilterStatus,
+#if (NDIS_SUPPORT_NDIS61)
+            NULL,
+            NULL,
+            NULL,
+#endif
+        };
+
+        //
+        // Initialize global variables
+        //
+        NdisAllocateSpinLock(&FilterListLock);
+        InitializeListHead(&FilterModuleList);
+        
+        //
+        // Register the filter with NDIS
+        //
+        Status = 
+            NdisFRegisterFilterDriver(
+                DriverObject,
+                (NDIS_HANDLE)FilterDriverObject,
+                &FChars,
+                &FilterDriverHandle
+                );
+        if (Status != NDIS_STATUS_SUCCESS)
+        {
+            LogError(DRIVER_DEFAULT, "Register filter driver failed, %!NDIS_STATUS!", Status);
+            break;
+        }
+
+        //
+        // Register the device IOCTL interface
+        //
+        Status = otLwfRegisterDevice();
+        if (Status != NDIS_STATUS_SUCCESS)
+        {
+            LogError(DRIVER_DEFAULT, "Register device for the filter driver failed, %!NDIS_STATUS!", Status);
+            break;
+        }
+
+    } while (FALSE);
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        if (FilterDriverHandle)
+        {
+            NdisFDeregisterFilterDriver(FilterDriverHandle);
+            FilterDriverHandle = NULL;
+        }
+        WPP_CLEANUP(DriverObject);
+    }
+
+    return Status;
+}
+
+PAGEDX
+_Use_decl_annotations_
+VOID
+DriverUnload(
+    _In_ PDRIVER_OBJECT     DriverObject
+    )
+/*++
+
+Routine Description:
+
+    Filter driver's unload routine.
+    Deregister the driver from NDIS.
+
+Arguments:
+
+    DriverObject - pointer to the system's driver object structure
+                   for this driver
+
+Return Value:
+
+    NONE
+
+--*/
+{
+    PAGED_CODE();
+    
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    //
+    // Clean up the device IOCTL interface
+    //
+    otLwfDeregisterDevice();
+
+    //
+    // Deregister the NDIS filter
+    //
+    NdisFDeregisterFilterDriver(FilterDriverHandle);
+    FilterDriverHandle = NULL;
+
+    // Validate we have no outstanding filter instances
+    NT_ASSERT(IsListEmpty(&FilterModuleList));
+
+    //
+    // Clean up global variables
+    //
+    NdisFreeSpinLock(&FilterListLock);
+
+    LogFuncExit(DRIVER_DEFAULT);
+
+    //
+    // Clean up WPP logging
+    //
+    WPP_CLEANUP(DriverObject);
+}
diff --git a/examples/drivers/windows/otLwf/driver.h b/examples/drivers/windows/otLwf/driver.h
new file mode 100644
index 0000000..ddc1cd0
--- /dev/null
+++ b/examples/drivers/windows/otLwf/driver.h
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the top-level functions and variables for driver initialization
+ *  and clean up.
+ */
+
+#ifndef _DRIVER_H
+#define _DRIVER_H
+
+// Legal values include:
+//    6.0  Available starting with Windows Vista RTM
+//    6.1  Available starting with Windows Vista SP1 / Windows Server 2008
+//    6.20 Available starting with Windows 7 / Windows Server 2008 R2
+//    6.30 Available starting with Windows 8 / Windows Server "8"
+#define FILTER_MAJOR_NDIS_VERSION   6
+
+#if defined(NDIS60)
+#define FILTER_MINOR_NDIS_VERSION   0
+#elif defined(NDIS620)
+#define FILTER_MINOR_NDIS_VERSION   20
+#elif defined(NDIS630)
+#define FILTER_MINOR_NDIS_VERSION   30
+#endif
+
+//
+// Global variables
+//
+
+// Global Driver Object from DriverEntry
+extern PDRIVER_OBJECT      FilterDriverObject;
+
+// NDIS Filter handle from NdisFRegisterFilterDriver
+extern NDIS_HANDLE         FilterDriverHandle;
+
+// IoControl Device Object from IoCreateDeviceSecure
+extern PDEVICE_OBJECT      IoDeviceObject;
+
+// Global list of THREAD_FILTER instances
+extern NDIS_SPIN_LOCK      FilterListLock;
+extern LIST_ENTRY          FilterModuleList;
+
+// Cached performance frequency of the system
+extern LARGE_INTEGER       FilterPerformanceFrequency;
+
+#define FILTER_FRIENDLY_NAME        L"OpenThread NDIS LightWeight Filter"
+#define FILTER_UNIQUE_NAME          L"{B3A3845A-164E-4727-B12E-32B8DCE1F6CD}" //unique name, quid name
+#define FILTER_SERVICE_NAME         L"OTLWF"
+
+//
+// Function prototypes
+//
+INITCODE DRIVER_INITIALIZE DriverEntry;
+
+PAGEDX DRIVER_UNLOAD DriverUnload;
+
+#endif // _DRIVER_H
diff --git a/examples/drivers/windows/otLwf/eventprocessing.c b/examples/drivers/windows/otLwf/eventprocessing.c
new file mode 100644
index 0000000..2e9eebe
--- /dev/null
+++ b/examples/drivers/windows/otLwf/eventprocessing.c
@@ -0,0 +1,1114 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the functions for creating new notifications for IOCTL clients.
+ */
+
+#include "precomp.h"
+#include "eventprocessing.tmh"
+
+typedef struct _OTLWF_ADDR_EVENT
+{
+    LIST_ENTRY              Link;
+    MIB_NOTIFICATION_TYPE   NotificationType;
+    IN6_ADDR                Address;
+
+} OTLWF_ADDR_EVENT, *POTLWF_ADDR_EVENT;
+
+typedef struct _OTLWF_NBL_EVENT
+{
+    LIST_ENTRY          Link;
+    PNET_BUFFER_LIST    NetBufferLists;
+
+} OTLWF_NBL_EVENT, *POTLWF_NBL_EVENT;
+
+typedef struct _OTLWF_MAC_FRAME_EVENT
+{
+    LIST_ENTRY          Link;
+    uint8_t             BufferLength;
+    uint8_t             Buffer[0];
+
+} OTLWF_MAC_FRAME_EVENT, *POTLWF_MAC_FRAME_EVENT;
+
+
+KSTART_ROUTINE otLwfEventWorkerThread;
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfCompleteNBLs(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_ PNET_BUFFER_LIST       NetBufferLists,
+    _In_ NTSTATUS               Status
+    );
+
+// Starts the event queue processing
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfEventProcessingStart(
+    _In_ PMS_FILTER             pFilter
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    HANDLE   threadHandle = NULL;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p, TimeIncrement = %u", pFilter, KeQueryTimeIncrement());
+    
+    pFilter->NextAlarmTickCount.QuadPart = 0;
+
+    NT_ASSERT(pFilter->EventWorkerThread == NULL);
+    if (pFilter->EventWorkerThread != NULL)
+    {
+        status = STATUS_ALREADY_REGISTERED;
+        goto error;
+    }
+
+    // Make sure to reset the necessary events
+    KeResetEvent(&pFilter->EventWorkerThreadStopEvent);
+    KeResetEvent(&pFilter->SendNetBufferListComplete);
+    KeResetEvent(&pFilter->EventWorkerThreadEnergyScanComplete);
+
+    // Start the worker thread
+    status = PsCreateSystemThread(
+                &threadHandle,                  // ThreadHandle
+                THREAD_ALL_ACCESS,              // DesiredAccess
+                NULL,                           // ObjectAttributes
+                NULL,                           // ProcessHandle
+                NULL,                           // ClientId
+                otLwfEventWorkerThread,         // StartRoutine
+                pFilter                         // StartContext
+                );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "PsCreateSystemThread failed, %!STATUS!", status);
+        goto error;
+    }
+
+    // Grab the object reference to the worker thread
+    status = ObReferenceObjectByHandle(
+                threadHandle,
+                THREAD_ALL_ACCESS,
+                *PsThreadType,
+                KernelMode,
+                &pFilter->EventWorkerThread,
+                NULL
+                );
+    if (!NT_VERIFYMSG("ObReferenceObjectByHandle can't fail with a valid kernel handle", NT_SUCCESS(status)))
+    {
+        LogError(DRIVER_DEFAULT, "ObReferenceObjectByHandle failed, %!STATUS!", status);
+        KeSetEvent(&pFilter->EventWorkerThreadStopEvent, IO_NO_INCREMENT, FALSE);
+    }
+
+    ZwClose(threadHandle);
+
+error:
+
+    if (!NT_SUCCESS(status))
+    {
+        ExSetTimerResolution(0, FALSE);
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+// Stops the event queue processing
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingStop(
+    _In_ PMS_FILTER             pFilter
+    )
+{
+    PLIST_ENTRY Link = NULL;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", pFilter);
+
+    // By this point, we have disabled the Data Path, so no more 
+    // NBLs should be queued up.
+
+    // Clean up worker thread
+    if (pFilter->EventWorkerThread)
+    {
+        LogInfo(DRIVER_DEFAULT, "Stopping event processing worker thread and waiting for it to complete.");
+
+        // Send event to shutdown worker thread
+        KeSetEvent(&pFilter->EventWorkerThreadStopEvent, 0, FALSE);
+
+        // Wait for worker thread to finish
+        KeWaitForSingleObject(
+            pFilter->EventWorkerThread,
+            Executive,
+            KernelMode,
+            FALSE,
+            NULL
+            );
+
+        // Free worker thread
+        ObDereferenceObject(pFilter->EventWorkerThread);
+        pFilter->EventWorkerThread = NULL;
+
+        LogInfo(DRIVER_DEFAULT, "Event processing worker thread cleaned up.");
+    }
+
+    // Clean up any left over events
+    if (pFilter->AddressChangesHead.Flink)
+    {
+        Link = pFilter->AddressChangesHead.Flink;
+        while (Link != &pFilter->AddressChangesHead)
+        {
+            POTLWF_ADDR_EVENT Event = CONTAINING_RECORD(Link, OTLWF_ADDR_EVENT, Link);
+            Link = Link->Flink;
+
+            // Delete the event
+            NdisFreeMemory(Event, 0, 0);
+        }
+    }
+
+    // Clean up any left over events
+    if (pFilter->NBLsHead.Flink)
+    {
+        Link = pFilter->NBLsHead.Flink;
+        while (Link != &pFilter->NBLsHead)
+        {
+            POTLWF_NBL_EVENT Event = CONTAINING_RECORD(Link, OTLWF_NBL_EVENT, Link);
+            Link = Link->Flink;
+
+            otLwfCompleteNBLs(pFilter, FALSE, Event->NetBufferLists, STATUS_CANCELLED);
+
+            // Delete the event
+            NdisFreeMemory(Event, 0, 0);
+        }
+    }
+
+    // Clean up any left over events
+    if (pFilter->MacFramesHead.Flink)
+    {
+        Link = pFilter->MacFramesHead.Flink;
+        while (Link != &pFilter->MacFramesHead)
+        {
+            POTLWF_MAC_FRAME_EVENT Event = CONTAINING_RECORD(Link, OTLWF_MAC_FRAME_EVENT, Link);
+            Link = Link->Flink;
+
+            // Delete the event
+            NdisFreeMemory(Event, 0, 0);
+        }
+    }
+
+    // Reinitialize the list head
+    InitializeListHead(&pFilter->AddressChangesHead);
+    InitializeListHead(&pFilter->NBLsHead);
+    InitializeListHead(&pFilter->MacFramesHead);
+    
+    if (pFilter->EventIrpListHead.Flink)
+    {
+        FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, FALSE);
+
+        // Clean up any left over IRPs
+        Link = pFilter->EventIrpListHead.Flink;
+        while (Link != &pFilter->EventIrpListHead)
+        {
+            PIRP Irp = CONTAINING_RECORD(Link, IRP, Tail.Overlay.ListEntry);
+            Link = Link->Flink;
+        
+            // Before we are allowed to complete the pending IRP, we must remove the cancel routine
+            KIRQL irql;
+            IoAcquireCancelSpinLock(&irql);
+            IoSetCancelRoutine(Irp, NULL);
+            IoReleaseCancelSpinLock(irql);
+
+            Irp->IoStatus.Status = STATUS_CANCELLED;
+            Irp->IoStatus.Information = 0;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        }
+    
+        // Reinitialize the list head
+        InitializeListHead(&pFilter->EventIrpListHead);
+    
+        FILTER_RELEASE_LOCK(&pFilter->EventsLock, FALSE);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Updates the wait time for the alarm
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewWaitTime(
+    _In_ PMS_FILTER             pFilter,
+    _In_ ULONG                  waitTime
+    )
+{
+    BOOLEAN FireUpdateEvent = TRUE;
+    
+    // Cancel previous timer
+    if (ExCancelTimer(pFilter->EventHighPrecisionTimer, NULL))
+    {
+        pFilter->EventTimerState = OT_EVENT_TIMER_NOT_RUNNING;
+    }
+
+    if (waitTime == (ULONG)(-1))
+    {
+        // Ignore if we are already stopped
+        if (pFilter->NextAlarmTickCount.QuadPart == 0) return;
+        pFilter->NextAlarmTickCount.QuadPart = 0;
+    }
+    else
+    {
+        if (waitTime == 0)
+        {
+#ifdef DEBUG_TIMING
+            LogInfo(DRIVER_DEFAULT, "Event processing updating to fire timer immediately.");
+#endif
+            pFilter->EventTimerState = OT_EVENT_TIMER_FIRED;
+            pFilter->NextAlarmTickCount.QuadPart = 0;
+        }
+        else if (waitTime * 10000ll < (KeQueryTimeIncrement() - 30000))
+        {
+#ifdef DEBUG_TIMING
+            LogInfo(DRIVER_DEFAULT, "Event processing starting high precision timer for %u ms.", waitTime);
+#endif
+            pFilter->EventTimerState = OT_EVENT_TIMER_RUNNING;
+            pFilter->NextAlarmTickCount.QuadPart = 0;
+            FireUpdateEvent = FALSE;
+            ExSetTimer(pFilter->EventHighPrecisionTimer, waitTime * -10000ll, 0, NULL);
+        }
+        else
+        {
+
+            ULONG TickWaitTime = (waitTime * 10000ll) / KeQueryTimeIncrement();
+            if (TickWaitTime == 0) TickWaitTime = 1;
+#ifdef DEBUG_TIMING
+            LogInfo(DRIVER_DEFAULT, "Event processing updating wait ticks to %u.", TickWaitTime);
+#endif
+
+            // Update the time to be 'waitTime' ms from 'now', saved in TickCounts
+            KeQueryTickCount(&pFilter->NextAlarmTickCount);
+            pFilter->NextAlarmTickCount.QuadPart += TickWaitTime;
+        }
+    }
+    
+    // Indicate event to worker thread to update the wait time
+    KeSetEvent(&pFilter->EventWorkerThreadWaitTimeUpdated, 0, FALSE);
+}
+
+// Indicates another tasklet needs to be processed
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewTasklet(
+    _In_ PMS_FILTER             pFilter
+    )
+{
+    KeSetEvent(&pFilter->EventWorkerThreadProcessTasklets, 0, FALSE);
+}
+
+// Called to indicate that we have an Address change to process
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateAddressChange(
+    _In_ PMS_FILTER             pFilter,
+    _In_ MIB_NOTIFICATION_TYPE  NotificationType,
+    _In_ PIN6_ADDR              pAddr
+    )
+{
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", pFilter);
+
+    NT_ASSERT(pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE);
+
+    POTLWF_ADDR_EVENT Event = FILTER_ALLOC_MEM(pFilter->FilterHandle, sizeof(OTLWF_ADDR_EVENT));
+    if (Event == NULL)
+    {
+        LogWarning(DRIVER_DEFAULT, "Failed to alloc new OTLWF_ADDR_EVENT");
+    }
+    else
+    {
+        Event->NotificationType = NotificationType;
+        Event->Address = *pAddr;
+
+        // Add the event to the queue
+        NdisAcquireSpinLock(&pFilter->EventsLock);
+        InsertTailList(&pFilter->AddressChangesHead, &Event->Link);
+        NdisReleaseSpinLock(&pFilter->EventsLock);
+
+        // Set the event to indicate we have a new address to process
+        KeSetEvent(&pFilter->EventWorkerThreadProcessAddressChanges, 0, FALSE);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Called to indicate that we have a NetBufferLists to process
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewNetBufferLists(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_ PNET_BUFFER_LIST       NetBufferLists
+    )
+{
+    POTLWF_NBL_EVENT Event = FILTER_ALLOC_MEM(pFilter->FilterHandle, sizeof(OTLWF_NBL_EVENT));
+    if (Event == NULL)
+    {
+        LogWarning(DRIVER_DATA_PATH, "Failed to alloc new OTLWF_NBL_EVENT");
+        otLwfCompleteNBLs(pFilter, DispatchLevel, NetBufferLists, STATUS_INSUFFICIENT_RESOURCES);
+        return;
+    }
+
+    Event->NetBufferLists = NetBufferLists;
+
+    // Add the event to the queue
+    FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, DispatchLevel);
+    InsertTailList(&pFilter->NBLsHead, &Event->Link);
+    FILTER_RELEASE_LOCK(&pFilter->EventsLock, DispatchLevel);
+    
+    // Set the event to indicate we have a new NBL to process
+    KeSetEvent(&pFilter->EventWorkerThreadProcessNBLs, 0, FALSE);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewMacFrameCommand(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_reads_bytes_(BufferLength) 
+         const uint8_t*         Buffer,
+    _In_ uint8_t                BufferLength
+    )
+{
+    POTLWF_MAC_FRAME_EVENT Event = FILTER_ALLOC_MEM(pFilter->FilterHandle, FIELD_OFFSET(OTLWF_MAC_FRAME_EVENT, Buffer) + BufferLength);
+    if (Event == NULL)
+    {
+        LogWarning(DRIVER_DATA_PATH, "Failed to alloc new OTLWF_MAC_FRAME_EVENT");
+        return;
+    }
+
+    Event->BufferLength = BufferLength;
+    memcpy(Event->Buffer, Buffer, BufferLength);
+
+    // Add the event to the queue
+    FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, DispatchLevel);
+    InsertTailList(&pFilter->MacFramesHead, &Event->Link);
+    FILTER_RELEASE_LOCK(&pFilter->EventsLock, DispatchLevel);
+    
+    // Set the event to indicate we have a new Mac Frame to process
+    KeSetEvent(&pFilter->EventWorkerThreadProcessMacFrames, 0, FALSE);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNetBufferListsCancelled(
+    _In_ PMS_FILTER             pFilter,
+    _In_ PVOID                  CancelId
+    )
+{
+    PLIST_ENTRY Link = NULL;
+    LIST_ENTRY CancelList = {0};
+    InitializeListHead(&CancelList);
+    
+    // Build up a local list of all NBLs that need to be cancelled
+    NdisAcquireSpinLock(&pFilter->EventsLock);
+    Link = pFilter->NBLsHead.Flink;
+    while (Link != &pFilter->NBLsHead)
+    {
+        POTLWF_NBL_EVENT Event = CONTAINING_RECORD(Link, OTLWF_NBL_EVENT, Link);
+        Link = Link->Flink;
+
+        if (NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(Event->NetBufferLists) == CancelId)
+        {
+            RemoveEntryList(&Event->Link);
+            InsertTailList(&CancelList, &Event->Link);
+        }
+    }
+    NdisReleaseSpinLock(&pFilter->EventsLock);
+    
+    // Cancel all the NBLs
+    Link = CancelList.Flink;
+    while (Link != &CancelList)
+    {
+        POTLWF_NBL_EVENT Event = CONTAINING_RECORD(Link, OTLWF_NBL_EVENT, Link);
+        Link = Link->Flink;
+
+        otLwfCompleteNBLs(pFilter, FALSE, Event->NetBufferLists, STATUS_CANCELLED);
+
+        // Delete the event
+        NdisFreeMemory(Event, 0, 0);
+    }
+}
+
+// Completes the NetBufferLists in the event
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfCompleteNBLs(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_ PNET_BUFFER_LIST       NetBufferLists,
+    _In_ NTSTATUS               Status
+    )
+{
+    LogVerbose(DRIVER_DATA_PATH, "otLwfCompleteNBLs, Filter:%p, NBL:%p, Status:%!STATUS!", pFilter, NetBufferLists, Status);
+
+    // Set the status for all the NBLs
+    PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+    while (CurrNbl)
+    {
+        NET_BUFFER_LIST_STATUS(CurrNbl) = Status;
+        CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+    }
+
+    NT_ASSERT(NetBufferLists);
+
+    // Indicate the completion
+    NdisFSendNetBufferListsComplete(
+        pFilter->FilterHandle,
+        NetBufferLists,
+        DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0
+        );
+}
+
+_Function_class_(DRIVER_CANCEL)
+_Requires_lock_held_(_Global_cancel_spin_lock_)
+_Releases_lock_(_Global_cancel_spin_lock_)
+_IRQL_requires_min_(DISPATCH_LEVEL)
+_IRQL_requires_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingCancelIrp(
+    _Inout_ struct _DEVICE_OBJECT *DeviceObject,
+    _Inout_ _IRQL_uses_cancel_ struct _IRP *Irp
+    )
+{
+    PIRP IrpToCancel = NULL;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    LogFuncEntryMsg(DRIVER_IOCTL, "Irp=%p", Irp);
+
+    IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+    //
+    // Search for a queued up Irp and cancel it if we find it
+    //
+
+    NdisAcquireSpinLock(&FilterListLock);
+
+    // Iterate through each filter instance
+    for (PLIST_ENTRY Link = FilterModuleList.Flink; Link != &FilterModuleList; Link = Link->Flink)
+    {
+        PMS_FILTER pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
+            
+        FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, TRUE);
+        
+        // Iterate through all queued IRPs for the filter
+        PLIST_ENTRY IrpLink = pFilter->EventIrpListHead.Flink;
+        while (IrpLink != &pFilter->EventIrpListHead)
+        {
+            PIRP QueuedIrp = CONTAINING_RECORD(IrpLink, IRP, Tail.Overlay.ListEntry);
+            IrpLink = IrpLink->Flink;
+
+            // If we find it, remove from the and prepare to complete it
+            if (QueuedIrp == Irp)
+            {
+                RemoveEntryList(&QueuedIrp->Tail.Overlay.ListEntry);
+                IrpToCancel = QueuedIrp;
+                break;
+            }
+        }
+            
+        FILTER_RELEASE_LOCK(&pFilter->EventsLock, TRUE);
+
+        if (IrpToCancel) break;
+    }
+
+    NdisReleaseSpinLock(&FilterListLock);
+
+    if (IrpToCancel)
+    {
+        IrpToCancel->IoStatus.Status = STATUS_CANCELLED;
+        IrpToCancel->IoStatus.Information = 0;
+        IoCompleteRequest(IrpToCancel, IO_NO_INCREMENT);
+    }
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+// Queues an Irp for processing
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateIrp(
+    _In_ PMS_FILTER pFilter,
+    _In_ PIRP       Irp
+    )
+{
+    LogFuncEntryMsg(DRIVER_IOCTL, "Irp=%p", Irp);
+
+    // Mark the Irp as pending
+    IoMarkIrpPending(Irp);
+    
+    FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, FALSE);
+
+    // Set the cancel routine for the Irp
+    IoSetCancelRoutine(Irp, otLwfEventProcessingCancelIrp);
+
+    // Queue the Irp up for processing
+    InsertTailList(&pFilter->EventIrpListHead, &Irp->Tail.Overlay.ListEntry);
+
+    FILTER_RELEASE_LOCK(&pFilter->EventsLock, FALSE);
+    
+    // Set the event to indicate we have an Irp to process
+    KeSetEvent(&pFilter->EventWorkerThreadProcessIrp, 0, FALSE);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+// Processes the next OpenThread IoCtl Irp
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingNextIrp(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    PIRP Irp = NULL;
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    do
+    {
+        // Reset pointer
+        Irp = NULL;
+
+        // Get the next Irp in the queue
+        FILTER_ACQUIRE_LOCK(&pFilter->EventsLock, FALSE);
+        if (!IsListEmpty(&pFilter->EventIrpListHead))
+        {
+            PLIST_ENTRY Link = RemoveHeadList(&pFilter->EventIrpListHead);
+            Irp = CONTAINING_RECORD(Link, IRP, Tail.Overlay.ListEntry);
+
+            // Clear the cancel routine since we are processing this now
+            KIRQL irql;
+            IoAcquireCancelSpinLock(&irql);
+            IoSetCancelRoutine(Irp, NULL);
+            IoReleaseCancelSpinLock(irql);
+        }
+        FILTER_RELEASE_LOCK(&pFilter->EventsLock, FALSE);
+
+        if (Irp)
+        {    
+            otLwfCompleteOpenThreadIrp(pFilter, Irp);
+        }
+
+    } while (Irp);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+// Indicates a energy scan was completed
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateEnergyScanResult(
+    _In_ PMS_FILTER pFilter,
+    _In_ CHAR       MaxRssi
+    )
+{
+    LogFuncEntry(DRIVER_IOCTL);
+
+    // Cache the Rssi
+    pFilter->otLastEnergyScanMaxRssi = MaxRssi;
+    
+    // Set the event to indicate we should indicate the state back to OpenThread
+    KeSetEvent(&pFilter->EventWorkerThreadEnergyScanComplete, 0, FALSE);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+// Helper function to copy data out of a NET_BUFFER
+__forceinline
+NTSTATUS
+CopyDataBuffer(
+    _In_ PNET_BUFFER            NetBuffer,
+    _In_ ULONG                  Size,
+    _Out_writes_bytes_all_(Size) 
+         PVOID                  Destination
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    // Read the data out of the NetBuffer
+    PVOID mem = NdisGetDataBuffer(NetBuffer, Size, Destination, 1, 0);
+    if (mem == NULL)
+    {
+        NT_ASSERT(FALSE);
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto error;
+    }
+
+    // If we get a different output memory address, then copy that data to Destination;
+    // otherwise, it was already copied there
+    if (mem != Destination)
+    {
+        RtlCopyMemory(Destination, mem, Size);
+    }
+
+error:
+
+    return status;
+}
+
+_Function_class_(EXT_CALLBACK)
+_IRQL_requires_(DISPATCH_LEVEL)
+_IRQL_requires_same_
+VOID
+otLwfEventProcessingTimer(
+    _In_ PEX_TIMER Timer,
+    _In_opt_ PVOID Context
+    )
+{
+    if (Context == NULL) return;
+
+    PMS_FILTER pFilter = (PMS_FILTER)Context;
+    UNREFERENCED_PARAMETER(Timer);
+    
+#ifdef DEBUG_TIMING
+    LogInfo(DRIVER_DEFAULT, "Event processing high precision timer fired.");
+#endif
+
+    pFilter->EventTimerState = OT_EVENT_TIMER_FIRED;
+
+    // Indicate event to worker thread to update the wait time
+    KeSetEvent(&pFilter->EventWorkerThreadWaitTimeUpdated, 0, FALSE);
+}
+
+// Worker thread for processing all events
+_Use_decl_annotations_
+VOID
+otLwfEventWorkerThread(
+    PVOID   Context
+    )
+{
+    PMS_FILTER pFilter = (PMS_FILTER)Context;
+    NT_ASSERT(pFilter);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PKEVENT WaitEvents[] = 
+    { 
+        &pFilter->EventWorkerThreadStopEvent,
+        &pFilter->EventWorkerThreadProcessNBLs,
+        &pFilter->EventWorkerThreadProcessMacFrames,
+        &pFilter->EventWorkerThreadWaitTimeUpdated,
+        &pFilter->EventWorkerThreadProcessTasklets,
+        &pFilter->SendNetBufferListComplete,
+        &pFilter->EventWorkerThreadProcessIrp,
+        &pFilter->EventWorkerThreadProcessAddressChanges,
+        &pFilter->EventWorkerThreadEnergyScanComplete
+    };
+
+    KWAIT_BLOCK WaitBlocks[ARRAYSIZE(WaitEvents)] = { 0 };
+
+    // Space to processing buffers
+    const ULONG MessageBufferSize = 1280;
+    PUCHAR MessageBuffer = FILTER_ALLOC_MEM(pFilter->FilterHandle, MessageBufferSize);
+    if (MessageBuffer == NULL)
+    {
+        LogError(DRIVER_DATA_PATH, "Failed to allocate 1280 bytes for MessageBuffer!");
+        return;
+    }
+
+#if DEBUG_ALLOC
+    // Initialize the list head for allocations
+    InitializeListHead(&pFilter->otOutStandingAllocations);
+
+    // Cache the Thread ID
+    pFilter->otThreadId = PsGetCurrentThreadId();
+#endif
+
+    // Initialize the radio layer
+    otLwfRadioInit(pFilter);
+
+    // Calculate the size of the otInstance and allocate it
+    pFilter->otInstanceSize = 0;
+    (VOID)otInstanceInit(NULL, &pFilter->otInstanceSize);
+    NT_ASSERT(pFilter->otInstanceSize != 0);
+
+    // Add space for a pointer back to the filter
+    pFilter->otInstanceSize += sizeof(PMS_FILTER);
+
+    // Allocate the buffer
+    pFilter->otInstanceBuffer = (PUCHAR)FILTER_ALLOC_MEM(pFilter->FilterHandle, (ULONG)pFilter->otInstanceSize);
+    if (pFilter == NULL)
+    {
+        LogWarning(DRIVER_DEFAULT, "Failed to allocate otInstance buffer, 0x%x bytes", (ULONG)pFilter->otInstanceSize);
+        goto exit;
+    }
+    RtlZeroMemory(pFilter->otInstanceBuffer, pFilter->otInstanceSize);
+
+    // Store the pointer and decrement the size
+    memcpy(pFilter->otInstanceBuffer, &pFilter, sizeof(PMS_FILTER));
+    pFilter->otInstanceSize -= sizeof(PMS_FILTER);
+
+    // Initialize the OpenThread library
+    pFilter->otCachedRole = OT_DEVICE_ROLE_DISABLED;
+    pFilter->otCtx = otInstanceInit(pFilter->otInstanceBuffer + sizeof(PMS_FILTER), &pFilter->otInstanceSize);
+    NT_ASSERT(pFilter->otCtx);
+    if (pFilter->otCtx == NULL)
+    {
+        LogError(DRIVER_DEFAULT, "otInstanceInit failed, otInstanceSize = %u bytes", (ULONG)pFilter->otInstanceSize);
+        goto exit;
+    }
+
+    // Make sure our helper function returns the right pointer for the filter, given the openthread instance
+    NT_ASSERT(otCtxToFilter(pFilter->otCtx) == pFilter);
+
+    // Disable Icmp (ping) handling
+    otIcmp6SetEchoEnabled(pFilter->otCtx, FALSE);
+
+    // Register callbacks with OpenThread
+    otSetStateChangedCallback(pFilter->otCtx, otLwfStateChangedCallback, pFilter);
+    otIp6SetReceiveCallback(pFilter->otCtx, otLwfReceiveIp6DatagramCallback, pFilter);
+
+    // Query the current addresses from TCPIP and cache them
+    (void)otLwfInitializeAddresses(pFilter);
+
+    // Initialze media connect state to disconnected
+    otLwfIndicateLinkState(pFilter, MediaConnectStateDisconnected);
+
+    for (;;)
+    {
+        NTSTATUS status = STATUS_SUCCESS;
+
+        if (pFilter->NextAlarmTickCount.QuadPart == 0)
+        {
+#ifdef DEBUG_TIMING
+            LogVerbose(DRIVER_DEFAULT, "Event Processing waiting for next event.");
+#endif
+
+            // Wait for event to stop or process event to fire
+            status = KeWaitForMultipleObjects(ARRAYSIZE(WaitEvents), (PVOID*)WaitEvents, WaitAny, Executive, KernelMode, FALSE, NULL, WaitBlocks);
+        }
+        else
+        {
+            LARGE_INTEGER SystemTickCount;
+            KeQueryTickCount(&SystemTickCount);
+
+            if (pFilter->NextAlarmTickCount.QuadPart > SystemTickCount.QuadPart)
+            {
+                // Create the relative (negative) time to wait on
+                LARGE_INTEGER Timeout;
+                Timeout.QuadPart = (SystemTickCount.QuadPart - pFilter->NextAlarmTickCount.QuadPart) * KeQueryTimeIncrement();
+                
+#ifdef DEBUG_TIMING
+                LogVerbose(DRIVER_DEFAULT, "Event Processing waiting for next event, with timeout, %d ms.", (int)(Timeout.QuadPart / -10000));
+#endif
+
+                // Wait for event to stop or process event to fire or timeout
+                status = KeWaitForMultipleObjects(ARRAYSIZE(WaitEvents), (PVOID*)WaitEvents, WaitAny, Executive, KernelMode, FALSE, &Timeout, WaitBlocks);
+            }
+            else
+            {
+#ifdef DEBUG_TIMING
+                LogInfo(DRIVER_DEFAULT, "Event Processing running immediately.");
+#endif
+
+                // No need to wait
+                status = STATUS_TIMEOUT;
+            }
+        }
+
+        // If it is the first event, then we are shutting down. Exit loop and terminate thread
+        if (status == STATUS_WAIT_0)
+        {
+            LogInfo(DRIVER_DEFAULT, "Received event worker thread shutdown event.");
+            break;
+        }
+        
+#ifdef DEBUG_TIMING
+        LogVerbose(DRIVER_DEFAULT, "Event Processing status=0x%x", status);
+#endif
+
+        //
+        // Event fired to process events
+        //
+
+        if (status == STATUS_TIMEOUT || 
+            (pFilter->EventTimerState == OT_EVENT_TIMER_FIRED && status == STATUS_WAIT_0 + 3))
+        {
+            // Reset the wait timeout
+            pFilter->NextAlarmTickCount.QuadPart = 0;
+            pFilter->EventTimerState = OT_EVENT_TIMER_NOT_RUNNING;
+
+            // Indicate to OpenThread that the alarm has fired
+            otPlatAlarmFired(pFilter->otCtx);
+        }
+        else if (status == STATUS_WAIT_0 + 1) // EventWorkerThreadProcessNBLs fired
+        {
+            // Go through the queue until there are no more items
+            for (;;)
+            {
+                POTLWF_NBL_EVENT Event = NULL;
+                NdisAcquireSpinLock(&pFilter->EventsLock);
+
+                // Just get the first item, if available
+                if (!IsListEmpty(&pFilter->NBLsHead))
+                {
+                    PLIST_ENTRY Link = RemoveHeadList(&pFilter->NBLsHead);
+                    Event = CONTAINING_RECORD(Link, OTLWF_NBL_EVENT, Link);
+                }
+
+                NdisReleaseSpinLock(&pFilter->EventsLock);
+
+                // Break out of the loop if we have emptied the queue
+                if (Event == NULL) break;
+
+                NT_ASSERT(Event->NetBufferLists);
+                NTSTATUS NblStatus = STATUS_INSUFFICIENT_RESOURCES;
+
+                // Process the event
+                PNET_BUFFER_LIST CurrNbl = Event->NetBufferLists;
+                while (CurrNbl != NULL)
+                {
+                    PNET_BUFFER CurrNb = NET_BUFFER_LIST_FIRST_NB(CurrNbl);
+                    while (CurrNb != NULL)
+                    {
+                        NT_ASSERT(NET_BUFFER_DATA_LENGTH(CurrNb) <= MessageBufferSize);
+                        if (NET_BUFFER_DATA_LENGTH(CurrNb) <= MessageBufferSize)
+                        {
+                            // Copy NB data into message
+                            if (NT_SUCCESS(CopyDataBuffer(CurrNb, NET_BUFFER_DATA_LENGTH(CurrNb), MessageBuffer)))
+                            {
+                                otError error = OT_ERROR_NONE;
+
+                                // Create a new message
+                                otMessage *message = otIp6NewMessage(pFilter->otCtx, TRUE);
+                                if (message)
+                                {
+                                    // Write to the message
+                                    error = otMessageAppend(message, MessageBuffer, (uint16_t)NET_BUFFER_DATA_LENGTH(CurrNb));
+                                    if (error != OT_ERROR_NONE)
+                                    {
+                                        LogError(DRIVER_DATA_PATH, "otAppendMessage failed with %!otError!", error);
+                                        otMessageFree(message);
+                                    }
+                                    else
+                                    {
+                                        IPV6_HEADER* v6Header = (IPV6_HEADER*)MessageBuffer;
+
+                                        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, IP6_SEND: %p : %!IPV6ADDR! => %!IPV6ADDR! (%u bytes)",
+                                            pFilter, CurrNbl, &v6Header->SourceAddress, &v6Header->DestinationAddress,
+                                            NET_BUFFER_DATA_LENGTH(CurrNb));
+
+#ifdef LOG_BUFFERS
+                                        otLogBuffer(MessageBuffer, NET_BUFFER_DATA_LENGTH(CurrNb));
+#endif
+
+                                        // Send message (it will free 'message')
+                                        error = otIp6Send(pFilter->otCtx, message);
+                                        if (error != OT_ERROR_NONE)
+                                        {
+                                            LogError(DRIVER_DATA_PATH, "otSendIp6Datagram failed with %!otError!", error);
+                                        }
+                                        else
+                                        {
+                                            NblStatus = STATUS_SUCCESS;
+                                        }
+                                    }
+                                }
+                                else
+                                {
+                                    LogError(DRIVER_DATA_PATH, "otNewIPv6Message failed!");
+                                }
+                            }
+                        }
+
+                        CurrNb = NET_BUFFER_NEXT_NB(CurrNb);
+                    }
+
+                    CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl);
+                }
+
+                if (Event->NetBufferLists)
+                {
+                    // Complete the NBLs
+                    otLwfCompleteNBLs(pFilter, FALSE, Event->NetBufferLists, NblStatus);
+                }
+
+                // Free the event
+                NdisFreeMemory(Event, 0, 0);
+            }
+        }
+        else if (status == STATUS_WAIT_0 + 2) // EventWorkerThreadProcessMacFrames fired
+        {
+            // Go through the queue until there are no more items
+            for (;;)
+            {
+                POTLWF_MAC_FRAME_EVENT Event = NULL;
+                NdisAcquireSpinLock(&pFilter->EventsLock);
+
+                // Just get the first item, if available
+                if (!IsListEmpty(&pFilter->MacFramesHead))
+                {
+                    PLIST_ENTRY Link = RemoveHeadList(&pFilter->MacFramesHead);
+                    Event = CONTAINING_RECORD(Link, OTLWF_MAC_FRAME_EVENT, Link);
+                }
+
+                NdisReleaseSpinLock(&pFilter->EventsLock);
+
+                // Break out of the loop if we have emptied the queue
+                if (Event == NULL) break;
+
+                // Read the initial length value and validate
+                uint16_t packetLength = 0;
+                if (try_spinel_datatype_unpack(
+                        Event->Buffer,
+                        Event->BufferLength,
+                        SPINEL_DATATYPE_UINT16_S,
+                        &packetLength) &&
+                    packetLength <= sizeof(pFilter->otReceiveMessage) &&
+                    Event->BufferLength > sizeof(uint16_t) + packetLength)
+                {
+                    pFilter->otReceiveFrame.mLength = (uint8_t)packetLength;
+
+                    uint8_t offset = 2;
+                    uint8_t length = Event->BufferLength - 2;
+
+                    if (packetLength != 0)
+                    {
+                        memcpy(&pFilter->otReceiveMessage, Event->Buffer + offset, packetLength);
+                        offset += pFilter->otReceiveFrame.mLength;
+                        length -= pFilter->otReceiveFrame.mLength;
+                    }
+
+                    otError errorCode;
+                    int8_t noiseFloor = -128;
+                    uint16_t flags = 0;
+                    if (try_spinel_datatype_unpack(
+                            Event->Buffer + offset,
+                            length,
+                            SPINEL_DATATYPE_INT8_S
+                            SPINEL_DATATYPE_INT8_S
+                            SPINEL_DATATYPE_UINT16_S
+                            SPINEL_DATATYPE_STRUCT_S( // PHY-data
+                                SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
+                                SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
+                            )
+                            SPINEL_DATATYPE_STRUCT_S( // Vendor-data
+                                SPINEL_DATATYPE_UINT_PACKED_S
+                            ),
+                            &pFilter->otReceiveFrame.mPower,
+                            &noiseFloor,
+                            &flags,
+                            &pFilter->otReceiveFrame.mChannel,
+                            &pFilter->otReceiveFrame.mLqi,
+                            &errorCode))
+                    {
+                        otLwfRadioReceiveFrame(pFilter, errorCode);
+                    }
+                }
+
+                // Free the event
+                NdisFreeMemory(Event, 0, 0);
+            }
+        }
+        else if (status == STATUS_WAIT_0 + 3) // EventWorkerThreadWaitTimeUpdated fired
+        {
+            // Nothing to do, the next time we wait, we will be using the updated time
+        }
+        else if (status == STATUS_WAIT_0 + 4) // EventWorkerThreadProcessTasklets fired
+        {
+            // Process all tasklets that were indicated to us from OpenThread
+            otTaskletsProcess(pFilter->otCtx);
+        }
+        else if (status == STATUS_WAIT_0 + 5) // SendNetBufferListComplete fired
+        {
+            // Handle the completion of the NBL send
+            otLwfRadioTransmitFrameDone(pFilter);
+        }
+        else if (status == STATUS_WAIT_0 + 6) // EventWorkerThreadProcessIrp fired
+        {
+            // Process any IRPs that were pended
+            otLwfEventProcessingNextIrp(pFilter);
+        }
+        else if (status == STATUS_WAIT_0 + 7) // EventWorkerThreadProcessAddressChanges fired
+        {
+            // Go through the queue until there are no more items
+            for (;;)
+            {
+                POTLWF_ADDR_EVENT Event = NULL;
+                NdisAcquireSpinLock(&pFilter->EventsLock);
+
+                // Get the next item, if available
+                if (!IsListEmpty(&pFilter->AddressChangesHead))
+                {
+                    PLIST_ENTRY Link = RemoveHeadList(&pFilter->AddressChangesHead);
+                    Event = CONTAINING_RECORD(Link, OTLWF_ADDR_EVENT, Link);
+                }
+
+                NdisReleaseSpinLock(&pFilter->EventsLock);
+
+                // Break out of the loop if we have emptied the queue
+                if (Event == NULL) break;
+                
+                // Process the address change on the Openthread thread
+                otLwfEventProcessingAddressChanged(pFilter, Event->NotificationType, &Event->Address);
+
+                // Free the event
+                NdisFreeMemory(Event, 0, 0);
+            }
+        }
+        else if (status == STATUS_WAIT_0 + 8) // EventWorkerThreadEnergyScanComplete fired
+        {
+            // Indicate energy scan complete
+            otPlatRadioEnergyScanDone(pFilter->otCtx, pFilter->otLastEnergyScanMaxRssi);
+        }
+        else
+        {
+            LogWarning(DRIVER_DEFAULT, "Unexpected wait result, %!STATUS!", status);
+        }
+
+        // If we have a frame ready to transmit, do it now if we are allowed to transmit
+        if (pFilter->otRadioState == OT_RADIO_STATE_TRANSMIT && !pFilter->SendPending)
+        {
+            otLwfRadioTransmitFrame(pFilter);
+        }
+    }
+
+exit:
+
+    otLwfReleaseInstance(pFilter);
+
+    if (pFilter->otInstanceBuffer != NULL)
+    {
+        NdisFreeMemory(pFilter->otInstanceBuffer, 0, 0);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+
+    FILTER_FREE_MEM(MessageBuffer);
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+}
diff --git a/examples/drivers/windows/otLwf/filter.c b/examples/drivers/windows/otLwf/filter.c
new file mode 100644
index 0000000..25d32fd
--- /dev/null
+++ b/examples/drivers/windows/otLwf/filter.c
@@ -0,0 +1,848 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "filter.tmh"
+
+// Helper function to query the CompartmentID of a Network Interface
+COMPARTMENT_ID 
+GetInterfaceCompartmentID(
+    _In_ PIF_LUID pNetLuid
+    )
+{  
+    COMPARTMENT_ID CompartmentID = UNSPECIFIED_COMPARTMENT_ID;  
+
+    NTSTATUS Status =
+        NsiGetParameter(
+            NsiActive,
+            &NPI_MS_NDIS_MODULEID,
+            NdisNsiObjectInterfaceInformation,
+            pNetLuid, sizeof(*pNetLuid),
+            NsiStructRoDynamic,
+            &CompartmentID, sizeof(CompartmentID),
+            FIELD_OFFSET(NDIS_NSI_INTERFACE_INFORMATION_ROD, CompartmentId)
+            );
+
+    return (NT_SUCCESS(Status) ? CompartmentID : DEFAULT_COMPARTMENT_ID);
+}
+
+_Use_decl_annotations_
+NDIS_STATUS
+FilterAttach(
+    NDIS_HANDLE                     NdisFilterHandle,
+    NDIS_HANDLE                     FilterDriverContext,
+    PNDIS_FILTER_ATTACH_PARAMETERS  AttachParameters
+    )
+/*++
+
+Routine Description:
+
+    Filter attach routine.
+    Create filter's context, allocate NetBufferLists and NetBuffer pools and any
+    other resources, and read configuration if needed.
+
+Arguments:
+
+    NdisFilterHandle - Specify a handle identifying this instance of the filter. FilterAttach
+                       should save this handle. It is a required  parameter in subsequent calls
+                       to NdisFxxx functions.
+    FilterDriverContext - Filter driver context passed to NdisFRegisterFilterDriver.
+
+    AttachParameters - attach parameters
+
+Return Value:
+
+    NDIS_STATUS_SUCCESS: FilterAttach successfully allocated and initialize data structures
+                         for this filter instance.
+    NDIS_STATUS_RESOURCES: FilterAttach failed due to insufficient resources.
+    NDIS_STATUS_FAILURE: FilterAttach could not set up this instance of this filter and it has called
+                         NdisWriteErrorLogEntry with parameters specifying the reason for failure.
+
+N.B.:  FILTER can use NdisRegisterDeviceEx to create a device, so the upper 
+    layer can send Irps to the filter.
+
+--*/
+{
+    PMS_FILTER              pFilter = NULL;
+    NDIS_STATUS             Status = NDIS_STATUS_SUCCESS;
+    NTSTATUS                NtStatus;
+    NDIS_FILTER_ATTRIBUTES  FilterAttributes;
+    ULONG                   Size;
+    COMPARTMENT_ID          OriginalCompartmentID;
+    OBJECT_ATTRIBUTES       ObjectAttributes = {0};
+
+    const ULONG RegKeyOffset = ARRAYSIZE(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\") - 1;
+    DECLARE_CONST_UNICODE_STRING(RegKeyPath, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\otlwf\\Parameters\\NdisAdapters\\{00000000-0000-0000-0000-000000000000}");
+    RtlCopyMemory(RegKeyPath.Buffer + RegKeyOffset, AttachParameters->BaseMiniportName->Buffer + 8, sizeof(L"{00000000-0000-0000-0000-000000000000}"));
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    do
+    {
+        ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject);
+        if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject)
+        {
+            Status = NDIS_STATUS_INVALID_PARAMETER;
+            break;
+        }
+
+        // Verify the media type is supported.  This is a last resort; the
+        // the filter should never have been bound to an unsupported miniport
+        // to begin with.
+        if (AttachParameters->MiniportMediaType != NdisMediumIP)
+        {
+            LogError(DRIVER_DEFAULT, "Unsupported media type, 0x%x.", (ULONG)AttachParameters->MiniportMediaType);
+            Status = NDIS_STATUS_INVALID_PARAMETER;
+            break;
+        }
+
+        Size = sizeof(MS_FILTER) +  AttachParameters->BaseMiniportInstanceName->Length;
+
+        pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size);
+        if (pFilter == NULL)
+        {
+            LogWarning(DRIVER_DEFAULT, "Failed to allocate context structure, 0x%x bytes", Size);
+            Status = NDIS_STATUS_RESOURCES;
+            break;
+        }
+
+        NdisZeroMemory(pFilter, sizeof(MS_FILTER));
+
+        LogVerbose(DRIVER_DEFAULT, "Opening interface registry key %S", RegKeyPath.Buffer);
+
+        InitializeObjectAttributes(
+            &ObjectAttributes,
+            (PUNICODE_STRING)&RegKeyPath,
+            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+            NULL,
+            NULL);
+
+        // Open the registry key
+        NtStatus = ZwOpenKey(&pFilter->InterfaceRegKey, KEY_ALL_ACCESS, &ObjectAttributes);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            LogError(DRIVER_DEFAULT, "ZwOpenKey failed to open %S, %!STATUS!", RegKeyPath.Buffer, NtStatus);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        // Format of "\DEVICE\{5BA90C49-0D7E-455B-8D3B-614F6714A212}"
+        AttachParameters->BaseMiniportName->Buffer += 8;
+        AttachParameters->BaseMiniportName->Length -= 8 * sizeof(WCHAR);
+        NtStatus = RtlGUIDFromString(AttachParameters->BaseMiniportName, &pFilter->InterfaceGuid);
+        AttachParameters->BaseMiniportName->Buffer -= 8;
+        AttachParameters->BaseMiniportName->Length += 8 * sizeof(WCHAR);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            LogError(DRIVER_DEFAULT, "Failed to convert FilterModuleGuidName to a GUID, %!STATUS!", NtStatus);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        pFilter->InterfaceFriendlyName.Length = pFilter->InterfaceFriendlyName.MaximumLength = AttachParameters->BaseMiniportInstanceName->Length;
+        pFilter->InterfaceFriendlyName.Buffer = (PWSTR)((PUCHAR)pFilter + sizeof(MS_FILTER));
+        NdisMoveMemory(pFilter->InterfaceFriendlyName.Buffer,
+                        AttachParameters->BaseMiniportInstanceName->Buffer,
+                        pFilter->InterfaceFriendlyName.Length);
+
+        pFilter->InterfaceIndex = AttachParameters->BaseMiniportIfIndex;
+        pFilter->InterfaceLuid = AttachParameters->BaseMiniportNetLuid;
+        pFilter->InterfaceCompartmentID = UNSPECIFIED_COMPARTMENT_ID;
+        pFilter->FilterHandle = NdisFilterHandle;
+
+        NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
+        FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
+        FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
+        FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
+        FilterAttributes.Flags = 0;
+
+        NDIS_DECLARE_FILTER_MODULE_CONTEXT(MS_FILTER);
+        Status = NdisFSetAttributes(NdisFilterHandle, pFilter, &FilterAttributes);
+        if (Status != NDIS_STATUS_SUCCESS)
+        {
+            LogError(DRIVER_DEFAULT, "Failed to set attributes, %!NDIS_STATUS!", Status);
+            break;
+        }
+
+        // Filter initially in Paused state
+        pFilter->State = FilterPaused;
+
+        // Initialize rundowns to disabled with no active references
+        pFilter->ExternalRefs.Count = EX_RUNDOWN_ACTIVE;
+        pFilter->cmdRundown.Count = EX_RUNDOWN_ACTIVE;
+
+        // Query the compartment ID for this interface to use for the IP stack
+        pFilter->InterfaceCompartmentID = GetInterfaceCompartmentID(&pFilter->InterfaceLuid);
+        LogVerbose(DRIVER_DEFAULT, "Interface %!GUID! is in Compartment %u", &pFilter->InterfaceGuid, (ULONG)pFilter->InterfaceCompartmentID);
+
+        // Make sure we are in the right compartment
+        (VOID)otLwfSetCompartment(pFilter, &OriginalCompartmentID);
+
+        // Register for address changed notifications
+        NtStatus = 
+            NotifyUnicastIpAddressChange(
+                AF_INET6,
+                otLwfAddressChangeCallback,
+                pFilter,
+                FALSE,
+                &pFilter->AddressChangeHandle
+                );
+
+        // Revert the compartment, now that we have the table
+        otLwfRevertCompartment(OriginalCompartmentID);
+
+        if (!NT_SUCCESS(NtStatus))
+        {
+            LogError(DRIVER_DEFAULT, "NotifyUnicastIpAddressChange failed, %!STATUS!", NtStatus);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        // Add Filter to global list of Thread Filters
+        NdisAcquireSpinLock(&FilterListLock);
+        InsertTailList(&FilterModuleList, &pFilter->FilterModuleLink);
+        NdisReleaseSpinLock(&FilterListLock);
+
+        LogVerbose(DRIVER_DEFAULT, "Created Filter: %p", pFilter);
+
+    } while (FALSE);
+
+    // Clean up on failure
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        if (pFilter != NULL)
+        {
+            if (pFilter->AddressChangeHandle != NULL)
+            {
+                CancelMibChangeNotify2(pFilter->AddressChangeHandle);
+                pFilter->AddressChangeHandle = NULL;
+            }
+
+            NdisFreeMemory(pFilter, 0, 0);
+        }
+    }
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    return Status;
+}
+
+_Use_decl_annotations_
+VOID
+FilterDetach(
+    NDIS_HANDLE     FilterModuleContext
+    )
+/*++
+
+Routine Description:
+
+    Filter detach routine.
+    This is a required function that will deallocate all the resources allocated during
+    FilterAttach. NDIS calls FilterAttach to remove a filter instance from a filter stack.
+
+Arguments:
+
+    FilterModuleContext - pointer to the filter context area.
+
+Return Value:
+    None.
+
+NOTE: Called at PASSIVE_LEVEL and the filter is in paused state
+
+--*/
+{
+    PMS_FILTER  pFilter = (PMS_FILTER)FilterModuleContext;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
+
+    // Filter must be in paused state and pretty much inactive
+    NT_ASSERT(pFilter->State == FilterPaused);
+    NT_ASSERT(pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_UNINTIALIZED);
+
+    //
+    // Detach must not fail, so do not put any code here that can possibly fail.
+    //
+
+    // Remove this Filter from the global list
+    NdisAcquireSpinLock(&FilterListLock);
+    RemoveEntryList(&pFilter->FilterModuleLink);
+    NdisReleaseSpinLock(&FilterListLock);
+
+    // Unregister from address change notifications
+    CancelMibChangeNotify2(pFilter->AddressChangeHandle);
+    pFilter->AddressChangeHandle = NULL;
+
+    // Close the registry key
+    if (pFilter->InterfaceRegKey)
+    {
+        ZwClose(pFilter->InterfaceRegKey);
+        pFilter->InterfaceRegKey = NULL;
+    }
+
+    // Free the memory allocated
+    NdisFreeMemory(pFilter, 0, 0);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Indicates an interface state change has taken place (used for interface arrival/removal)
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfNotifyDeviceAvailabilityChange(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                fAvailable
+    )
+{
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_DEVICE_AVAILABILITY;
+        NotifEntry->Notif.DeviceAvailabilityPayload.Available = fAvailable;
+
+        otLwfIndicateNotification(NotifEntry);
+    }
+}
+
+PAGED
+NTSTATUS
+GetRegDWORDValue(
+    _In_  PMS_FILTER        pFilter,
+    _In_  PCWSTR            ValueName,
+    _Out_ PULONG            ValueData
+)
+{
+    NTSTATUS            status;
+    ULONG               resultLength;
+    UCHAR               keybuf[128] = {0};
+    UNICODE_STRING      UValueName;
+
+    PAGED_CODE();
+
+    RtlInitUnicodeString(&UValueName, ValueName);
+
+    status = ZwQueryValueKey(
+        pFilter->InterfaceRegKey,
+        &UValueName,
+        KeyValueFullInformation,
+        keybuf,
+        sizeof(keybuf),
+        &resultLength);
+
+    if (NT_SUCCESS(status)) 
+    {
+        PKEY_VALUE_FULL_INFORMATION keyInfo = (PKEY_VALUE_FULL_INFORMATION)keybuf;
+
+        if (keyInfo->Type != REG_DWORD)
+        {
+            status = STATUS_INVALID_PARAMETER_MIX;
+        }
+        else
+        {
+            *ValueData = *((ULONG UNALIGNED *)(keybuf + keyInfo->DataOffset));
+        }
+    }
+
+    return status;
+}
+
+PAGED
+NTSTATUS
+SetRegDWORDValue(
+    _In_ PMS_FILTER     pFilter,
+    _In_ PCWSTR         ValueName,
+    _In_ ULONG          ValueData
+)
+{
+    NTSTATUS            status;
+    UNICODE_STRING      UValueName;
+
+    PAGED_CODE();
+
+    RtlInitUnicodeString(&UValueName, ValueName);
+
+    status = ZwSetValueKey(
+        pFilter->InterfaceRegKey,
+        &UValueName,
+        0,
+        REG_DWORD,
+        (PVOID)&ValueData,
+        sizeof(ValueData));
+
+    return status;
+}
+
+_Use_decl_annotations_
+NDIS_STATUS
+FilterRestart(
+    NDIS_HANDLE                     FilterModuleContext,
+    PNDIS_FILTER_RESTART_PARAMETERS RestartParameters
+    )
+/*++
+
+Routine Description:
+
+    Filter restart routine.
+    Start the datapath - begin sending and receiving NBLs.
+
+Arguments:
+
+    FilterModuleContext - pointer to the filter context stucture.
+    RestartParameters   - additional information about the restart operation.
+
+Return Value:
+
+    NDIS_STATUS_SUCCESS: if filter restarts successfully
+    NDIS_STATUS_XXX: Otherwise.
+
+--*/
+{
+    NTSTATUS            NtStatus = STATUS_SUCCESS;
+    NDIS_STATUS         NdisStatus = NDIS_STATUS_SUCCESS;
+    PMS_FILTER          pFilter = (PMS_FILTER)FilterModuleContext;
+    PVOID               SpinelCapsDataBuffer = NULL;
+    const uint8_t*      SpinelCapsPtr = NULL;
+    spinel_size_t       SpinelCapsLen = 0;
+    NL_INTERFACE_KEY    key = {0};
+    NL_INTERFACE_RW     interfaceRw;
+    ULONG               ThreadOnHost = TRUE;
+
+    PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes;
+    PNDIS_RESTART_ATTRIBUTES         NdisRestartAttributes;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
+
+    NT_ASSERT(pFilter->State == FilterPaused);
+
+    NdisRestartAttributes = RestartParameters->RestartAttributes;
+
+    //
+    // If NdisRestartAttributes is not NULL, then the filter can modify generic 
+    // attributes and add new media specific info attributes at the end. 
+    // Otherwise, if NdisRestartAttributes is NULL, the filter should not try to 
+    // modify/add attributes.
+    //
+    if (NdisRestartAttributes != NULL)
+    {
+        ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES);
+
+        NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data;
+
+        //
+        // Check to see if we need to change any attributes. For example, the
+        // driver can change the current MAC address here. Or the driver can add
+        // media specific info attributes.
+        //
+        NdisGeneralAttributes->LookaheadSize = 128;
+    }
+
+    // Initialize the Spinel command processing
+    NdisStatus = otLwfCmdInitialize(pFilter);
+    if (NdisStatus != NDIS_STATUS_SUCCESS)
+    {
+        LogError(DRIVER_DEFAULT, "otLwfCmdInitialize failed, %!NDIS_STATUS!", NdisStatus);
+        goto exit;
+    }
+
+    // Query the device capabilities
+    NtStatus = otLwfCmdGetProp(pFilter, &SpinelCapsDataBuffer, SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, &SpinelCapsPtr, &SpinelCapsLen);
+    if (!NT_SUCCESS(NtStatus))
+    {
+        NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
+        LogError(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_CAPS, %!STATUS!", NtStatus);
+        goto exit;
+    }
+
+    // Iterate and process returned capabilities
+    NT_ASSERT(SpinelCapsDataBuffer);
+    while (SpinelCapsLen > 0)
+    {
+        ULONG SpinelCap = 0;
+        spinel_ssize_t len = spinel_datatype_unpack(SpinelCapsPtr, SpinelCapsLen, SPINEL_DATATYPE_UINT_PACKED_S, &SpinelCap);
+        if (len < 1) break;
+        SpinelCapsLen -= (spinel_size_t)len;
+        SpinelCapsPtr += len;
+
+        switch (SpinelCap)
+        {
+        case SPINEL_CAP_MAC_RAW:
+            pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO;
+            pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ACK_TIMEOUT;
+            pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_MAC_RETRY_AND_COLLISION_AVOIDANCE;
+            pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_RADIO_ENERGY_SCAN;
+            break;
+        case SPINEL_CAP_NET_THREAD_1_0:
+            pFilter->DeviceCapabilities |= OTLWF_DEVICE_CAP_THREAD_1_0;
+            break;
+        default:
+            break;
+        }
+    }
+
+    // Set the state indicating where we should be running the Thread logic (Host or Device).
+    if (!NT_SUCCESS(GetRegDWORDValue(pFilter, L"RunOnHost", &ThreadOnHost)))
+    {
+        // Default to running on the host if the key isn't present
+        ThreadOnHost = TRUE;
+        SetRegDWORDValue(pFilter, L"RunOnHost", ThreadOnHost);
+    }
+
+    LogInfo(DRIVER_DEFAULT, "Filter: %p initializing ThreadOnHost=%d", FilterModuleContext, ThreadOnHost);
+
+    // Initialize the processing logic
+    if (ThreadOnHost)
+    {
+        // Ensure the device has the capabilities to support raw radio commands
+        if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO) == 0)
+        {
+            LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support raw radio commands");
+            NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
+            goto exit;
+        }
+
+        pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_RADIO_MODE;
+        NtStatus = otLwfInitializeThreadMode(pFilter);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            LogError(DRIVER_DEFAULT, "otLwfInitializeThreadMode failed, %!STATUS!", NtStatus);
+            NdisStatus = NDIS_STATUS_FAILURE;
+            pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
+            goto exit;
+        }
+    }
+    else
+    {
+        // Ensure the device has the capabilities to support Thread commands
+        if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_THREAD_1_0) == 0)
+        {
+            LogError(DRIVER_DEFAULT, "Failed to start because device doesn't support thread commands");
+            NdisStatus = NDIS_STATUS_NOT_SUPPORTED;
+            goto exit;
+        }
+
+        pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_THREAD_MODE;
+        NtStatus = otLwfTunInitialize(pFilter);
+        if (!NT_SUCCESS(NtStatus))
+        {
+            LogError(DRIVER_DEFAULT, "otLwfInitializeTunnelMode failed, %!STATUS!", NtStatus);
+            NdisStatus = NDIS_STATUS_FAILURE;
+            pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
+            goto exit;
+        }
+    }
+
+    //
+    // Disable DAD and Neighbor advertisements
+    //
+    key.Luid = pFilter->InterfaceLuid;
+    NlInitializeInterfaceRw(&interfaceRw);
+    interfaceRw.DadTransmits = 0;
+    interfaceRw.SendUnsolicitedNeighborAdvertisementOnDad = FALSE;
+  
+    NtStatus =
+        NsiSetAllParameters(
+            NsiActive,
+            NsiSetDefault,
+            &NPI_MS_IPV6_MODULEID,
+            NlInterfaceObject,
+            &key,
+            sizeof(key),
+            &interfaceRw,
+            sizeof(interfaceRw));
+    if (!NT_SUCCESS(NtStatus))
+    {
+        LogError(DRIVER_DEFAULT, "NsiSetAllParameters (NlInterfaceObject) failed, %!STATUS!", NtStatus);
+        NdisStatus = NDIS_STATUS_FAILURE;
+        goto exit;
+    }
+
+    //
+    // Enable the external references to the filter
+    //
+    ExReInitializeRundownProtection(&pFilter->ExternalRefs);
+
+    //
+    // If everything is OK, set the filter in running state.
+    //
+    pFilter->State = FilterRunning; // when successful
+    otLwfNotifyDeviceAvailabilityChange(pFilter, TRUE);
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! arrival, Filter=%p", &pFilter->InterfaceGuid, pFilter);
+
+exit:
+
+    //
+    // Ensure the state is Paused if restart failed.
+    //
+    if (NdisStatus != NDIS_STATUS_SUCCESS)
+    {
+        pFilter->State = FilterPaused;
+
+        if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+        {
+            otLwfUninitializeThreadMode(pFilter);
+        }
+        else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
+        {
+            otLwfTunUninitialize(pFilter);
+        }
+
+        pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
+
+        // Clean up Spinel command processing
+        otLwfCmdUninitialize(pFilter);
+    }
+
+    // Free the buffer for the capabilities we queried
+    if (SpinelCapsDataBuffer != NULL)
+    {
+        FILTER_FREE_MEM(SpinelCapsDataBuffer);
+    }
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, NdisStatus);
+    return NdisStatus;
+}
+
+_Use_decl_annotations_
+NDIS_STATUS
+FilterPause(
+    NDIS_HANDLE                     FilterModuleContext,
+    PNDIS_FILTER_PAUSE_PARAMETERS   PauseParameters
+    )
+/*++
+
+Routine Description:
+
+    Filter pause routine.
+    Complete all the outstanding sends and queued sends,
+    wait for all the outstanding recvs to be returned
+    and return all the queued receives.
+
+Arguments:
+
+    FilterModuleContext - pointer to the filter context stucture
+    PauseParameters     - additional information about the pause
+
+Return Value:
+
+    NDIS_STATUS_SUCCESS if filter pauses successfully, NDIS_STATUS_PENDING
+    if not.  No other return value is allowed (pause must succeed, eventually).
+
+N.B.: When the filter is in Pausing state, it can still process OID requests, 
+    complete sending, and returning packets to NDIS, and also indicate status.
+    After this function completes, the filter must not attempt to send or 
+    receive packets, but it may still process OID requests and status 
+    indications.
+
+--*/
+{
+    PMS_FILTER      pFilter = (PMS_FILTER)(FilterModuleContext);
+    NDIS_STATUS     Status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(PauseParameters);
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p", FilterModuleContext);
+
+    //
+    // Set the flag that the filter is going to pause
+    //
+    NT_ASSERT(pFilter->State == FilterRunning);
+    NdisAcquireSpinLock(&FilterListLock);
+    pFilter->State = FilterPausing;
+    NdisReleaseSpinLock(&FilterListLock);
+
+    //
+    // Send final notification of interface removal
+    //
+    otLwfNotifyDeviceAvailabilityChange(pFilter, FALSE);
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! removal.", &pFilter->InterfaceGuid);
+
+    //
+    // Disable external references and wait for existing calls to complete
+    //
+    LogInfo(DRIVER_DEFAULT, "Disabling and waiting for external references to release");
+    ExWaitForRundownProtectionRelease(&pFilter->ExternalRefs);
+    LogInfo(DRIVER_DEFAULT, "External references released.");
+
+    //
+    // Clean up based on the device mode
+    //
+    if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+    {
+        otLwfUninitializeThreadMode(pFilter);
+    }
+    else if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_THREAD_MODE)
+    {
+        otLwfTunUninitialize(pFilter);
+    }
+
+    pFilter->DeviceStatus = OTLWF_DEVICE_STATUS_UNINTIALIZED;
+
+    //
+    // Clean up the Spinel command processing
+    //
+    otLwfCmdUninitialize(pFilter);
+
+    //
+    // Set the state back to Paused now that we are done
+    //
+    pFilter->State = FilterPaused;
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+    return Status;
+}
+
+_Use_decl_annotations_
+VOID
+FilterStatus(
+    NDIS_HANDLE             FilterModuleContext,
+    PNDIS_STATUS_INDICATION StatusIndication
+    )
+/*++
+
+Routine Description:
+
+    Status indication handler
+
+Arguments:
+
+    FilterModuleContext     - our filter context
+    StatusIndication        - the status being indicated
+
+NOTE: called at <= DISPATCH_LEVEL
+
+  FILTER driver may call NdisFIndicateStatus to generate a status indication to
+  all higher layer modules.
+
+--*/
+{
+    PMS_FILTER      pFilter = (PMS_FILTER)FilterModuleContext;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "Filter: %p, IndicateStatus: %8x", FilterModuleContext, StatusIndication->StatusCode);
+
+    if (StatusIndication->StatusCode == NDIS_STATUS_LINK_STATE)
+    {
+        PNDIS_LINK_STATE LinkState = (PNDIS_LINK_STATE)StatusIndication->StatusBuffer;
+
+        LogInfo(DRIVER_DEFAULT, "Filter: %p, MediaConnectState: %u", FilterModuleContext, LinkState->MediaConnectState);
+
+        // Cache the link state from the miniport
+        memcpy(&pFilter->MiniportLinkState, LinkState, sizeof(NDIS_LINK_STATE));
+    }
+
+    NdisFIndicateStatus(pFilter->FilterHandle, StatusIndication);
+    
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Indicate a change of the link state
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfIndicateLinkState(
+    _In_ PMS_FILTER                 pFilter,
+    _In_ NDIS_MEDIA_CONNECT_STATE   MediaState
+    )
+{
+    // If we are already in the correct state, just return
+    if (pFilter->MiniportLinkState.MediaConnectState == MediaState)
+    {
+        return;
+    }
+
+    NDIS_STATUS_INDICATION StatusIndication = {0};
+  
+    StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;  
+    StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;  
+    StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION);  
+    StatusIndication.SourceHandle = pFilter->FilterHandle;  
+      
+    StatusIndication.StatusCode = NDIS_STATUS_LINK_STATE;  
+    StatusIndication.StatusBuffer = &pFilter->MiniportLinkState;  
+    StatusIndication.StatusBufferSize = sizeof(pFilter->MiniportLinkState);  
+      
+    pFilter->MiniportLinkState.MediaConnectState = MediaState;
+    
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! new media state: %u", &pFilter->InterfaceGuid, MediaState);
+  
+    NdisFIndicateStatus(pFilter->FilterHandle, &StatusIndication);  
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfSetCompartment(
+    _In_  PMS_FILTER                pFilter,
+    _Out_ COMPARTMENT_ID*           pOriginalCompartment
+    )
+/*++
+
+Routine Description:
+
+    Sets the current thread's compartment ID to match the filter instance.
+
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    // Make sure we are in the right compartment
+    *pOriginalCompartment = NdisGetCurrentThreadCompartmentId();
+    if (*pOriginalCompartment != pFilter->InterfaceCompartmentID)
+    {
+        status = NdisSetCurrentThreadCompartmentId(pFilter->InterfaceCompartmentID);
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "NdisSetCurrentThreadCompartmentId failed, %!STATUS!", status);
+            *pOriginalCompartment = 0;
+        }
+    }
+    else
+    {
+        *pOriginalCompartment = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfRevertCompartment(
+    _In_ COMPARTMENT_ID             OriginalCompartment
+    )
+/*++
+
+Routine Description:
+
+    Resets the current thread's compartment ID.
+
+--*/
+{
+    // Revert the compartment if it is set
+    if (OriginalCompartment != 0)
+    {
+        (VOID)NdisSetCurrentThreadCompartmentId(OriginalCompartment);
+    }
+}
diff --git a/examples/drivers/windows/otLwf/filter.h b/examples/drivers/windows/otLwf/filter.h
new file mode 100644
index 0000000..14ab782
--- /dev/null
+++ b/examples/drivers/windows/otLwf/filter.h
@@ -0,0 +1,450 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the structures and functions for the otLwf Filter instance.
+ */
+
+#ifndef _FILT_H
+#define _FILT_H
+
+// The maximum allowed addresses an OpenThread interface
+#if (OPENTHREAD_ENABLE_DHCP6_CLIENT && OPENTHREAD_ENABLE_DHCP6_SERVER)
+#define OT_MAX_ADDRESSES (4 + OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES + 2 * OPENTHREAD_CONFIG_NUM_DHCP_PREFIXES)
+#elif (OPENTHREAD_ENABLE_DHCP6_CLIENT || OPENTHREAD_ENABLE_DHCP6_SERVER)
+#define OT_MAX_ADDRESSES (4 + OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES + OPENTHREAD_CONFIG_NUM_DHCP_PREFIXES)
+#else
+#define OT_MAX_ADDRESSES (4 + OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES)
+#endif
+
+#define OTLWF_ALLOC_TAG 'mFto' // otFm
+
+#define FILTER_ALLOC_MEM(_NdisHandle, _Size) \
+    NdisAllocateMemoryWithTagPriority(_NdisHandle, _Size, OTLWF_ALLOC_TAG, LowPoolPriority)
+#define FILTER_FREE_MEM(_pMem)      NdisFreeMemory(_pMem, 0, 0)
+#define FILTER_INIT_LOCK(_pLock)    NdisAllocateSpinLock(_pLock)
+#define FILTER_FREE_LOCK(_pLock)    NdisFreeSpinLock(_pLock)
+
+// Helper for locking an NDIS lock
+#define FILTER_ACQUIRE_LOCK(_pLock, DispatchLevel)          \
+{                                                           \
+    if (DispatchLevel) { NdisDprAcquireSpinLock(_pLock); }  \
+    else               { NdisAcquireSpinLock(_pLock);    }  \
+}
+
+// Helper for releasing an NDIS lock
+#define FILTER_RELEASE_LOCK(_pLock, DispatchLevel)          \
+{                                                           \
+    if (DispatchLevel) { NdisDprReleaseSpinLock(_pLock); }  \
+    else               { NdisReleaseSpinLock(_pLock);    }  \
+}
+
+//
+// Enum of filter's states
+// Filter can only be in one state at one time
+//
+typedef enum _FILTER_STATE
+{
+    FilterStateUnspecified,
+    FilterPausing,
+    FilterPaused,
+    FilterRunning,
+} FILTER_STATE;
+
+// Flags for the different device capabilities
+typedef enum OTLWF_DEVICE_CAPABILITY
+{
+    // Device supports raw Radio commands
+    OTLWF_DEVICE_CAP_RADIO                                      = 1 << 0,
+
+    // Device supports Ack timeouts internally
+    OTLWF_DEVICE_CAP_RADIO_ACK_TIMEOUT                          = 1 << 1,
+
+    // Device supports MAC retry logic and timers; as well as collision avoidance.
+    OTLWF_DEVICE_CAP_RADIO_MAC_RETRY_AND_COLLISION_AVOIDANCE    = 1 << 2,
+
+    // Device supports the energy scan command.
+    OTLWF_DEVICE_CAP_RADIO_ENERGY_SCAN                          = 1 << 3,
+
+    // Device support Net & Thread commands.
+    OTLWF_DEVICE_CAP_THREAD_1_0                                 = 1 << 16,
+
+} OTLWF_DEVICE_CAPABILITY;
+
+// Flags for the different device capabilities
+typedef enum OTLWF_DEVICE_STATUS
+{
+    OTLWF_DEVICE_STATUS_UNINTIALIZED,   // Not yet initialzied.
+    OTLWF_DEVICE_STATUS_RADIO_MODE,     // The device is just operating as a simple radio.
+    OTLWF_DEVICE_STATUS_THREAD_MODE     // The device is managing the Thread stack.
+
+} OTLWF_DEVICE_STATUS;
+
+#define OT_EVENT_TIMER_NOT_RUNNING  0
+#define OT_EVENT_TIMER_RUNNING      1
+#define OT_EVENT_TIMER_FIRED        2
+
+#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
+
+typedef struct BufferPool
+{
+    struct BufferPool* Next;
+    uint8_t Buffers[0];
+
+} BufferPool;
+
+enum
+{
+    kPageSize                = PAGE_SIZE,
+    kPagesPerBufferPool      = 1,
+    kMaxPagesForBufferPools  = 64,
+    kMaxBytesForBufferPools  = kPageSize * kMaxPagesForBufferPools,
+
+    kEstimatedBufferSize     = 128,         // sizeof(ot::Buffer)
+    kEstimatedBufferPoolSize = ((kPageSize * kPagesPerBufferPool) - sizeof(BufferPool)) / kEstimatedBufferSize,
+    kEstimatedMaxBuffers     = kMaxPagesForBufferPools * kEstimatedBufferPoolSize
+};
+
+#endif
+
+//
+// Define the filter struct
+//
+typedef struct _MS_FILTER
+{
+    // Entry in the global list of Filter instances
+    LIST_ENTRY                      FilterModuleLink;
+
+    // NDIS Handle for the Filter instance
+    NDIS_HANDLE                     FilterHandle;
+
+    // Current state (Running or not) of the Filter instance
+    FILTER_STATE                    State;
+
+    // Handle for unicast IP address notifications
+    HANDLE                          AddressChangeHandle;
+
+    //
+    // Interface variables
+    //
+    GUID                            InterfaceGuid;
+    NET_IFINDEX                     InterfaceIndex;
+    NET_LUID                        InterfaceLuid;
+    COMPARTMENT_ID                  InterfaceCompartmentID;
+    NDIS_STRING                     InterfaceFriendlyName;
+    HANDLE                          InterfaceRegKey;
+
+    //
+    // Miniport Link State
+    //
+    NDIS_LINK_STATE                 MiniportLinkState;
+
+    //
+    // External references management
+    //   Used for IOCTLs, SendNBLs, and Address Changed callbacks
+    //
+    EX_RUNDOWN_REF                  ExternalRefs;
+
+    //
+    // Spinel Command State
+    //
+    EX_RUNDOWN_REF                  cmdRundown;
+    NDIS_SPIN_LOCK                  cmdLock;
+    LIST_ENTRY                      cmdHandlers;
+    USHORT                          cmdTIDsInUse;
+    spinel_tid_t                    cmdNextTID;
+    NDIS_HANDLE                     cmdNblPool;
+#ifdef COMMAND_INIT_RETRY
+    ULONG                           cmdInitTryCount;
+#endif
+    otPlatResetReason               cmdResetReason;
+    KEVENT                          cmdResetCompleteEvent;
+
+    //
+    // Device Capabilities / State
+    //
+    OTLWF_DEVICE_CAPABILITY         DeviceCapabilities;
+    OTLWF_DEVICE_STATUS             DeviceStatus;
+
+    //
+    // OpenThread addresses
+    //
+    IN6_ADDR                    otCachedAddr[OT_MAX_ADDRESSES];
+    ULONG                       otCachedAddrCount;
+    IN6_ADDR                    otLinkLocalAddr;
+    otNetifAddress              otAutoAddresses[OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES];
+#if OPENTHREAD_ENABLE_DHCP6_CLIENT
+    otDhcpAddress               otDhcpAddresses[OPENTHREAD_CONFIG_NUM_DHCP_PREFIXES];
+#endif // OPENTHREAD_ENABLE_DHCP6_CLIENT
+
+    union
+    {
+    struct // Thread Mode Variables
+    {
+        //
+        // OpenThread Event processing
+        //
+        PVOID                       EventWorkerThread;
+        KEVENT                      EventWorkerThreadStopEvent;
+        KEVENT                      EventWorkerThreadProcessAddressChanges;
+        KEVENT                      EventWorkerThreadProcessNBLs;
+        KEVENT                      EventWorkerThreadProcessMacFrames;
+        NDIS_SPIN_LOCK              EventsLock;
+        LIST_ENTRY                  AddressChangesHead;
+        LIST_ENTRY                  NBLsHead;
+        LIST_ENTRY                  MacFramesHead;
+        LARGE_INTEGER               NextAlarmTickCount;
+        KEVENT                      EventWorkerThreadWaitTimeUpdated;
+        KEVENT                      EventWorkerThreadProcessTasklets;
+        PEX_TIMER                   EventHighPrecisionTimer;
+        UCHAR                       EventTimerState;
+        LIST_ENTRY                  EventIrpListHead;
+        KEVENT                      EventWorkerThreadProcessIrp;
+        KEVENT                      EventWorkerThreadEnergyScanComplete;
+
+        //
+        // OpenThread Settings Management
+        //
+        HANDLE                      otSettingsRegKey;
+
+        //
+        // OpenThread state management
+        //
+        otDeviceRole                otCachedRole;
+
+        //
+        // OpenThread data path state
+        //
+        BOOLEAN                     SendPending;
+        KEVENT                      SendNetBufferListComplete;
+    
+        //
+        // OpenThread radio variables
+        //
+        otRadioCaps                 otRadioCapabilities;
+        otRadioState                otRadioState;
+        uint8_t                     otCurrentListenChannel;
+        uint8_t                     otReceiveMessage[OT_RADIO_FRAME_MAX_SIZE];
+        uint8_t                     otTransmitMessage[OT_RADIO_FRAME_MAX_SIZE];
+        otRadioFrame                otReceiveFrame;
+        otRadioFrame                otTransmitFrame;
+        otError                     otLastTransmitError;
+        BOOLEAN                     otLastTransmitFramePending;
+        CHAR                        otLastEnergyScanMaxRssi;
+
+        BOOLEAN                     otPromiscuous;
+        uint16_t                    otPanID;
+        uint64_t                    otFactoryAddress;
+        uint64_t                    otExtendedAddress;
+        uint16_t                    otShortAddress;
+
+        BOOLEAN                     otPendingMacOffloadEnabled;
+
+#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
+        uint16_t                    otBufferSize;               // Bytes in a single buffer
+        uint16_t                    otBufferPoolByteSize;       // Bytes in a buffer pool
+        uint16_t                    otBufferPoolBufferCount;    // Number of buffers in a pool
+        uint16_t                    otBuffersLeft;              // Number of buffers left to return
+        BufferPool*                 otBufferPoolHead;           // List of buffer pools
+        otMessage*                  otFreeBuffers;              // List of buffers to return
+#endif
+
+#if DEBUG_ALLOC
+        // Used for tracking memory allocations
+        HANDLE                      otThreadId;
+        volatile LONG               otOutstandingAllocationCount;
+        volatile LONG               otOutstandingMemoryAllocated;
+        LIST_ENTRY                  otOutStandingAllocations;
+        ULONG                       otAllocationID;
+#endif
+
+        //
+        // OpenThread Joiner Vendor Info
+        //
+        char otVendorName[OPENTHREAD_VENDOR_NAME_MAX_LENGTH + 1];
+        char otVendorModel[OPENTHREAD_VENDOR_MODEL_MAX_LENGTH + 1];
+        char otVendorSwVersion[OPENTHREAD_VENDOR_SW_VERSION_MAX_LENGTH + 1];
+        char otVendorData[OPENTHREAD_VENDOR_DATA_MAX_LENGTH + 1];
+
+        //
+        // OpenThread context buffer
+        //
+        otInstance*                 otCtx;
+        size_t                      otInstanceSize;
+        PUCHAR                      otInstanceBuffer;
+    };
+    struct // Tunnel Mode Variables
+    {
+        PVOID                       TunWorkerThread;
+        KEVENT                      TunWorkerThreadStopEvent;
+        KEVENT                      TunWorkerThreadAddressChangedEvent;
+    };
+    };
+
+} MS_FILTER, * PMS_FILTER;
+
+//
+// NDIS Filter Functions
+//
+
+FILTER_ATTACH FilterAttach;
+FILTER_DETACH FilterDetach;
+FILTER_RESTART FilterRestart;
+FILTER_PAUSE FilterPause;
+FILTER_STATUS FilterStatus;
+FILTER_SEND_NET_BUFFER_LISTS FilterSendNetBufferLists;
+FILTER_RETURN_NET_BUFFER_LISTS FilterReturnNetBufferLists;
+FILTER_SEND_NET_BUFFER_LISTS_COMPLETE FilterSendNetBufferListsComplete;
+FILTER_RECEIVE_NET_BUFFER_LISTS FilterReceiveNetBufferLists;
+FILTER_CANCEL_SEND_NET_BUFFER_LISTS FilterCancelSendNetBufferLists;
+
+//
+// Link State Functions
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfIndicateLinkState(
+    _In_ PMS_FILTER                 pFilter,
+    _In_ NDIS_MEDIA_CONNECT_STATE   MediaState
+    );
+
+//
+// Compartment Functions
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfSetCompartment(
+    _In_  PMS_FILTER                pFilter,
+    _Out_ COMPARTMENT_ID*           pOriginalCompartment
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfRevertCompartment(
+    _In_ COMPARTMENT_ID             OriginalCompartment
+    );
+
+//
+// Address Functions
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+NETIOAPI_API_ 
+otLwfAddressChangeCallback(
+    _In_ PVOID CallerContext,
+    _In_opt_ PMIB_UNICASTIPADDRESS_ROW Row,
+    _In_ MIB_NOTIFICATION_TYPE NotificationType
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingAddressChanged(
+    _In_ PMS_FILTER             pFilter,
+    _In_ MIB_NOTIFICATION_TYPE  NotificationType,
+    _In_ PIN6_ADDR              pAddr
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS 
+otLwfInitializeAddresses(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID 
+otLwfRadioAddressesUpdated(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID 
+otLwfTunAddressesUpdated(
+    _In_ PMS_FILTER pFilter,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len,
+    _Out_ uint32_t *aNotifFlags
+    );
+
+int 
+otLwfFindCachedAddrIndex(
+    _In_ PMS_FILTER pFilter, 
+    _In_ PIN6_ADDR addr
+    );
+
+//
+// Tunnel Logic Functions
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfInitializeTunnelMode(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfUninitializeTunnelMode(
+    _In_ PMS_FILTER pFilter
+    );
+
+//
+// Logging Helper
+//
+
+#ifdef LOG_BUFFERS
+void
+otLogBuffer(
+    _In_reads_bytes_(BufferLength) PUCHAR Buffer,
+    _In_                           ULONG  BufferLength
+    );
+#endif
+
+//
+// Debug Helpers
+//
+
+#if DEBUG_ALLOC
+
+typedef struct _OT_ALLOC
+{
+    LIST_ENTRY Link;
+    LONG Length;
+    ULONG ID;
+} OT_ALLOC;
+
+PMS_FILTER
+otLwfFindFromCurrentThread();
+
+#endif
+
+#endif  //_FILT_H
diff --git a/examples/drivers/windows/otLwf/filter.rc b/examples/drivers/windows/otLwf/filter.rc
new file mode 100644
index 0000000..1a9ad2c
--- /dev/null
+++ b/examples/drivers/windows/otLwf/filter.rc
@@ -0,0 +1,40 @@
+#include <windows.h>
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define    VER_FILETYPE    VFT_DRV
+/* possible values:        VFT_UNKNOWN
+                VFT_APP
+                VFT_DLL
+                VFT_DRV
+                VFT_FONT
+                VFT_VXD
+                VFT_STATIC_LIB
+*/
+#define    VER_FILESUBTYPE    VFT2_DRV_NETWORK
+/* possible values        VFT2_UNKNOWN
+                VFT2_DRV_PRINTER
+                VFT2_DRV_KEYBOARD
+                VFT2_DRV_LANGUAGE
+                VFT2_DRV_DISPLAY
+                VFT2_DRV_MOUSE
+                VFT2_DRV_NETWORK
+                VFT2_DRV_SYSTEM
+                VFT2_DRV_INSTALLABLE
+                VFT2_DRV_SOUND
+                VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR     "otLwf NDIS 6.0 Filter Driver"
+#define VER_INTERNALNAME_STR        "otLwf.sys"
+#define VER_ORIGINALFILENAME_STR    "otLwf.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
+
diff --git a/examples/drivers/windows/otLwf/iocontrol.c b/examples/drivers/windows/otLwf/iocontrol.c
new file mode 100644
index 0000000..a1b343d
--- /dev/null
+++ b/examples/drivers/windows/otLwf/iocontrol.c
@@ -0,0 +1,6263 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "iocontrol.tmh"
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl(
+    _In_ PMS_FILTER     pFilter,
+    _In_ PIRP           Irp
+    );
+
+typedef struct _OTLWF_IOCTL_HANDLER
+{
+    const char*             Name;
+    OTLWF_OT_IOCTL_FUNC*    otFunc;
+    OTLWF_TUN_IOCTL_FUNC*   tunFunc;
+} OTLWF_IOCTL_HANDLER;
+
+OTLWF_IOCTL_HANDLER IoCtls[] =
+{
+    { "IOCTL_OTLWF_OT_ENABLED",                     NULL },
+    { "IOCTL_OTLWF_OT_INTERFACE",                   REF_IOCTL_FUNC_WITH_TUN(otInterface) },
+    { "IOCTL_OTLWF_OT_THREAD",                      REF_IOCTL_FUNC_WITH_TUN(otThread) },
+    { "IOCTL_OTLWF_OT_ACTIVE_SCAN",                 REF_IOCTL_FUNC_WITH_TUN(otActiveScan) },
+    { "IOCTL_OTLWF_OT_DISCOVER",                    REF_IOCTL_FUNC(otDiscover) },
+    { "IOCTL_OTLWF_OT_CHANNEL",                     REF_IOCTL_FUNC_WITH_TUN(otChannel) },
+    { "IOCTL_OTLWF_OT_CHILD_TIMEOUT",               REF_IOCTL_FUNC_WITH_TUN(otChildTimeout) },
+    { "IOCTL_OTLWF_OT_EXTENDED_ADDRESS",            REF_IOCTL_FUNC_WITH_TUN(otExtendedAddress) },
+    { "IOCTL_OTLWF_OT_EXTENDED_PANID",              REF_IOCTL_FUNC_WITH_TUN(otExtendedPanId) },
+    { "IOCTL_OTLWF_OT_LEADER_RLOC",                 REF_IOCTL_FUNC_WITH_TUN(otLeaderRloc) },
+    { "IOCTL_OTLWF_OT_LINK_MODE",                   REF_IOCTL_FUNC_WITH_TUN(otLinkMode) },
+    { "IOCTL_OTLWF_OT_MASTER_KEY",                  REF_IOCTL_FUNC_WITH_TUN(otMasterKey) },
+    { "IOCTL_OTLWF_OT_MESH_LOCAL_EID",              REF_IOCTL_FUNC_WITH_TUN(otMeshLocalEid) },
+    { "IOCTL_OTLWF_OT_MESH_LOCAL_PREFIX",           REF_IOCTL_FUNC_WITH_TUN(otMeshLocalPrefix) },
+    { "IOCTL_OTLWF_OT_NETWORK_DATA_LEADER",         NULL },
+    { "IOCTL_OTLWF_OT_NETWORK_DATA_LOCAL",          NULL },
+    { "IOCTL_OTLWF_OT_NETWORK_NAME",                REF_IOCTL_FUNC_WITH_TUN(otNetworkName) },
+    { "IOCTL_OTLWF_OT_PAN_ID",                      REF_IOCTL_FUNC_WITH_TUN(otPanId) },
+    { "IOCTL_OTLWF_OT_ROUTER_ROLL_ENABLED",         REF_IOCTL_FUNC_WITH_TUN(otRouterRollEnabled) },
+    { "IOCTL_OTLWF_OT_SHORT_ADDRESS",               REF_IOCTL_FUNC_WITH_TUN(otShortAddress) },
+    { "IOCTL_OTLWF_OT_UNICAST_ADDRESSES",           NULL },
+    { "IOCTL_OTLWF_OT_ACTIVE_DATASET",              REF_IOCTL_FUNC(otActiveDataset) },
+    { "IOCTL_OTLWF_OT_PENDING_DATASET",             REF_IOCTL_FUNC(otPendingDataset) },
+    { "IOCTL_OTLWF_OT_LOCAL_LEADER_WEIGHT",         REF_IOCTL_FUNC_WITH_TUN(otLocalLeaderWeight) },
+    { "IOCTL_OTLWF_OT_ADD_BORDER_ROUTER",           REF_IOCTL_FUNC_WITH_TUN(otAddBorderRouter) },
+    { "IOCTL_OTLWF_OT_REMOVE_BORDER_ROUTER",        REF_IOCTL_FUNC_WITH_TUN(otRemoveBorderRouter) },
+    { "IOCTL_OTLWF_OT_ADD_EXTERNAL_ROUTE",          REF_IOCTL_FUNC_WITH_TUN(otAddExternalRoute) },
+    { "IOCTL_OTLWF_OT_REMOVE_EXTERNAL_ROUTE",       REF_IOCTL_FUNC_WITH_TUN(otRemoveExternalRoute) },
+    { "IOCTL_OTLWF_OT_SEND_SERVER_DATA",            REF_IOCTL_FUNC(otSendServerData) },
+    { "IOCTL_OTLWF_OT_CONTEXT_ID_REUSE_DELAY",      REF_IOCTL_FUNC_WITH_TUN(otContextIdReuseDelay) },
+    { "IOCTL_OTLWF_OT_KEY_SEQUENCE_COUNTER",        REF_IOCTL_FUNC_WITH_TUN(otKeySequenceCounter) },
+    { "IOCTL_OTLWF_OT_NETWORK_ID_TIMEOUT",          REF_IOCTL_FUNC_WITH_TUN(otNetworkIdTimeout) },
+    { "IOCTL_OTLWF_OT_ROUTER_UPGRADE_THRESHOLD",    REF_IOCTL_FUNC_WITH_TUN(otRouterUpgradeThreshold) },
+    { "IOCTL_OTLWF_OT_RELEASE_ROUTER_ID",           REF_IOCTL_FUNC_WITH_TUN(otReleaseRouterId) },
+    { "IOCTL_OTLWF_OT_MAC_WHITELIST_ENABLED",       REF_IOCTL_FUNC_WITH_TUN(otMacWhitelistEnabled) },
+    { "IOCTL_OTLWF_OT_ADD_MAC_WHITELIST",           REF_IOCTL_FUNC_WITH_TUN(otAddMacWhitelist) },
+    { "IOCTL_OTLWF_OT_REMOVE_MAC_WHITELIST",        REF_IOCTL_FUNC_WITH_TUN(otRemoveMacWhitelist) },
+    { "IOCTL_OTLWF_OT_MAC_WHITELIST_ENTRY",         REF_IOCTL_FUNC(otMacWhitelistEntry) },
+    { "IOCTL_OTLWF_OT_CLEAR_MAC_WHITELIST",         REF_IOCTL_FUNC_WITH_TUN(otClearMacWhitelist) },
+    { "IOCTL_OTLWF_OT_DEVICE_ROLE",                 REF_IOCTL_FUNC_WITH_TUN(otDeviceRole) },
+    { "IOCTL_OTLWF_OT_CHILD_INFO_BY_ID",            REF_IOCTL_FUNC(otChildInfoById) },
+    { "IOCTL_OTLWF_OT_CHILD_INFO_BY_INDEX",         REF_IOCTL_FUNC(otChildInfoByIndex) },
+    { "IOCTL_OTLWF_OT_EID_CACHE_ENTRY",             REF_IOCTL_FUNC(otEidCacheEntry) },
+    { "IOCTL_OTLWF_OT_LEADER_DATA",                 REF_IOCTL_FUNC(otLeaderData) },
+    { "IOCTL_OTLWF_OT_LEADER_ROUTER_ID",            REF_IOCTL_FUNC_WITH_TUN(otLeaderRouterId) },
+    { "IOCTL_OTLWF_OT_LEADER_WEIGHT",               REF_IOCTL_FUNC_WITH_TUN(otLeaderWeight) },
+    { "IOCTL_OTLWF_OT_NETWORK_DATA_VERSION",        REF_IOCTL_FUNC_WITH_TUN(otNetworkDataVersion) },
+    { "IOCTL_OTLWF_OT_PARTITION_ID",                REF_IOCTL_FUNC_WITH_TUN(otPartitionId) },
+    { "IOCTL_OTLWF_OT_RLOC16",                      REF_IOCTL_FUNC_WITH_TUN(otRloc16) },
+    { "IOCTL_OTLWF_OT_ROUTER_ID_SEQUENCE",          REF_IOCTL_FUNC(otRouterIdSequence) },
+    { "IOCTL_OTLWF_OT_ROUTER_INFO",                 REF_IOCTL_FUNC(otRouterInfo) },
+    { "IOCTL_OTLWF_OT_STABLE_NETWORK_DATA_VERSION", REF_IOCTL_FUNC_WITH_TUN(otStableNetworkDataVersion) },
+    { "IOCTL_OTLWF_OT_MAC_BLACKLIST_ENABLED",       REF_IOCTL_FUNC(otMacBlacklistEnabled) },
+    { "IOCTL_OTLWF_OT_ADD_MAC_BLACKLIST",           REF_IOCTL_FUNC(otAddMacBlacklist) },
+    { "IOCTL_OTLWF_OT_REMOVE_MAC_BLACKLIST",        REF_IOCTL_FUNC(otRemoveMacBlacklist) },
+    { "IOCTL_OTLWF_OT_MAC_BLACKLIST_ENTRY",         REF_IOCTL_FUNC(otMacBlacklistEntry) },
+    { "IOCTL_OTLWF_OT_CLEAR_MAC_BLACKLIST",         REF_IOCTL_FUNC(otClearMacBlacklist) },
+    { "IOCTL_OTLWF_OT_MAX_TRANSMIT_POWER",          REF_IOCTL_FUNC(otMaxTransmitPower) },
+    { "IOCTL_OTLWF_OT_NEXT_ON_MESH_PREFIX",         REF_IOCTL_FUNC(otNextOnMeshPrefix) },
+    { "IOCTL_OTLWF_OT_POLL_PERIOD",                 REF_IOCTL_FUNC(otPollPeriod) },
+    { "IOCTL_OTLWF_OT_LOCAL_LEADER_PARTITION_ID",   REF_IOCTL_FUNC(otLocalLeaderPartitionId) },
+    { "IOCTL_OTLWF_OT_ASSIGN_LINK_QUALITY",         REF_IOCTL_FUNC(otAssignLinkQuality) },
+    { "IOCTL_OTLWF_OT_PLATFORM_RESET",              REF_IOCTL_FUNC_WITH_TUN(otPlatformReset) },
+    { "IOCTL_OTLWF_OT_PARENT_INFO",                 REF_IOCTL_FUNC_WITH_TUN(otParentInfo) },
+    { "IOCTL_OTLWF_OT_SINGLETON",                   REF_IOCTL_FUNC(otSingleton) },
+    { "IOCTL_OTLWF_OT_MAC_COUNTERS",                REF_IOCTL_FUNC(otMacCounters) },
+    { "IOCTL_OTLWF_OT_MAX_CHILDREN",                REF_IOCTL_FUNC_WITH_TUN(otMaxChildren) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_START",           REF_IOCTL_FUNC(otCommissionerStart) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_STOP",            REF_IOCTL_FUNC(otCommissionerStop) },
+    { "IOCTL_OTLWF_OT_JOINER_START",                REF_IOCTL_FUNC(otJoinerStart) },
+    { "IOCTL_OTLWF_OT_JOINER_STOP",                 REF_IOCTL_FUNC(otJoinerStop) },
+    { "IOCTL_OTLWF_OT_FACTORY_EUI64",               REF_IOCTL_FUNC(otFactoryAssignedIeeeEui64) },
+    { "IOCTL_OTLWF_OT_HASH_MAC_ADDRESS",            REF_IOCTL_FUNC(otHashMacAddress) },
+    { "IOCTL_OTLWF_OT_ROUTER_DOWNGRADE_THRESHOLD",  REF_IOCTL_FUNC_WITH_TUN(otRouterDowngradeThreshold) },
+    { "IOCTL_OTLWF_OT_COMMISSIONER_PANID_QUERY",    REF_IOCTL_FUNC(otCommissionerPanIdQuery) },
+    { "IOCTL_OTLWF_OT_COMMISSIONER_ENERGY_SCAN",    REF_IOCTL_FUNC(otCommissionerEnergyScan) },
+    { "IOCTL_OTLWF_OT_ROUTER_SELECTION_JITTER",     REF_IOCTL_FUNC_WITH_TUN(otRouterSelectionJitter) },
+    { "IOCTL_OTLWF_OT_JOINER_UDP_PORT",             REF_IOCTL_FUNC(otJoinerUdpPort) },
+    { "IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_GET",         REF_IOCTL_FUNC(otSendDiagnosticGet) },
+    { "IOCTL_OTLWF_OT_SEND_DIAGNOSTIC_RESET",       REF_IOCTL_FUNC(otSendDiagnosticReset) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_ADD_JOINER",      REF_IOCTL_FUNC(otCommissionerAddJoiner) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_REMOVE_JOINER",   REF_IOCTL_FUNC(otCommissionerRemoveJoiner) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_PROVISIONING_URL", REF_IOCTL_FUNC(otCommissionerProvisioningUrl) },
+    { "IOCTL_OTLWF_OT_COMMISIONER_ANNOUNCE_BEGIN",  REF_IOCTL_FUNC(otCommissionerAnnounceBegin) },
+    { "IOCTL_OTLWF_OT_ENERGY_SCAN",                 REF_IOCTL_FUNC_WITH_TUN(otEnergyScan) },
+    { "IOCTL_OTLWF_OT_SEND_ACTIVE_GET",             REF_IOCTL_FUNC(otSendActiveGet) },
+    { "IOCTL_OTLWF_OT_SEND_ACTIVE_SET",             REF_IOCTL_FUNC(otSendActiveSet) },
+    { "IOCTL_OTLWF_OT_SEND_PENDING_GET",            REF_IOCTL_FUNC(otSendPendingGet) },
+    { "IOCTL_OTLWF_OT_SEND_PENDING_SET",            REF_IOCTL_FUNC(otSendPendingSet) },
+    { "IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_GET",  REF_IOCTL_FUNC(otSendMgmtCommissionerGet) },
+    { "IOCTL_OTLWF_OT_SEND_MGMT_COMMISSIONER_SET",  REF_IOCTL_FUNC(otSendMgmtCommissionerSet) },
+    { "IOCTL_OTLWF_OT_KEY_SWITCH_GUARDTIME",        REF_IOCTL_FUNC_WITH_TUN(otKeySwitchGuardtime) },
+    { "IOCTL_OTLWF_OT_FACTORY_RESET",               REF_IOCTL_FUNC(otFactoryReset) },
+    { "IOCTL_OTLWF_OT_THREAD_AUTO_START",           REF_IOCTL_FUNC(otThreadAutoStart) },
+    { "IOCTL_OTLWF_OT_PREFERRED_ROUTER_ID",         REF_IOCTL_FUNC(otThreadPreferredRouterId) },
+    { "IOCTL_OTLWF_OT_PSKC",                        REF_IOCTL_FUNC_WITH_TUN(otPSKc) },
+    { "IOCTL_OTLWF_OT_PARENT_PRIORITY",             REF_IOCTL_FUNC(otParentPriority) },
+};
+
+static_assert(ARRAYSIZE(IoCtls) == (MAX_OTLWF_IOCTL_FUNC_CODE - MIN_OTLWF_IOCTL_FUNC_CODE) + 1,
+              "The IoCtl strings should be up to date with the actual IoCtl list.");
+
+const char*
+IoCtlString(
+    ULONG IoControlCode
+)
+{
+    ULONG FuncCode = ((IoControlCode >> 2) & 0xFFF) - 100;
+    return FuncCode < ARRAYSIZE(IoCtls) ? IoCtls[FuncCode].Name : "UNKNOWN IOCTL";
+}
+
+// Handles queries for the current list of Thread interfaces
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtlEnumerateInterfaces(
+    _In_reads_bytes_(InBufferLength)
+            PVOID           InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG NewOutBufferLength = 0;
+    POTLWF_INTERFACE_LIST pInterfaceList = (POTLWF_INTERFACE_LIST)OutBuffer;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    // Make sure to zero out the output first
+    RtlZeroMemory(OutBuffer, *OutBufferLength);
+
+    NdisAcquireSpinLock(&FilterListLock);
+
+    // Make sure there is enough space for the first uint16_t
+    if (*OutBufferLength < sizeof(uint16_t))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        goto error;
+    }
+
+    // Iterate through each interface and build up the list of running interfaces
+    for (PLIST_ENTRY Link = FilterModuleList.Flink; Link != &FilterModuleList; Link = Link->Flink)
+    {
+        PMS_FILTER pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
+        if (pFilter->State != FilterRunning) continue;
+
+        PGUID pInterfaceGuid = &pInterfaceList->InterfaceGuids[pInterfaceList->cInterfaceGuids];
+        pInterfaceList->cInterfaceGuids++;
+
+        NewOutBufferLength =
+            FIELD_OFFSET(OTLWF_INTERFACE_LIST, InterfaceGuids) +
+            pInterfaceList->cInterfaceGuids * sizeof(GUID);
+
+        if (NewOutBufferLength <= *OutBufferLength)
+        {
+            *pInterfaceGuid = pFilter->InterfaceGuid;
+        }
+    }
+
+    if (NewOutBufferLength > *OutBufferLength)
+    {
+        NewOutBufferLength = sizeof(USHORT);
+    }
+
+error:
+
+    NdisReleaseSpinLock(&FilterListLock);
+
+    *OutBufferLength = NewOutBufferLength;
+
+    LogFuncExitNT(DRIVER_IOCTL, status);
+
+    return status;
+}
+
+// Handles queries for the details of a specific Thread interface
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtlQueryInterface(
+    _In_reads_bytes_(InBufferLength)
+            PVOID           InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG    NewOutBufferLength = 0;
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    // Make sure there is enough space for the first USHORT
+    if (InBufferLength < sizeof(GUID) || *OutBufferLength < sizeof(OTLWF_DEVICE))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        goto error;
+    }
+
+    PGUID pInterfaceGuid = (PGUID)InBuffer;
+    POTLWF_DEVICE pDevice = (POTLWF_DEVICE)OutBuffer;
+
+    // Look up the interface
+    PMS_FILTER pFilter = otLwfFindAndRefInterface(pInterfaceGuid);
+    if (pFilter == NULL)
+    {
+        status = STATUS_DEVICE_DOES_NOT_EXIST;
+        goto error;
+    }
+
+    NewOutBufferLength = sizeof(OTLWF_DEVICE);
+    pDevice->CompartmentID = pFilter->InterfaceCompartmentID;
+
+    // Release the ref on the interface
+    ExReleaseRundownProtection(&pFilter->ExternalRefs);
+
+error:
+
+    if (NewOutBufferLength < *OutBufferLength)
+    {
+        RtlZeroMemory((PUCHAR)OutBuffer + NewOutBufferLength, *OutBufferLength - NewOutBufferLength);
+    }
+
+    *OutBufferLength = NewOutBufferLength;
+
+    LogFuncExitNT(DRIVER_IOCTL, status);
+
+    return status;
+}
+
+// Handles IOTCLs for OpenThread control
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtlOpenThreadControl(
+    _In_ PIRP Irp
+    )
+{
+    NTSTATUS   status = STATUS_PENDING;
+    PMS_FILTER pFilter = NULL;
+
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+    LogFuncEntry(DRIVER_IOCTL);
+
+    if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(GUID))
+    {
+        status = STATUS_INVALID_PARAMETER;
+        goto error;
+    }
+
+    pFilter = otLwfFindAndRefInterface((PGUID)Irp->AssociatedIrp.SystemBuffer);
+    if (pFilter == NULL)
+    {
+        status = STATUS_DEVICE_DOES_NOT_EXIST;
+        goto error;
+    }
+
+    if (pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE)
+    {
+        // Pend the Irp for processing on the OpenThread event processing thread
+        otLwfEventProcessingIndicateIrp(pFilter, Irp);
+    }
+    else
+    {
+        status = otLwfTunIoCtl(pFilter, Irp);
+    }
+
+    // Release our ref on the filter
+    ExReleaseRundownProtection(&pFilter->ExternalRefs);
+
+error:
+
+    // Complete the IRP if we aren't pending (indicates we failed)
+    if (status != STATUS_PENDING)
+    {
+        NT_ASSERT(status != STATUS_SUCCESS);
+        RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength);
+        Irp->IoStatus.Status = status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    LogFuncExitNT(DRIVER_IOCTL, status);
+
+    return status;
+}
+
+// Handles Irp for IOTCLs for OpenThread control on the OpenThread thread
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfCompleteOpenThreadIrp(
+    _In_ PMS_FILTER     pFilter,
+    _In_ PIRP           Irp
+    )
+{
+    PIO_STACK_LOCATION  IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+    PUCHAR InBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer + sizeof(GUID);
+    PVOID OutBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+    ULONG InBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(GUID);
+    ULONG OutBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+    ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+    ULONG OrigOutBufferLength = OutBufferLength;
+
+    NTSTATUS status = STATUS_NOT_IMPLEMENTED;
+
+    ULONG FuncCode = ((IoControlCode >> 2) & 0xFFF) - 100;
+    if (FuncCode < ARRAYSIZE(IoCtls))
+    {
+        LogVerbose(DRIVER_IOCTL, "Processing Irp=%p, for %s (In:%u,Out:%u)",
+                    Irp, IoCtls[FuncCode].Name, InBufferLength, OutBufferLength);
+
+        if (IoCtls[FuncCode].otFunc)
+        {
+            status = IoCtls[FuncCode].otFunc(pFilter, InBuffer, InBufferLength, OutBuffer, &OutBufferLength);
+        }
+        else
+        {
+            OutBufferLength = 0;
+        }
+
+        LogVerbose(DRIVER_IOCTL, "Completing Irp=%p, with %!STATUS! for %s (Out:%u)",
+                    Irp, status, IoCtls[FuncCode].Name, OutBufferLength);
+    }
+    else
+    {
+        OutBufferLength = 0;
+    }
+
+    // Clear any leftover output buffer
+    if (OutBufferLength < OrigOutBufferLength)
+    {
+        RtlZeroMemory((PUCHAR)OutBuffer + OutBufferLength, OrigOutBufferLength - OutBufferLength);
+    }
+
+    // Complete the IRP
+    Irp->IoStatus.Information = OutBufferLength;
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+// Handles Irp for IOTCLs for OpenThread control on the OpenThread thread
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl(
+    _In_ PMS_FILTER     pFilter,
+    _In_ PIRP           Irp
+    )
+{
+    PIO_STACK_LOCATION  IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+    PUCHAR InBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer + sizeof(GUID);
+    ULONG InBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(GUID);
+    ULONG OutBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+    ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+
+    NTSTATUS status = STATUS_NOT_IMPLEMENTED;
+
+    ULONG FuncCode = ((IoControlCode >> 2) & 0xFFF) - 100;
+    if (FuncCode < ARRAYSIZE(IoCtls))
+    {
+        LogVerbose(DRIVER_IOCTL, "Processing Irp=%p, for %s (In:%u,Out:%u)",
+                    Irp, IoCtls[FuncCode].Name, InBufferLength, OutBufferLength);
+
+        if (IoCtls[FuncCode].tunFunc)
+        {
+            status = IoCtls[FuncCode].tunFunc(pFilter, Irp, InBuffer, InBufferLength, OutBufferLength);
+        }
+
+        if (!NT_SUCCESS(status))
+        {
+            LogVerbose(DRIVER_IOCTL, "Completing Irp=%p, with %!STATUS! for %s",
+                        Irp, status, IoCtls[FuncCode].Name);
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = STATUS_PENDING;
+
+        // Mark the Irp as pending
+        IoMarkIrpPending(Irp);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otInterface(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        BOOLEAN IsEnabled = *(BOOLEAN*)InBuffer;
+        if (IsEnabled)
+        {
+            // Make sure our addresses are in sync
+            (void)otLwfInitializeAddresses(pFilter);
+            otLwfRadioAddressesUpdated(pFilter);
+	}
+
+        status = ThreadErrorToNtstatus(otIp6SetEnabled(pFilter->otCtx, IsEnabled));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otIp6IsEnabled(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otInterface(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        BOOLEAN IsEnabled = *(BOOLEAN*)InBuffer;
+        if (IsEnabled)
+        {
+            // Make sure our addresses are in sync
+            (void)otLwfInitializeAddresses(pFilter);
+
+            // Sync the current addresses
+            KeSetEvent(&pFilter->TunWorkerThreadAddressChangedEvent, IO_NO_INCREMENT, FALSE);
+        }
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_IF_UP,
+                sizeof(BOOLEAN),
+                SPINEL_DATATYPE_BOOL_S,
+                IsEnabled);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otInterface_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_IF_UP,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otInterface_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_IF_UP)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_BOOL_S, (BOOLEAN*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otThread(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        BOOLEAN IsEnabled = *(BOOLEAN*)InBuffer;
+        status = ThreadErrorToNtstatus(otThreadSetEnabled(pFilter->otCtx, IsEnabled));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = (otThreadGetDeviceRole(pFilter->otCtx) > OT_DEVICE_ROLE_DISABLED) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otThread(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_STACK_UP,
+                sizeof(BOOLEAN),
+                SPINEL_DATATYPE_BOOL_S,
+                *(BOOLEAN*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otThread_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_STACK_UP,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otThread_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_STACK_UP)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_BOOL_S, (BOOLEAN*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otActiveScan(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint16_t))
+    {
+        uint32_t aScanChannels = *(uint32_t*)InBuffer;
+        uint16_t aScanDuration = *(uint16_t*)(InBuffer + sizeof(uint32_t));
+        status = ThreadErrorToNtstatus(
+            otLinkActiveScan(
+                pFilter->otCtx,
+                aScanChannels,
+                aScanDuration,
+                otLwfActiveScanCallback,
+                pFilter)
+            );
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otLinkIsActiveScanInProgress(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otActiveScan(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint16_t))
+    {
+        uint32_t aScanChannels = *(uint32_t*)InBuffer;
+        uint16_t aScanDuration = *(uint16_t*)(InBuffer + sizeof(uint32_t));
+        uint8_t aScanState = SPINEL_SCAN_STATE_BEACON;
+
+        // TODO - Send down scan channel & duration first
+        UNREFERENCED_PARAMETER(aScanChannels);
+        status = otLwfCmdSetProp(pFilter, SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_UINT16_S, aScanDuration);
+        if (!NT_SUCCESS(status)) goto error;
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_MAC_SCAN_STATE,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                aScanState);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otActiveScan_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_MAC_SCAN_STATE,
+                0,
+                NULL);
+    }
+
+error:
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otActiveScan_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_MAC_SCAN_STATE)
+    {
+        uint8_t aScanState = 0;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, &aScanState))
+        {
+            *(BOOLEAN*)OutBuffer = (aScanState == SPINEL_SCAN_STATE_BEACON) ? TRUE : FALSE;
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otEnergyScan(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint16_t))
+    {
+        uint32_t aScanChannels = *(uint32_t*)InBuffer;
+        uint16_t aScanDuration = *(uint16_t*)(InBuffer + sizeof(uint32_t));
+        status = ThreadErrorToNtstatus(
+            otLinkEnergyScan(
+                pFilter->otCtx,
+                aScanChannels,
+                aScanDuration,
+                otLwfEnergyScanCallback,
+                pFilter)
+            );
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otLinkIsEnergyScanInProgress(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otEnergyScan(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint16_t))
+    {
+        uint32_t aScanChannels = *(uint32_t*)InBuffer;
+        uint16_t aScanDuration = *(uint16_t*)(InBuffer + sizeof(uint32_t));
+        uint8_t aScanState = SPINEL_SCAN_STATE_ENERGY;
+
+        // TODO - Send down scan channel & duration first
+        UNREFERENCED_PARAMETER(aScanChannels);
+        status = otLwfCmdSetProp(pFilter, SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_UINT16_S, aScanDuration);
+        if (!NT_SUCCESS(status)) goto error;
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_MAC_SCAN_STATE,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                aScanState);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otEnergyScan_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_MAC_SCAN_STATE,
+                0,
+                NULL);
+    }
+
+error:
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otEnergyScan_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_MAC_SCAN_STATE)
+    {
+        uint8_t aScanState = 0;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, &aScanState))
+        {
+            *(BOOLEAN*)OutBuffer = (aScanState == SPINEL_SCAN_STATE_ENERGY) ? TRUE : FALSE;
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otDiscover(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint16_t) + sizeof(bool))
+    {
+        uint32_t aScanChannels = *(uint32_t*)InBuffer;
+        uint16_t aPanid = *(uint16_t*)(InBuffer + sizeof(uint32_t));
+        bool aJoiner = *(uint8_t*)(InBuffer + sizeof(uint32_t) + sizeof(uint16_t));
+        bool aEnableEui64Filtering = *(uint8_t*)(InBuffer + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t));
+        status = ThreadErrorToNtstatus(
+            otThreadDiscover(
+                pFilter->otCtx,
+                aScanChannels,
+                aPanid,
+                aJoiner,
+                aEnableEui64Filtering,
+                otLwfDiscoverCallback,
+                pFilter)
+            );
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otThreadIsDiscoverInProgress(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otChannel(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status = ThreadErrorToNtstatus(otLinkSetChannel(pFilter->otCtx, *(uint8_t*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otLinkGetChannel(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otChannel(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_PHY_CHAN,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otChannel_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_PHY_CHAN,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otChannel_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_PHY_CHAN)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otChildTimeout(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otThreadSetChildTimeout(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetChildTimeout(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint32_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otChildTimeout(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_CHILD_TIMEOUT,
+                sizeof(uint32_t),
+                SPINEL_DATATYPE_UINT32_S,
+                *(uint32_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otChildTimeout_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_CHILD_TIMEOUT,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otChildTimeout_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_CHILD_TIMEOUT)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT32_S, (uint32_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint32_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otExtendedAddress(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        status = ThreadErrorToNtstatus(otLinkSetExtendedAddress(pFilter->otCtx, (otExtAddress*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otExtAddress))
+    {
+        memcpy(OutBuffer, otLinkGetExtendedAddress(pFilter->otCtx), sizeof(otExtAddress));
+        *OutBufferLength = sizeof(otExtAddress);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otExtendedAddress(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_HWADDR,
+                sizeof(otExtAddress),
+                SPINEL_DATATYPE_EUI64_S,
+                (otExtAddress*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(otExtAddress))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otExtendedAddress_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_HWADDR,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otExtendedAddress_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_HWADDR)
+    {
+        spinel_eui64_t *data = NULL;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_EUI64_S, &data) && data != NULL)
+        {
+            memcpy(OutBuffer, data, sizeof(otExtAddress));
+            *OutBufferLength = sizeof(otExtAddress);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otExtendedPanId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otExtendedPanId))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetExtendedPanId(pFilter->otCtx, (uint8_t*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otExtendedPanId))
+    {
+        memcpy(OutBuffer, otThreadGetExtendedPanId(pFilter->otCtx), sizeof(otExtendedPanId));
+        *OutBufferLength = sizeof(otExtendedPanId);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otExtendedPanId(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otExtendedPanId))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_XPANID,
+                sizeof(otExtendedPanId) + sizeof(uint16_t),
+                SPINEL_DATATYPE_DATA_S,
+                (otExtendedPanId*)InBuffer,
+                sizeof(otExtendedPanId));
+    }
+    else if (OutBufferLength >= sizeof(otExtendedPanId))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otExtendedPanId_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_XPANID,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otExtendedPanId_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_XPANID)
+    {
+        uint8_t *data = NULL;
+        spinel_size_t aExtPanIdLen;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_DATA_S, &data, &aExtPanIdLen) && data != NULL &&
+            aExtPanIdLen == sizeof(otExtendedPanId))
+        {
+            memcpy(OutBuffer, data, sizeof(otExtendedPanId));
+            *OutBufferLength = sizeof(otExtendedPanId);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otFactoryAssignedIeeeEui64(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otExtAddress))
+    {
+        otLinkGetFactoryAssignedIeeeEui64(pFilter->otCtx, (otExtAddress*)OutBuffer);
+        *OutBufferLength = sizeof(otExtAddress);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otHashMacAddress(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otExtAddress))
+    {
+        otLinkGetJoinerId(pFilter->otCtx, (otExtAddress*)OutBuffer);
+        *OutBufferLength = sizeof(otExtAddress);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLeaderRloc(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otIp6Address))
+    {
+        status = ThreadErrorToNtstatus(otThreadGetLeaderRloc(pFilter->otCtx, (otIp6Address*)OutBuffer));
+        *OutBufferLength = sizeof(otIp6Address);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderRloc(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otIp6Address))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_LEADER_ADDR,
+                sizeof(otIp6Address),
+                SPINEL_DATATYPE_IPv6ADDR_S,
+                (otIp6Address*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(otIp6Address))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otLeaderRloc_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_LEADER_ADDR,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderRloc_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_LEADER_ADDR)
+    {
+        spinel_ipv6addr_t *data = NULL;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_IPv6ADDR_S, &data) && data != NULL)
+        {
+            memcpy(OutBuffer, data, sizeof(spinel_ipv6addr_t));
+            *OutBufferLength = sizeof(otIp6Address);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLinkMode(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    static_assert(sizeof(otLinkModeConfig) == 4, "The size of otLinkModeConfig should be 4 bytes");
+    if (InBufferLength >= sizeof(otLinkModeConfig))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetLinkMode(pFilter->otCtx, *(otLinkModeConfig*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otLinkModeConfig))
+    {
+        *(otLinkModeConfig*)OutBuffer = otThreadGetLinkMode(pFilter->otCtx);
+        *OutBufferLength = sizeof(otLinkModeConfig);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+enum
+{
+    kThreadMode_RxOnWhenIdle        = (1 << 3),
+    kThreadMode_SecureDataRequest   = (1 << 2),
+    kThreadMode_FullFunctionDevice  = (1 << 1),
+    kThreadMode_FullNetworkData     = (1 << 0),
+};
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLinkMode(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otLinkModeConfig))
+    {
+        const otLinkModeConfig* aLinkMode = (otLinkModeConfig*)InBuffer;
+        uint8_t numeric_mode = 0;
+
+        if (aLinkMode->mRxOnWhenIdle)       numeric_mode |= kThreadMode_RxOnWhenIdle;
+        if (aLinkMode->mSecureDataRequests) numeric_mode |= kThreadMode_SecureDataRequest;
+        if (aLinkMode->mDeviceType)         numeric_mode |= kThreadMode_FullFunctionDevice;
+        if (aLinkMode->mNetworkData)        numeric_mode |= kThreadMode_FullNetworkData;
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_MODE,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                numeric_mode);
+    }
+    else if (OutBufferLength >= sizeof(otLinkModeConfig))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otLinkMode_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_MODE,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLinkMode_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_MODE)
+    {
+		uint8_t numeric_mode = 0;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, &numeric_mode))
+        {
+            otLinkModeConfig* aLinkMode = (otLinkModeConfig*)OutBuffer;
+
+            aLinkMode->mRxOnWhenIdle = ((numeric_mode & kThreadMode_RxOnWhenIdle) == kThreadMode_RxOnWhenIdle);
+            aLinkMode->mSecureDataRequests = ((numeric_mode & kThreadMode_SecureDataRequest) == kThreadMode_SecureDataRequest);
+            aLinkMode->mDeviceType = ((numeric_mode & kThreadMode_FullFunctionDevice) == kThreadMode_FullFunctionDevice);
+            aLinkMode->mNetworkData = ((numeric_mode & kThreadMode_FullNetworkData) == kThreadMode_FullNetworkData);
+
+            *OutBufferLength = sizeof(otLinkModeConfig);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMasterKey(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otMasterKey))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetMasterKey(pFilter->otCtx, (otMasterKey*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otMasterKey))
+    {
+        const otMasterKey* aMasterKey = otThreadGetMasterKey(pFilter->otCtx);
+        memcpy(OutBuffer, aMasterKey, sizeof(otMasterKey));
+        *OutBufferLength = sizeof(otMasterKey);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMasterKey(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otMasterKey))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_MASTER_KEY,
+                sizeof(otMasterKey) + sizeof(uint16_t),
+                SPINEL_DATATYPE_DATA_S,
+                (otMasterKey*)InBuffer,
+                sizeof(otMasterKey));
+    }
+    else if (OutBufferLength >= sizeof(otMasterKey))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otMasterKey_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_MASTER_KEY,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMasterKey_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_MASTER_KEY)
+    {
+        uint8_t *data = NULL;
+        spinel_size_t aKeyLength;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_DATA_S, &data, &aKeyLength) && data != NULL &&
+            aKeyLength == sizeof(otMasterKey))
+        {
+            memcpy(OutBuffer, data, sizeof(otMasterKey));
+            *OutBufferLength = sizeof(otMasterKey);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPSKc(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otPSKc))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetPSKc(pFilter->otCtx, InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otPSKc))
+    {
+        const uint8_t* aPSKc = otThreadGetPSKc(pFilter->otCtx);
+        memcpy(OutBuffer, aPSKc, sizeof(otPSKc));
+        *OutBufferLength = sizeof(otPSKc);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPSKc(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otPSKc))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_PSKC,
+                sizeof(otPSKc) + sizeof(uint16_t),
+                SPINEL_DATATYPE_DATA_S,
+                (otPSKc*)InBuffer,
+                OT_PSKC_MAX_SIZE);
+    }
+    else if (OutBufferLength >= sizeof(otPSKc))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otPSKc_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_PSKC,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPSKc_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_PSKC)
+    {
+        uint8_t *data = NULL;
+        spinel_size_t aPSKcLength;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_DATA_S, &data, &aPSKcLength) && data != NULL &&
+            aPSKcLength == sizeof(otPSKc))
+        {
+            memcpy(OutBuffer, data, sizeof(otPSKc));
+            *OutBufferLength = sizeof(otPSKc);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMeshLocalEid(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otIp6Address))
+    {
+        memcpy(OutBuffer,  otThreadGetMeshLocalEid(pFilter->otCtx), sizeof(otIp6Address));
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMeshLocalEid(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(otIp6Address))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otMeshLocalEid_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_IPV6_ML_ADDR,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMeshLocalEid_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_IPV6_ML_ADDR)
+    {
+        spinel_ipv6addr_t *data = NULL;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_IPv6ADDR_S, &data) && data != NULL)
+        {
+            memcpy(OutBuffer, data, sizeof(spinel_ipv6addr_t));
+            *OutBufferLength = sizeof(otIp6Address);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMeshLocalPrefix(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otMeshLocalPrefix))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetMeshLocalPrefix(pFilter->otCtx, InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otMeshLocalPrefix))
+    {
+        memcpy(OutBuffer, otThreadGetMeshLocalPrefix(pFilter->otCtx), sizeof(otMeshLocalPrefix));
+        *OutBufferLength = sizeof(otMeshLocalPrefix);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMeshLocalPrefix(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otMeshLocalPrefix))
+    {
+        otIp6Address aAddress = {0};
+        memcpy(&aAddress, InBuffer, sizeof(otMeshLocalPrefix));
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_IPV6_ML_PREFIX,
+                sizeof(otIp6Address),
+                SPINEL_DATATYPE_IPv6ADDR_S,
+                &aAddress);
+    }
+    else if (OutBufferLength >= sizeof(otMeshLocalPrefix))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otMeshLocalPrefix_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_IPV6_ML_PREFIX,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMeshLocalPrefix_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_IPV6_ML_PREFIX)
+    {
+        if (DataLength >= sizeof(otMeshLocalPrefix))
+        {
+            memcpy(OutBuffer, Data, sizeof(otMeshLocalPrefix));
+            *OutBufferLength = sizeof(otMeshLocalPrefix);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+// otLwfIoCtl_otNetworkDataLeader
+
+// otLwfIoCtl_otNetworkDataLocal
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otNetworkName(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otNetworkName))
+    {
+        status = ThreadErrorToNtstatus(otThreadSetNetworkName(pFilter->otCtx, (char*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otNetworkName))
+    {
+        strcpy_s((char*)OutBuffer, sizeof(otNetworkName), otThreadGetNetworkName(pFilter->otCtx));
+        *OutBufferLength = sizeof(otNetworkName);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkName(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otNetworkName))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_NETWORK_NAME,
+                sizeof(otIp6Address),
+                SPINEL_DATATYPE_UTF8_S,
+                (otNetworkName*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(otNetworkName))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otNetworkName_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_NETWORK_NAME,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkName_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_NETWORK_NAME)
+    {
+        const char *data = NULL;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UTF8_S, &data) && data != NULL)
+        {
+            strcpy_s(OutBuffer, sizeof(otNetworkName), data);
+            *OutBufferLength = sizeof(otNetworkName);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPanId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otPanId))
+    {
+        status = ThreadErrorToNtstatus(otLinkSetPanId(pFilter->otCtx, *(otPanId*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otPanId))
+    {
+        *(otPanId*)OutBuffer = otLinkGetPanId(pFilter->otCtx);
+        *OutBufferLength = sizeof(otPanId);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPanId(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otPanId))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_MAC_15_4_PANID,
+                sizeof(otPanId),
+                SPINEL_DATATYPE_UINT16_S,
+                *(otPanId*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(otPanId))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otPanId_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_MAC_15_4_PANID,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPanId_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_MAC_15_4_PANID)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT16_S, (otPanId*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(otPanId);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterRollEnabled(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        otThreadSetRouterRoleEnabled(pFilter->otCtx, *(BOOLEAN*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otThreadIsRouterRoleEnabled(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterRollEnabled(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED,
+                sizeof(BOOLEAN),
+                SPINEL_DATATYPE_BOOL_S,
+                *(BOOLEAN*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otRouterRollEnabled_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterRollEnabled_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_BOOL_S, (BOOLEAN*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otShortAddress(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otShortAddress))
+    {
+        *(otShortAddress*)OutBuffer = otLinkGetShortAddress(pFilter->otCtx);
+        *OutBufferLength = sizeof(otShortAddress);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otShortAddress(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(otShortAddress))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otShortAddress_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_MAC_15_4_SADDR,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otShortAddress_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_MAC_15_4_SADDR)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT16_S, (otShortAddress*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(otShortAddress);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+// otLwfIoCtl_otUnicastAddresses
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otActiveDataset(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otOperationalDataset))
+    {
+        status = ThreadErrorToNtstatus(otDatasetSetActive(pFilter->otCtx, (otOperationalDataset*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otOperationalDataset))
+    {
+        status = ThreadErrorToNtstatus(otDatasetGetActive(pFilter->otCtx, (otOperationalDataset*)OutBuffer));
+        *OutBufferLength = sizeof(otOperationalDataset);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPendingDataset(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otOperationalDataset))
+    {
+        status = ThreadErrorToNtstatus(otDatasetSetPending(pFilter->otCtx, (otOperationalDataset*)InBuffer));
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(otOperationalDataset))
+    {
+        status = ThreadErrorToNtstatus(otDatasetGetPending(pFilter->otCtx, (otOperationalDataset*)OutBuffer));
+        *OutBufferLength = sizeof(otOperationalDataset);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLocalLeaderWeight(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetLocalLeaderWeight(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetLeaderWeight(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLocalLeaderWeight(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otLocalLeaderWeight_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLocalLeaderWeight_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otAddBorderRouter(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otBorderRouterConfig))
+    {
+        status = ThreadErrorToNtstatus(otBorderRouterAddOnMeshPrefix(pFilter->otCtx, (otBorderRouterConfig*)InBuffer));
+    }
+
+    return status;
+}
+
+const uint8_t kPreferenceOffset = 6;
+//const uint8_t kPreferenceMask = 3 << kPreferenceOffset;
+const uint8_t kPreferredFlag = 1 << 5;
+const uint8_t kSlaacFlag = 1 << 4;
+const uint8_t kDhcpFlag = 1 << 3;
+const uint8_t kConfigureFlag = 1 << 2;
+const uint8_t kDefaultRouteFlag = 1 << 1;
+const uint8_t kOnMeshFlag = 1 << 0;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otAddBorderRouter(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otBorderRouterConfig))
+    {
+        const otBorderRouterConfig *aConfig = (otBorderRouterConfig*)InBuffer;
+
+        otIp6Address prefix = {0};
+        memcpy_s(&prefix, sizeof(prefix), &aConfig->mPrefix.mPrefix, aConfig->mPrefix.mLength);
+
+        uint8_t flags = (uint8_t)(aConfig->mPreference << kPreferenceOffset);
+        if (aConfig->mSlaac)        flags |= kSlaacFlag;
+        if (aConfig->mDhcp)         flags |= kDhcpFlag;
+        if (aConfig->mConfigure)    flags |= kConfigureFlag;
+        if (aConfig->mDefaultRoute) flags |= kDefaultRouteFlag;
+        if (aConfig->mOnMesh)       flags |= kOnMeshFlag;
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_INSERT,
+                SPINEL_PROP_THREAD_ON_MESH_NETS,
+                sizeof(otIp6Address) + 3 * sizeof(uint8_t),
+                "6CbC",
+                &prefix,
+                aConfig->mPrefix.mLength,
+                aConfig->mStable,
+                flags);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRemoveBorderRouter(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otIp6Prefix))
+    {
+        status = ThreadErrorToNtstatus(otBorderRouterRemoveOnMeshPrefix(pFilter->otCtx, (otIp6Prefix*)InBuffer));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRemoveBorderRouter(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otIp6Prefix))
+    {
+        const otIp6Prefix *aPrefix = (otIp6Prefix*)InBuffer;
+
+        otIp6Address prefix = {0};
+        memcpy_s(&prefix, sizeof(prefix), &aPrefix->mPrefix, aPrefix->mLength);
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_REMOVE,
+                SPINEL_PROP_THREAD_ON_MESH_NETS,
+                sizeof(otIp6Address) + sizeof(uint8_t),
+                "6C",
+                &prefix,
+                aPrefix->mLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otAddExternalRoute(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otExternalRouteConfig))
+    {
+        status = ThreadErrorToNtstatus(otBorderRouterAddRoute(pFilter->otCtx, (otExternalRouteConfig*)InBuffer));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otAddExternalRoute(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otBorderRouterConfig))
+    {
+        const otBorderRouterConfig *aConfig = (otBorderRouterConfig*)InBuffer;
+
+        otIp6Address prefix = {0};
+        memcpy_s(&prefix, sizeof(prefix), &aConfig->mPrefix.mPrefix, aConfig->mPrefix.mLength);
+
+        uint8_t flags = (uint8_t)(aConfig->mPreference << kPreferenceOffset);
+        if (aConfig->mSlaac)        flags |= kSlaacFlag;
+        if (aConfig->mDhcp)         flags |= kDhcpFlag;
+        if (aConfig->mConfigure)    flags |= kConfigureFlag;
+        if (aConfig->mDefaultRoute) flags |= kDefaultRouteFlag;
+        if (aConfig->mOnMesh)       flags |= kOnMeshFlag;
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_INSERT,
+                SPINEL_PROP_THREAD_OFF_MESH_ROUTES,
+                sizeof(otIp6Address) + 3 * sizeof(uint8_t),
+                "6CbC",
+                &prefix,
+                aConfig->mPrefix.mLength,
+                aConfig->mStable,
+                flags);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRemoveExternalRoute(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otIp6Prefix))
+    {
+        status = ThreadErrorToNtstatus(otBorderRouterRemoveRoute(pFilter->otCtx, (otIp6Prefix*)InBuffer));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRemoveExternalRoute(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otIp6Prefix))
+    {
+        const otIp6Prefix *aPrefix = (otIp6Prefix*)InBuffer;
+
+        otIp6Address prefix = {0};
+        memcpy_s(&prefix, sizeof(prefix), &aPrefix->mPrefix, aPrefix->mLength);
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_REMOVE,
+                SPINEL_PROP_THREAD_OFF_MESH_ROUTES,
+                sizeof(otIp6Address) + sizeof(uint8_t),
+                "6C",
+                &prefix,
+                aPrefix->mLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendServerData(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    status = ThreadErrorToNtstatus(otBorderRouterRegister(pFilter->otCtx));
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otContextIdReuseDelay(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otThreadSetContextIdReuseDelay(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetContextIdReuseDelay(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint32_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otContextIdReuseDelay(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY,
+                sizeof(uint32_t),
+                SPINEL_DATATYPE_UINT32_S,
+                *(uint32_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otContextIdReuseDelay_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otContextIdReuseDelay_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT32_S, (uint32_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint32_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otKeySequenceCounter(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otThreadSetKeySequenceCounter(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetKeySequenceCounter(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint32_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otKeySequenceCounter(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER,
+                sizeof(uint32_t),
+                SPINEL_DATATYPE_UINT32_S,
+                *(uint32_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otKeySequenceCounter_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otKeySequenceCounter_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT32_S, (uint32_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint32_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otNetworkIdTimeout(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetNetworkIdTimeout(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetNetworkIdTimeout(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint8_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkIdTimeout(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otNetworkIdTimeout_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkIdTimeout_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterUpgradeThreshold(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetRouterUpgradeThreshold(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetRouterUpgradeThreshold(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint8_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterUpgradeThreshold(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otRouterUpgradeThreshold_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterUpgradeThreshold_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterDowngradeThreshold(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetRouterDowngradeThreshold(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetRouterDowngradeThreshold(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint8_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterDowngradeThreshold(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otRouterDowngradeThreshold_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterDowngradeThreshold_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otReleaseRouterId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status = ThreadErrorToNtstatus(otThreadReleaseRouterId(pFilter->otCtx, *(uint8_t*)InBuffer));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otReleaseRouterId(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_REMOVE,
+                SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMacWhitelistEnabled(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        BOOLEAN aEnabled = *(BOOLEAN*)InBuffer;
+        otLinkSetWhitelistEnabled(pFilter->otCtx, aEnabled);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otLinkIsWhitelistEnabled(pFilter->otCtx) ? TRUE : FALSE;
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(BOOLEAN);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMacWhitelistEnabled(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_MAC_WHITELIST_ENABLED,
+                sizeof(BOOLEAN),
+                SPINEL_DATATYPE_BOOL_S,
+                *(BOOLEAN*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otMacWhitelistEnabled_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_MAC_WHITELIST_ENABLED,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMacWhitelistEnabled_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_MAC_WHITELIST_ENABLED)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_BOOL_S, (BOOLEAN*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(BOOLEAN);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otAddMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otExtAddress) + sizeof(int8_t))
+    {
+        int8_t aRssi = *(int8_t*)(InBuffer + sizeof(otExtAddress));
+        status = ThreadErrorToNtstatus(otLinkAddWhitelistRssi(pFilter->otCtx, (uint8_t*)InBuffer, aRssi));
+    }
+    else if (InBufferLength >= sizeof(otExtAddress))
+    {
+        status = ThreadErrorToNtstatus(otLinkAddWhitelist(pFilter->otCtx, (uint8_t*)InBuffer));
+    }
+
+    return status;
+}
+
+#define RSSI_OVERRIDE_DISABLED        127 // Used for PROP_MAC_WHITELIST
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otAddMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        int8_t aRssi = RSSI_OVERRIDE_DISABLED;
+        if (InBufferLength >= sizeof(otExtAddress) + sizeof(int8_t))
+        {
+            aRssi = *(int8_t*)(InBuffer + sizeof(otExtAddress));
+        }
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_INSERT,
+                SPINEL_PROP_MAC_WHITELIST,
+                sizeof(otExtAddress) + sizeof(int8_t),
+                "Ec",
+                (otExtAddress*)InBuffer,
+                &aRssi);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRemoveMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        otLinkRemoveWhitelist(pFilter->otCtx, (uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRemoveMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_REMOVE,
+                SPINEL_PROP_MAC_WHITELIST,
+                sizeof(otExtAddress),
+                "E",
+                (otExtAddress*)InBuffer);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMacWhitelistEntry(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t) &&
+        *OutBufferLength >= sizeof(otMacWhitelistEntry))
+    {
+        status = ThreadErrorToNtstatus(
+            otLinkGetWhitelistEntry(
+                pFilter->otCtx,
+                *(uint8_t*)InBuffer,
+                (otMacWhitelistEntry*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otMacWhitelistEntry);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otClearMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    otLinkClearWhitelist(pFilter->otCtx);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otClearMacWhitelist(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    status =
+        otLwfTunSendCommandForIrp(
+            pFilter,
+            pIrp,
+            NULL,
+            SPINEL_CMD_PROP_VALUE_SET,
+            SPINEL_PROP_MAC_WHITELIST,
+            0,
+            NULL);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otDeviceRole(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otDeviceRole role = *(uint8_t*)InBuffer;
+
+        InBufferLength -= sizeof(uint8_t);
+        InBuffer = InBuffer + sizeof(uint8_t);
+
+        if (role == OT_DEVICE_ROLE_LEADER)
+        {
+            status = ThreadErrorToNtstatus(
+                        otThreadBecomeLeader(pFilter->otCtx)
+                        );
+        }
+        else if (role == OT_DEVICE_ROLE_ROUTER)
+        {
+            status = ThreadErrorToNtstatus(
+                        otThreadBecomeRouter(pFilter->otCtx)
+                        );
+        }
+        else if (role == OT_DEVICE_ROLE_CHILD)
+        {
+            status = ThreadErrorToNtstatus(
+                        otThreadBecomeChild(pFilter->otCtx)
+                        );
+        }
+        else if (role == OT_DEVICE_ROLE_DETACHED)
+        {
+            status = ThreadErrorToNtstatus(
+                        otThreadBecomeDetached(pFilter->otCtx)
+                        );
+        }
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = (uint8_t)otThreadGetDeviceRole(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otDeviceRole(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otDeviceRole role = *(uint8_t*)InBuffer;
+        uint8_t spinel_role = SPINEL_NET_ROLE_DETACHED;
+
+        switch (role)
+        {
+        case OT_DEVICE_ROLE_CHILD:
+            spinel_role = SPINEL_NET_ROLE_CHILD;
+            break;
+        case OT_DEVICE_ROLE_ROUTER:
+            spinel_role = SPINEL_NET_ROLE_ROUTER;
+            break;
+        case OT_DEVICE_ROLE_LEADER:
+            spinel_role = SPINEL_NET_ROLE_LEADER;
+            break;
+        }
+
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_ROLE,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                spinel_role);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otDeviceRole_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_ROLE,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otDeviceRole_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_ROLE)
+    {
+		uint8_t spinel_role = 0;
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, &spinel_role))
+        {
+            switch (spinel_role)
+            {
+            default:
+            case SPINEL_NET_ROLE_DETACHED:
+                *(uint8_t*)OutBuffer = OT_DEVICE_ROLE_DETACHED;
+                break;
+            case SPINEL_NET_ROLE_CHILD:
+                *(uint8_t*)OutBuffer = OT_DEVICE_ROLE_CHILD;
+                break;
+            case SPINEL_NET_ROLE_ROUTER:
+                *(uint8_t*)OutBuffer = OT_DEVICE_ROLE_ROUTER;
+                break;
+            case SPINEL_NET_ROLE_LEADER:
+                *(uint8_t*)OutBuffer = OT_DEVICE_ROLE_LEADER;
+                break;
+            }
+
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otChildInfoById(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint16_t) &&
+        *OutBufferLength >= sizeof(otChildInfo))
+    {
+        status = ThreadErrorToNtstatus(
+            otThreadGetChildInfoById(
+                pFilter->otCtx,
+                *(uint16_t*)InBuffer,
+                (otChildInfo*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otChildInfo);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otChildInfoByIndex(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t) &&
+        *OutBufferLength >= sizeof(otChildInfo))
+    {
+        status = ThreadErrorToNtstatus(
+            otThreadGetChildInfoByIndex(
+                pFilter->otCtx,
+                *(uint8_t*)InBuffer,
+                (otChildInfo*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otChildInfo);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otEidCacheEntry(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t) &&
+        *OutBufferLength >= sizeof(otEidCacheEntry))
+    {
+        status = ThreadErrorToNtstatus(
+            otThreadGetEidCacheEntry(
+                pFilter->otCtx,
+                *(uint8_t*)InBuffer,
+                (otEidCacheEntry*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otEidCacheEntry);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLeaderData(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otLeaderData))
+    {
+        status = ThreadErrorToNtstatus(otThreadGetLeaderData(pFilter->otCtx, (otLeaderData*)OutBuffer));
+        *OutBufferLength = sizeof(otLeaderData);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLeaderRouterId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetLeaderRouterId(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderRouterId(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otLeaderRouterId_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_LEADER_RID,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderRouterId_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_LEADER_RID)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLeaderWeight(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetLeaderWeight(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderWeight(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otLeaderWeight_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_LEADER_WEIGHT,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otLeaderWeight_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_LEADER_WEIGHT)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otNetworkDataVersion(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otNetDataGetVersion(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkDataVersion(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otNetworkDataVersion_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_NETWORK_DATA_VERSION,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otNetworkDataVersion_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_NETWORK_DATA_VERSION)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPartitionId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetPartitionId(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint32_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPartitionId(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otPartitionId_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_PARTITION_ID,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPartitionId_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_PARTITION_ID)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT32_S, (uint32_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint32_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRloc16(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint16_t))
+    {
+        *(uint16_t*)OutBuffer = otThreadGetRloc16(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint16_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRloc16(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint16_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otRloc16_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_RLOC16,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRloc16_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_RLOC16)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT16_S, (uint16_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint16_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterIdSequence(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetRouterIdSequence(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterInfo(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint16_t) &&
+        *OutBufferLength >= sizeof(otRouterInfo))
+    {
+        status = ThreadErrorToNtstatus(
+            otThreadGetRouterInfo(
+                pFilter->otCtx,
+                *(uint16_t*)InBuffer,
+                (otRouterInfo*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otRouterInfo);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otStableNetworkDataVersion(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otNetDataGetStableVersion(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otStableNetworkDataVersion(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otStableNetworkDataVersion_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otStableNetworkDataVersion_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMacBlacklistEnabled(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        BOOLEAN aEnabled = *(BOOLEAN*)InBuffer;
+        otLinkSetBlacklistEnabled(pFilter->otCtx, aEnabled);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otLinkIsBlacklistEnabled(pFilter->otCtx) ? TRUE : FALSE;
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(BOOLEAN);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otAddMacBlacklist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        status = ThreadErrorToNtstatus(otLinkAddBlacklist(pFilter->otCtx, (uint8_t*)InBuffer));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRemoveMacBlacklist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otExtAddress))
+    {
+        otLinkRemoveBlacklist(pFilter->otCtx, (uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMacBlacklistEntry(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t) &&
+        *OutBufferLength >= sizeof(otMacBlacklistEntry))
+    {
+        status = ThreadErrorToNtstatus(
+            otLinkGetBlacklistEntry(
+                pFilter->otCtx,
+                *(uint8_t*)InBuffer,
+                (otMacBlacklistEntry*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(otMacBlacklistEntry);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otClearMacBlacklist(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    otLinkClearBlacklist(pFilter->otCtx);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMaxTransmitPower(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(int8_t))
+    {
+        otLinkSetMaxTransmitPower(pFilter->otCtx, *(int8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(int8_t))
+    {
+        *(int8_t*)OutBuffer = otLinkGetMaxTransmitPower(pFilter->otCtx);
+        *OutBufferLength = sizeof(int8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otNextOnMeshPrefix(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN) + sizeof(uint8_t) &&
+        *OutBufferLength >= sizeof(uint8_t) + sizeof(otBorderRouterConfig))
+    {
+        BOOLEAN aLocal = *(BOOLEAN*)InBuffer;
+        uint8_t aIterator = *(uint8_t*)(InBuffer + sizeof(BOOLEAN));
+        otBorderRouterConfig* aConfig = (otBorderRouterConfig*)((PUCHAR)OutBuffer + sizeof(uint8_t));
+        if (aLocal)
+        {
+            status = ThreadErrorToNtstatus(
+                otBorderRouterGetNextOnMeshPrefix(
+                    pFilter->otCtx,
+                    &aIterator,
+                    aConfig)
+                );
+        }
+        else
+        {
+            status = ThreadErrorToNtstatus(
+                otNetDataGetNextOnMeshPrefix(
+                    pFilter->otCtx,
+                    &aIterator,
+                    aConfig)
+                );
+        }
+        *OutBufferLength = sizeof(uint8_t) + sizeof(otBorderRouterConfig);
+        if (status == STATUS_SUCCESS)
+        {
+            *(uint8_t*)OutBuffer = aIterator;
+        }
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPollPeriod(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otLinkSetPollPeriod(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otLinkGetPollPeriod(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint32_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otLocalLeaderPartitionId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otThreadSetLocalLeaderPartitionId(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetLocalLeaderPartitionId(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint32_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otAssignLinkQuality(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(otExtAddress) + sizeof(uint8_t))
+    {
+        otLinkSetAssignLinkQuality(
+            pFilter->otCtx,
+            (uint8_t*)InBuffer,
+            *(uint8_t*)(InBuffer + sizeof(otExtAddress)));
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (InBufferLength >= sizeof(otExtAddress) &&
+            *OutBufferLength >= sizeof(uint8_t))
+    {
+        status = ThreadErrorToNtstatus(
+            otLinkGetAssignLinkQuality(
+                pFilter->otCtx,
+                (uint8_t*)InBuffer,
+                (uint8_t*)OutBuffer)
+            );
+        *OutBufferLength = sizeof(uint32_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otPlatformReset(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    otInstanceReset(pFilter->otCtx);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otFactoryReset(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    otInstanceFactoryReset(pFilter->otCtx);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otPlatformReset(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBufferLength);
+
+    status =
+        otLwfTunSendCommandForIrp(
+            pFilter,
+            pIrp,
+            NULL,
+            SPINEL_CMD_RESET,
+            0,
+            0,
+            NULL);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otParentInfo(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    static_assert(sizeof(otRouterInfo) == 20, "The size of otRouterInfo should be 20 bytes");
+    if (*OutBufferLength >= sizeof(otRouterInfo))
+    {
+        status = ThreadErrorToNtstatus(otThreadGetParentInfo(pFilter->otCtx, (otRouterInfo*)OutBuffer));
+        *OutBufferLength = sizeof(otRouterInfo);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otParentInfo(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (OutBufferLength >= sizeof(otRouterInfo))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otParentInfo_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_PARENT,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otParentInfo_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_PARENT)
+    {
+        otRouterInfo* aRouterInfo = (otRouterInfo*)OutBuffer;
+        RtlZeroMemory(aRouterInfo, sizeof(otRouterInfo));
+        if (try_spinel_datatype_unpack(Data, DataLength, "ES", &aRouterInfo->mExtAddress.m8, &aRouterInfo->mRloc16))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSingleton(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otThreadIsSingleton(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMacCounters(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+
+    if (*OutBufferLength >= sizeof(otMacCounters))
+    {
+        memcpy_s(OutBuffer, *OutBufferLength, otLinkGetCounters(pFilter->otCtx), sizeof(otMacCounters));
+        *OutBufferLength = sizeof(otMacCounters);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otMaxChildren(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetMaxAllowedChildren(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetMaxAllowedChildren(pFilter->otCtx);
+        *OutBufferLength = sizeof(uint8_t);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMaxChildren(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_CHILD_COUNT_MAX,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otMaxChildren_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_CHILD_COUNT_MAX,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otMaxChildren_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_CHILD_COUNT_MAX)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerStart(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    return ThreadErrorToNtstatus(otCommissionerStart(pFilter->otCtx));
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerStop(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    status = ThreadErrorToNtstatus(otCommissionerStop(pFilter->otCtx));
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otJoinerStart(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(otCommissionConfig))
+    {
+        otCommissionConfig *aConfig = (otCommissionConfig*)InBuffer;
+
+#define IsNotNullTerminated(buf) (strnlen(buf, sizeof(buf)) == sizeof(buf))
+
+        if (IsNotNullTerminated(aConfig->PSKd) ||
+            IsNotNullTerminated(aConfig->ProvisioningUrl) ||
+            IsNotNullTerminated(aConfig->VendorName) ||
+            IsNotNullTerminated(aConfig->VendorModel) ||
+            IsNotNullTerminated(aConfig->VendorSwVersion) ||
+            IsNotNullTerminated(aConfig->VendorData))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            strcpy_s(pFilter->otVendorName, sizeof(pFilter->otVendorName), aConfig->VendorName);
+            strcpy_s(pFilter->otVendorModel, sizeof(pFilter->otVendorModel), aConfig->VendorModel);
+            strcpy_s(pFilter->otVendorSwVersion, sizeof(pFilter->otVendorSwVersion), aConfig->VendorSwVersion);
+            strcpy_s(pFilter->otVendorData, sizeof(pFilter->otVendorData), aConfig->VendorData);
+
+            status = ThreadErrorToNtstatus(
+                otJoinerStart(
+                    pFilter->otCtx,
+                    aConfig->PSKd,
+                    aConfig->ProvisioningUrl,
+                    pFilter->otVendorName,
+                    pFilter->otVendorModel,
+                    pFilter->otVendorSwVersion,
+                    pFilter->otVendorData[0] == '\0' ? NULL : pFilter->otVendorData,
+                    otLwfJoinerCallback,
+                    pFilter)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otJoinerStop(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(InBuffer);
+    UNREFERENCED_PARAMETER(InBufferLength);
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    status = ThreadErrorToNtstatus(otJoinerStop(pFilter->otCtx));
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerPanIdQuery(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint16_t) + sizeof(uint32_t) + sizeof(otIp6Address))
+    {
+        uint16_t aPanId = *(uint16_t*)InBuffer;
+        uint32_t aChannelMask = *(uint32_t*)(InBuffer + sizeof(uint16_t));
+        const otIp6Address *aAddress = (otIp6Address*)(InBuffer + sizeof(uint16_t) + sizeof(uint32_t));
+
+        status = ThreadErrorToNtstatus(
+            otCommissionerPanIdQuery(
+                pFilter->otCtx,
+                aPanId,
+                aChannelMask,
+                aAddress,
+                otLwfCommissionerPanIdConflictCallback,
+                pFilter)
+            );
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerEnergyScan(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(otIp6Address))
+    {
+        uint32_t aChannelMask = *(uint32_t*)InBuffer;
+        uint8_t aCount = *(uint8_t*)(InBuffer + sizeof(uint32_t));
+        uint16_t aPeriod = *(uint16_t*)(InBuffer + sizeof(uint32_t) + sizeof(uint8_t));
+        uint16_t aScanDuration = *(uint16_t*)(InBuffer + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t));
+        const otIp6Address *aAddress = (otIp6Address*)(InBuffer + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t));
+
+        status = ThreadErrorToNtstatus(
+            otCommissionerEnergyScan(
+                pFilter->otCtx,
+                aChannelMask,
+                aCount,
+                aPeriod,
+                aScanDuration,
+                aAddress,
+                otLwfCommissionerEnergyReportCallback,
+                pFilter)
+            );
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otRouterSelectionJitter(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        otThreadSetRouterSelectionJitter(pFilter->otCtx, *(uint8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint8_t))
+    {
+        *(uint8_t*)OutBuffer = otThreadGetRouterSelectionJitter(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint8_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterSelectionJitter(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER,
+                sizeof(uint8_t),
+                SPINEL_DATATYPE_UINT8_S,
+                *(uint8_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otRouterSelectionJitter_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otRouterSelectionJitter_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT8_S, (uint8_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint8_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otJoinerUdpPort(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint16_t))
+    {
+        otThreadSetJoinerUdpPort(pFilter->otCtx, *(uint16_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint16_t))
+    {
+        *(uint16_t*)OutBuffer = otThreadGetJoinerUdpPort(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint16_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendDiagnosticGet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(otIp6Address) + sizeof(uint8_t))
+    {
+        const otIp6Address *aAddress = (otIp6Address*)InBuffer;
+        uint8_t aCount = *(uint8_t*)(InBuffer + sizeof(otIp6Address));
+        PUCHAR aTlvTypes = InBuffer + sizeof(otIp6Address) + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(otIp6Address) + sizeof(uint8_t) + aCount)
+        {
+            status = ThreadErrorToNtstatus(
+                otThreadSendDiagnosticGet(
+                    pFilter->otCtx,
+                    aAddress,
+                    aTlvTypes,
+                    aCount)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendDiagnosticReset(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(otIp6Address) + sizeof(uint8_t))
+    {
+        const otIp6Address *aAddress = (otIp6Address*)InBuffer;
+        uint8_t aCount = *(uint8_t*)(InBuffer + sizeof(otIp6Address));
+        PUCHAR aTlvTypes = InBuffer + sizeof(otIp6Address) + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(otIp6Address) + sizeof(uint8_t) + aCount)
+        {
+            status = ThreadErrorToNtstatus(
+                otThreadSendDiagnosticReset(
+                    pFilter->otCtx,
+                    aAddress,
+                    aTlvTypes,
+                    aCount)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerAddJoiner(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(uint8_t) + sizeof(otExtAddress))
+    {
+        const ULONG aPSKdBufferLength = InBufferLength - sizeof(uint8_t) - sizeof(otExtAddress) - sizeof(uint32_t);
+
+        if (aPSKdBufferLength <= OPENTHREAD_PSK_MAX_LENGTH + 1)
+        {
+            uint8_t aExtAddressValid = *(uint8_t*)InBuffer;
+            const otExtAddress *aExtAddress = aExtAddressValid == 0 ? NULL : (otExtAddress*)(InBuffer + sizeof(uint8_t));
+            char *aPSKd = (char*)(InBuffer + sizeof(uint8_t) + sizeof(otExtAddress));
+            uint32_t aTimeout = *(uint32_t*)(InBuffer + sizeof(uint8_t) + sizeof(otExtAddress) + aPSKdBufferLength);
+
+            // Ensure aPSKd is NULL terminated in the buffer
+            if (strnlen(aPSKd, aPSKdBufferLength) < aPSKdBufferLength)
+            {
+                status = ThreadErrorToNtstatus(otCommissionerAddJoiner(
+                    pFilter->otCtx, aExtAddress, aPSKd, aTimeout));
+            }
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerRemoveJoiner(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength >= sizeof(uint8_t) + sizeof(otExtAddress))
+    {
+        uint8_t aExtAddressValid = *(uint8_t*)InBuffer;
+        const otExtAddress *aExtAddress = aExtAddressValid == 0 ? NULL : (otExtAddress*)(InBuffer + sizeof(uint8_t));
+        status = ThreadErrorToNtstatus(otCommissionerRemoveJoiner(
+            pFilter->otCtx, aExtAddress));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerProvisioningUrl(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(OutBuffer);
+    *OutBufferLength = 0;
+
+    if (InBufferLength <= OPENTHREAD_PROV_URL_MAX_LENGTH + 1)
+    {
+        char *aProvisioningUrl = InBufferLength > 1 ? (char*)InBuffer : NULL;
+
+        // Ensure aProvisioningUrl is empty or NULL terminated in the buffer
+        if (aProvisioningUrl == NULL ||
+            strnlen(aProvisioningUrl, InBufferLength) < InBufferLength)
+        {
+            status = ThreadErrorToNtstatus(otCommissionerSetProvisioningUrl(
+                pFilter->otCtx, aProvisioningUrl));
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otCommissionerAnnounceBegin(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(otIp6Address))
+    {
+        uint32_t aChannelMask = *(uint32_t*)InBuffer;
+        uint8_t aCount = *(uint8_t*)(InBuffer + sizeof(uint32_t));
+        uint16_t aPeriod = *(uint16_t*)(InBuffer + sizeof(uint32_t) + sizeof(uint8_t));
+        const otIp6Address *aAddress = (otIp6Address*)(InBuffer + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t));
+
+        if (InBufferLength >= sizeof(otIp6Address) + sizeof(uint8_t) + aCount)
+        {
+            status = ThreadErrorToNtstatus(
+                otCommissionerAnnounceBegin(
+                    pFilter->otCtx,
+                    aChannelMask,
+                    aCount,
+                    aPeriod,
+                    aAddress)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendActiveGet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        uint8_t aLength = *(uint8_t*)InBuffer;
+        PUCHAR aTlvTypes = aLength == 0 ? NULL : InBuffer + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(uint8_t) + aLength)
+        {
+            otIp6Address *aAddress = NULL;
+            if (InBufferLength >= sizeof(uint8_t) + aLength + sizeof(otIp6Address))
+                aAddress = (otIp6Address*)(InBuffer + sizeof(uint8_t) + aLength);
+
+            status = ThreadErrorToNtstatus(
+                otDatasetSendMgmtActiveGet(
+                    pFilter->otCtx,
+                    aTlvTypes,
+                    aLength,
+                    aAddress)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendActiveSet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(otOperationalDataset) + sizeof(uint8_t))
+    {
+        const otOperationalDataset *aDataset = (otOperationalDataset*)InBuffer;
+        uint8_t aLength = *(uint8_t*)(InBuffer + sizeof(otOperationalDataset));
+        PUCHAR aTlvTypes = aLength == 0 ? NULL : InBuffer + sizeof(otOperationalDataset) + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength)
+        {
+            status = ThreadErrorToNtstatus(
+                otDatasetSendMgmtActiveSet(
+                    pFilter->otCtx,
+                    aDataset,
+                    aTlvTypes,
+                    aLength)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendPendingGet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        uint8_t aLength = *(uint8_t*)InBuffer;
+        PUCHAR aTlvTypes = aLength == 0 ? NULL : InBuffer + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(uint8_t) + aLength)
+        {
+            otIp6Address *aAddress = NULL;
+            if (InBufferLength >= sizeof(uint8_t) + aLength + sizeof(otIp6Address))
+                aAddress = (otIp6Address*)(InBuffer + sizeof(uint8_t) + aLength);
+
+            status = ThreadErrorToNtstatus(
+                otDatasetSendMgmtPendingGet(
+                    pFilter->otCtx,
+                    aTlvTypes,
+                    aLength,
+                    aAddress)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendPendingSet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(otOperationalDataset) + sizeof(uint8_t))
+    {
+        const otOperationalDataset *aDataset = (otOperationalDataset*)InBuffer;
+        uint8_t aLength = *(uint8_t*)(InBuffer + sizeof(otOperationalDataset));
+        PUCHAR aTlvTypes = aLength == 0 ? NULL : InBuffer + sizeof(otOperationalDataset) + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(otOperationalDataset) + sizeof(uint8_t) + aLength)
+        {
+            status = ThreadErrorToNtstatus(
+                otDatasetSendMgmtPendingSet(
+                    pFilter->otCtx,
+                    aDataset,
+                    aTlvTypes,
+                    aLength)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendMgmtCommissionerGet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        uint8_t aLength = *(uint8_t*)InBuffer;
+        PUCHAR aTlvs = aLength == 0 ? NULL : InBuffer + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(uint8_t) + aLength)
+        {
+            status = ThreadErrorToNtstatus(
+                otCommissionerSendMgmtGet(
+                    pFilter->otCtx,
+                    aTlvs,
+                    aLength)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otSendMgmtCommissionerSet(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(otCommissioningDataset) + sizeof(uint8_t))
+    {
+        const otCommissioningDataset *aDataset = (otCommissioningDataset*)InBuffer;
+        uint8_t aLength = *(uint8_t*)(InBuffer + sizeof(otCommissioningDataset));
+        PUCHAR aTlvs = aLength == 0 ? NULL : InBuffer + sizeof(otCommissioningDataset) + sizeof(uint8_t);
+
+        if (InBufferLength >= sizeof(otCommissioningDataset) + sizeof(uint8_t) + aLength)
+        {
+            status = ThreadErrorToNtstatus(
+                otCommissionerSendMgmtSet(
+                    pFilter->otCtx,
+                    aDataset,
+                    aTlvs,
+                    aLength)
+                );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otKeySwitchGuardtime(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        otThreadSetKeySwitchGuardTime(pFilter->otCtx, *(uint32_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(uint32_t))
+    {
+        *(uint32_t*)OutBuffer = otThreadGetKeySwitchGuardTime(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(uint32_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otKeySwitchGuardtime(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               pIrp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                NULL,
+                SPINEL_CMD_PROP_VALUE_SET,
+                SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME,
+                sizeof(uint32_t),
+                SPINEL_DATATYPE_UINT32_S,
+                *(uint32_t*)InBuffer);
+    }
+    else if (OutBufferLength >= sizeof(uint32_t))
+    {
+        status =
+            otLwfTunSendCommandForIrp(
+                pFilter,
+                pIrp,
+                otLwfTunIoCtl_otKeySwitchGuardtime_Handler,
+                SPINEL_CMD_PROP_VALUE_GET,
+                SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME,
+                0,
+                NULL);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+otLwfTunIoCtl_otKeySwitchGuardtime_Handler(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    if (Key == SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME)
+    {
+        if (try_spinel_datatype_unpack(Data, DataLength, SPINEL_DATATYPE_UINT32_S, (uint32_t*)OutBuffer))
+        {
+            *OutBufferLength = sizeof(uint32_t);
+            status = STATUS_SUCCESS;
+        }
+    }
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otThreadAutoStart(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(BOOLEAN))
+    {
+        status =
+            ThreadErrorToNtstatus(
+                otThreadSetAutoStart(
+                    pFilter->otCtx,
+                    *(BOOLEAN*)InBuffer != FALSE)
+            );
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(BOOLEAN))
+    {
+        *(BOOLEAN*)OutBuffer = otThreadGetAutoStart(pFilter->otCtx) ? TRUE : FALSE;
+        *OutBufferLength = sizeof(BOOLEAN);
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otThreadPreferredRouterId(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    *OutBufferLength = 0;
+    UNREFERENCED_PARAMETER(OutBuffer);
+
+    if (InBufferLength >= sizeof(uint8_t))
+    {
+        status =
+            ThreadErrorToNtstatus(
+                otThreadSetPreferredRouterId(
+                    pFilter->otCtx,
+                    *(uint8_t*)InBuffer != FALSE)
+            );
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtl_otParentPriority(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    )
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+    if (InBufferLength >= sizeof(int8_t))
+    {
+        otThreadSetParentPriority(pFilter->otCtx, *(int8_t*)InBuffer);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = 0;
+    }
+    else if (*OutBufferLength >= sizeof(int8_t))
+    {
+        *(uint16_t*)OutBuffer = otThreadGetParentPriority(pFilter->otCtx);
+        status = STATUS_SUCCESS;
+        *OutBufferLength = sizeof(int8_t);
+    }
+    else
+    {
+        *OutBufferLength = 0;
+    }
+
+    return status;
+}
+
diff --git a/examples/drivers/windows/otLwf/iocontrol.h b/examples/drivers/windows/otLwf/iocontrol.h
new file mode 100644
index 0000000..67eb724
--- /dev/null
+++ b/examples/drivers/windows/otLwf/iocontrol.h
@@ -0,0 +1,232 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IOCONTROL_H
+#define _IOCONTROL_H
+
+//
+// Function prototype for general Io Control functions
+//
+
+typedef 
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+OTLWF_IOCTL_FUNC(
+    _In_reads_bytes_(InBufferLength)
+            PVOID           InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    );
+
+//
+// General Io Control Functions
+//
+
+// Handles queries for the current list of Thread interfaces
+OTLWF_IOCTL_FUNC otLwfIoCtlEnumerateInterfaces;
+
+// Handles queries for the details of a specific Thread interface
+OTLWF_IOCTL_FUNC otLwfIoCtlQueryInterface;
+
+// Handles IOTCLs for OpenThread control
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfIoCtlOpenThreadControl(
+    _In_ PIRP Irp
+    );
+
+// Handles Irp for IOTCLs for OpenThread control on the OpenThread thread
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfCompleteOpenThreadIrp(
+    _In_ PMS_FILTER     pFilter,
+    _In_ PIRP           Irp
+    );
+
+// Helper for converting IoCtl to string
+const char*
+IoCtlString(
+    ULONG IoControlCode
+    );
+
+//
+// Function prototype for OpenThread Io Control functions
+//
+
+typedef 
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+OTLWF_OT_IOCTL_FUNC(
+    _In_ PMS_FILTER         pFilter,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID           OutBuffer,
+    _Inout_ PULONG          OutBufferLength
+    );
+
+typedef 
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+OTLWF_TUN_IOCTL_FUNC(
+    _In_ PMS_FILTER         pFilter,
+    _In_ PIRP               Irp,
+    _In_reads_bytes_(InBufferLength)
+            PUCHAR          InBuffer,
+    _In_    ULONG           InBufferLength,
+    _In_    ULONG           OutBufferLength
+    );
+
+typedef
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+(SPINEL_IRP_CMD_HANDLER)(
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength,
+    _Out_writes_bytes_(*OutBufferLength)
+            PVOID OutBuffer,
+    _Inout_ PULONG OutBufferLength
+    );
+
+#define DECL_IOCTL_FUNC(X) \
+    OTLWF_OT_IOCTL_FUNC otLwfIoCtl_##X
+
+#define DECL_IOCTL_FUNC_WITH_TUN(X) \
+    OTLWF_OT_IOCTL_FUNC otLwfIoCtl_##X; \
+    OTLWF_TUN_IOCTL_FUNC otLwfTunIoCtl_##X
+
+#define DECL_IOCTL_FUNC_WITH_TUN2(X) \
+    OTLWF_OT_IOCTL_FUNC otLwfIoCtl_##X; \
+    OTLWF_TUN_IOCTL_FUNC otLwfTunIoCtl_##X; \
+    SPINEL_IRP_CMD_HANDLER otLwfTunIoCtl_##X##_Handler
+
+#define REF_IOCTL_FUNC(X) otLwfIoCtl_##X , NULL
+
+#define REF_IOCTL_FUNC_WITH_TUN(X) otLwfIoCtl_##X , otLwfTunIoCtl_##X
+
+DECL_IOCTL_FUNC_WITH_TUN2(otInterface);
+DECL_IOCTL_FUNC_WITH_TUN2(otThread);
+DECL_IOCTL_FUNC_WITH_TUN2(otActiveScan);
+DECL_IOCTL_FUNC(otDiscover);
+DECL_IOCTL_FUNC_WITH_TUN2(otChannel);
+DECL_IOCTL_FUNC_WITH_TUN2(otChildTimeout);
+DECL_IOCTL_FUNC_WITH_TUN2(otExtendedAddress);
+DECL_IOCTL_FUNC_WITH_TUN2(otExtendedPanId);
+DECL_IOCTL_FUNC_WITH_TUN2(otLeaderRloc);
+DECL_IOCTL_FUNC_WITH_TUN2(otLinkMode);
+DECL_IOCTL_FUNC_WITH_TUN2(otMasterKey);
+DECL_IOCTL_FUNC_WITH_TUN2(otMeshLocalEid);
+DECL_IOCTL_FUNC_WITH_TUN2(otMeshLocalPrefix);
+//DECL_IOCTL_FUNC(otNetworkDataLeader);
+//DECL_IOCTL_FUNC(otNetworkDataLocal);
+DECL_IOCTL_FUNC_WITH_TUN2(otNetworkName);
+DECL_IOCTL_FUNC_WITH_TUN2(otPanId);
+DECL_IOCTL_FUNC_WITH_TUN2(otRouterRollEnabled);
+DECL_IOCTL_FUNC_WITH_TUN2(otShortAddress);
+DECL_IOCTL_FUNC(otActiveDataset);
+DECL_IOCTL_FUNC(otPendingDataset);
+DECL_IOCTL_FUNC_WITH_TUN2(otLocalLeaderWeight);
+DECL_IOCTL_FUNC_WITH_TUN(otAddBorderRouter);
+DECL_IOCTL_FUNC_WITH_TUN(otRemoveBorderRouter);
+DECL_IOCTL_FUNC_WITH_TUN(otAddExternalRoute);
+DECL_IOCTL_FUNC_WITH_TUN(otRemoveExternalRoute);
+DECL_IOCTL_FUNC(otSendServerData);
+DECL_IOCTL_FUNC_WITH_TUN2(otContextIdReuseDelay);
+DECL_IOCTL_FUNC_WITH_TUN2(otKeySequenceCounter);
+DECL_IOCTL_FUNC_WITH_TUN2(otNetworkIdTimeout);
+DECL_IOCTL_FUNC_WITH_TUN2(otRouterUpgradeThreshold);
+DECL_IOCTL_FUNC_WITH_TUN(otReleaseRouterId);
+DECL_IOCTL_FUNC_WITH_TUN2(otMacWhitelistEnabled);
+DECL_IOCTL_FUNC_WITH_TUN(otAddMacWhitelist);
+DECL_IOCTL_FUNC_WITH_TUN(otRemoveMacWhitelist);
+DECL_IOCTL_FUNC(otMacWhitelistEntry);
+DECL_IOCTL_FUNC_WITH_TUN(otClearMacWhitelist);
+DECL_IOCTL_FUNC_WITH_TUN2(otDeviceRole);
+DECL_IOCTL_FUNC(otChildInfoById);
+DECL_IOCTL_FUNC(otChildInfoByIndex);
+DECL_IOCTL_FUNC(otEidCacheEntry);
+DECL_IOCTL_FUNC(otLeaderData);
+DECL_IOCTL_FUNC_WITH_TUN2(otLeaderRouterId);
+DECL_IOCTL_FUNC_WITH_TUN2(otLeaderWeight);
+DECL_IOCTL_FUNC_WITH_TUN2(otNetworkDataVersion);
+DECL_IOCTL_FUNC_WITH_TUN2(otPartitionId);
+DECL_IOCTL_FUNC_WITH_TUN2(otRloc16);
+DECL_IOCTL_FUNC(otRouterIdSequence);
+DECL_IOCTL_FUNC(otRouterInfo);
+DECL_IOCTL_FUNC_WITH_TUN2(otStableNetworkDataVersion);
+DECL_IOCTL_FUNC(otMacBlacklistEnabled);
+DECL_IOCTL_FUNC(otAddMacBlacklist);
+DECL_IOCTL_FUNC(otRemoveMacBlacklist);
+DECL_IOCTL_FUNC(otMacBlacklistEntry);
+DECL_IOCTL_FUNC(otClearMacBlacklist);
+DECL_IOCTL_FUNC(otMaxTransmitPower);
+DECL_IOCTL_FUNC(otNextOnMeshPrefix);
+DECL_IOCTL_FUNC(otPollPeriod);
+DECL_IOCTL_FUNC(otLocalLeaderPartitionId);
+DECL_IOCTL_FUNC(otAssignLinkQuality);
+DECL_IOCTL_FUNC_WITH_TUN(otPlatformReset);
+DECL_IOCTL_FUNC_WITH_TUN2(otParentInfo);
+DECL_IOCTL_FUNC(otSingleton);
+DECL_IOCTL_FUNC(otMacCounters);
+DECL_IOCTL_FUNC_WITH_TUN2(otMaxChildren);
+DECL_IOCTL_FUNC(otCommissionerStart);
+DECL_IOCTL_FUNC(otCommissionerStop);
+DECL_IOCTL_FUNC(otJoinerStart);
+DECL_IOCTL_FUNC(otJoinerStop);
+DECL_IOCTL_FUNC(otFactoryAssignedIeeeEui64);
+DECL_IOCTL_FUNC(otHashMacAddress);
+DECL_IOCTL_FUNC_WITH_TUN2(otRouterDowngradeThreshold);
+DECL_IOCTL_FUNC(otCommissionerPanIdQuery);
+DECL_IOCTL_FUNC(otCommissionerEnergyScan);
+DECL_IOCTL_FUNC_WITH_TUN2(otRouterSelectionJitter);
+DECL_IOCTL_FUNC(otJoinerUdpPort);
+DECL_IOCTL_FUNC(otSendDiagnosticGet);
+DECL_IOCTL_FUNC(otSendDiagnosticReset);
+DECL_IOCTL_FUNC(otCommissionerAddJoiner);
+DECL_IOCTL_FUNC(otCommissionerRemoveJoiner);
+DECL_IOCTL_FUNC(otCommissionerProvisioningUrl);
+DECL_IOCTL_FUNC(otCommissionerAnnounceBegin);
+DECL_IOCTL_FUNC_WITH_TUN2(otEnergyScan);
+DECL_IOCTL_FUNC(otSendActiveGet);
+DECL_IOCTL_FUNC(otSendActiveSet);
+DECL_IOCTL_FUNC(otSendPendingGet);
+DECL_IOCTL_FUNC(otSendPendingSet);
+DECL_IOCTL_FUNC(otSendMgmtCommissionerGet);
+DECL_IOCTL_FUNC(otSendMgmtCommissionerSet);
+DECL_IOCTL_FUNC_WITH_TUN2(otKeySwitchGuardtime);
+DECL_IOCTL_FUNC(otFactoryReset);
+DECL_IOCTL_FUNC(otThreadAutoStart);
+DECL_IOCTL_FUNC(otThreadPreferredRouterId);
+DECL_IOCTL_FUNC_WITH_TUN2(otPSKc);
+DECL_IOCTL_FUNC(otParentPriority);
+
+#endif // _IOCONTROL_H
diff --git a/examples/drivers/windows/otLwf/nsihelper.h b/examples/drivers/windows/otLwf/nsihelper.h
new file mode 100644
index 0000000..c383199
--- /dev/null
+++ b/examples/drivers/windows/otLwf/nsihelper.h
@@ -0,0 +1,326 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the various types and function required to use NSI to
+ *  query an interface's compartment ID.
+ */
+
+#ifndef _NSI_HELPER_H
+#define _NSI_HELPER_H
+
+#define NSISTATUS NTSTATUS
+
+typedef enum _NSI_STORE {
+    NsiPersistent,
+    // Persists as long as module exists.
+    NsiActive,
+    NsiBoth,
+    NsiCurrent,
+    NsiBootFirmwareTable
+} NSI_STORE;
+
+typedef enum _NSI_SET_ACTION {  
+    NsiSetDefault,  
+    NsiSetCreateOnly,  
+    NsiSetCreateOrSet,  
+    NsiSetDelete,  
+    NsiSetReset,  
+    NsiSetClear,  
+    NsiSetCreateOrSetWithReference,  
+    NsiSetDeleteWithReference,  
+} NSI_SET_ACTION;  
+
+typedef enum _NSI_STRUCT_TYPE {
+    NsiStructRw,
+    NsiStructRoDynamic,
+    NsiStructRoStatic,
+    NsiMaximumStructType
+} NSI_STRUCT_TYPE;
+
+typedef struct _NL_INTERFACE_KEY {  
+    IF_LUID Luid;  
+} NL_INTERFACE_KEY, *PNL_INTERFACE_KEY;  
+
+typedef enum _NL_TYPE_OF_INTERFACE {  
+    InterfaceAllowAll = 0,  
+    InterfaceDisallowUnicast,  
+    InterfaceDisallowMulticast,  
+    InterfaceDisallowAll,  
+    InterfaceUnchanged = -1  
+} NL_TYPE_OF_INTERFACE;  
+  
+typedef enum _NL_DOMAIN_NETWORK_LOCATION {  
+    DomainNetworkLocationRemote = 0,   // connect to a domain network remotely via DA i.e. outside corp network.  
+    DomainNetworkCategoryLink = 1,     // connect to a domain network directly i.e. inside corp network.  
+    DomainNetworkUnchanged = -1  
+} NL_DOMAIN_NETWORK_LOCATION;  
+  
+typedef enum _NL_DOMAIN_TYPE {  
+    DomainTypeNonDomainNetwork = 0,    // connected to non-domain network.  
+    DomainTypeDomainNetwork = 1,       // connected to a network that has active directory.  
+    DomainTypeDomainAuthenticated = 2, // connected to AD network and machine is authenticated against it.  
+    DomainTypeUnchanged = -1  
+} NL_DOMAIN_TYPE;  
+  
+typedef enum _NL_INTERFACE_ECN_CAPABILITY {  
+    NlInterfaceEcnUnchanged = -1,  
+    NlInterfaceEcnDisabled = 0,  
+    NlInterfaceEcnUseEct1 = 1,  
+    NlInterfaceEcnUseEct0 = 2,  
+    NlInterfaceEcnAppDecide = 3  
+} NL_INTERFACE_ECN_CAPABILITY, *PNL_INTERFACE_ECN_CAPABILITY;  
+  
+typedef enum _NL_INTERNET_CONNECTIVITY_STATUS {  
+    NlNoInternetConnectivity,  
+    NlNoInternetDnsResolutionSucceeded,  
+    NlInternetConnectivityDetected,  
+    NlInternetConnectivityUnknown = -1  
+} NL_INTERNET_CONNECTIVITY_STATUS, *PNL_INTERNET_CONNECTIVITY_STATUS;  
+
+typedef union _IP_ADDRESS_STORAGE {  
+    IN_ADDR Ipv4;  
+    IN6_ADDR Ipv6;  
+    UCHAR Buffer[sizeof(IN6_ADDR)];  
+} IP_ADDRESS_STORAGE, *PIP_ADDRESS_STORAGE;  
+
+typedef struct _NL_INTERFACE_RW {  
+    BOOLEAN AdvertisingEnabled;  
+    BOOLEAN ForwardingEnabled;  
+    BOOLEAN MulticastForwardingEnabled;  
+    BOOLEAN WeakHostSend;  
+    BOOLEAN WeakHostReceive;  
+    BOOLEAN UseNeighborUnreachabilityDetection;  
+    BOOLEAN UseAutomaticMetric;
+    BOOLEAN UseZeroBroadcastAddress;  
+    BOOLEAN UseBroadcastForRouterDiscovery;  
+    BOOLEAN DhcpRouterDiscoveryEnabled;  
+    BOOLEAN ManagedAddressConfigurationSupported;  
+    BOOLEAN OtherStatefulConfigurationSupported;  
+    BOOLEAN AdvertiseDefaultRoute;  
+    NL_NETWORK_CATEGORY NetworkCategory;  
+    NL_ROUTER_DISCOVERY_BEHAVIOR RouterDiscoveryBehavior;  
+    NL_TYPE_OF_INTERFACE TypeOfInterface;  
+    ULONG Metric;  
+    ULONG BaseReachableTime;    // Base for random ReachableTime (in ms).  
+    ULONG RetransmitTime;       // Neighbor Solicitation timeout (in ms).  
+    ULONG PathMtuDiscoveryTimeout; // Path MTU discovery timeout (in ms).  
+    ULONG DadTransmits;         // DupAddrDetectTransmits in RFC 2462.  
+    NL_LINK_LOCAL_ADDRESS_BEHAVIOR LinkLocalAddressBehavior;  
+    ULONG LinkLocalAddressTimeout; // In ms.  
+    ULONG ZoneIndices[ScopeLevelCount]; // Zone part of a SCOPE_ID.  
+    ULONG NlMtu;  
+    ULONG SitePrefixLength;  
+    ULONG MulticastForwardingHopLimit;  
+    ULONG CurrentHopLimit; 
+    IP_ADDRESS_STORAGE LinkLocalAddress;  
+    BOOLEAN DisableDefaultRoutes;  
+    ULONG AdvertisedRouterLifetime;  
+    BOOLEAN SendUnsolicitedNeighborAdvertisementOnDad;  
+    BOOLEAN LimitedLinkConnectivity;  
+    BOOLEAN ForceARPNDPattern;  
+    BOOLEAN EnableDirectMACPattern;  
+    BOOLEAN EnableWol;  
+    BOOLEAN ForceTunneling;  
+    NL_DOMAIN_NETWORK_LOCATION DomainNetworkLocation;  
+    ULONGLONG RandomizedEpoch;  
+    NL_INTERFACE_ECN_CAPABILITY EcnCapability;  
+    NL_DOMAIN_TYPE DomainType;  
+    GUID NetworkSignature;  
+    NL_INTERNET_CONNECTIVITY_STATUS InternetConnectivityDetected;  
+    BOOLEAN ProxyDetected;  
+    ULONG DadRetransmitTime;  
+    BOOLEAN PrefixSharing;  
+    BOOLEAN DisableUnconstrainedRouteLookup;  
+    ULONG NetworkContext;  
+    BOOLEAN ResetAutoconfigurationOnOperStatusDown;   
+    BOOLEAN ClampMssEnabled;  
+  
+} NL_INTERFACE_RW, *PNL_INTERFACE_RW;  
+
+__inline  
+VOID  
+NlInitializeInterfaceRw(  
+    IN OUT PNL_INTERFACE_RW Rw  
+    )  
+{  
+    //  
+    // Initialize all fields to values that indicate "no change".  
+    //  
+    memset(Rw, 0xff, sizeof(*Rw));  
+    Rw->BaseReachableTime = 0;  
+    Rw->RetransmitTime = 0;  
+    Rw->PathMtuDiscoveryTimeout = 0;  
+    Rw->NlMtu = 0;  
+    Rw->DadRetransmitTime = 0;  
+}  
+
+typedef enum {  
+    NlBestRouteObject,  
+    NlCompartmentForwardingObject,  
+    NlCompartmentObject,  
+    NlControlProtocolObject,  
+    NlEchoRequestObject,  
+    NlEchoSequenceRequestObject,  
+    NlGlobalObject,  
+    NlInterfaceObject,  
+    NlLocalAnycastAddressObject,  
+    NlLocalMulticastAddressObject,  
+    NlLocalUnicastAddressObject,  
+    NlNeighborObject,  
+    NlPathObject,  
+    NlPotentialRouterObject,  
+    NlPrefixPolicyObject,  
+    NlProxyNeighborObject,  
+    NlRouteObject,  
+    NlSitePrefixObject,  
+    NlSubInterfaceObject,  
+    NlWakeUpPatternObject,  
+    NlResolveNeighborObject,  
+    NlSortAddressesObject,  
+    NlMfeObject,  
+    NlMfeNotifyObject,  
+    NlInterfaceHopObject,  
+    NlInterfaceUnprivilegedObject,  
+    NlTunnelPhysicalInterfaceObject,  
+    NlLocalityObject,  
+    NlLocalityDataObject,  
+    NlLocalityPrivateObject,  
+    NlLocalBottleneckObject,  
+    NlTimerObject,  
+    NlDisconnectInterface,  
+    NlMaximumObject  
+} NL_OBJECT_TYPE, *PNL_OBJECT_TYPE;  
+
+NSISTATUS
+NsiGetParameter(
+    __in NSI_STORE Store,
+    __in PNPI_MODULEID ModuleId,
+    __in ULONG ObjectIndex,
+    __in_bcount_opt(KeyStructLength) PVOID KeyStruct,
+    __in ULONG KeyStructLength,
+    __in NSI_STRUCT_TYPE StructType,
+    __out_bcount(ParameterLen) PVOID Parameter,
+    __in ULONG ParameterLen,
+    __in ULONG ParameterOffset
+    );
+
+NSISTATUS  
+NsiSetAllParameters(  
+    __in NSI_STORE      Store,  
+    __in NSI_SET_ACTION Action,  
+    __in PNPI_MODULEID  ModuleId,  
+    __in ULONG          ObjectIndex,  
+    __in_bcount_opt(KeyStructLength) PVOID KeyStruct,  
+    __in ULONG          KeyStructLength,  
+    __in_bcount_opt(RwParameterStructLength) PVOID RwParameterStruct,  
+    __in ULONG          RwParameterStructLength  
+    );  
+
+extern CONST NPI_MODULEID NPI_MS_NDIS_MODULEID;
+
+typedef enum _NDIS_NSI_OBJECT_INDEX
+{
+    NdisNsiObjectInterfaceInformation,
+    NdisNsiObjectInterfaceEnum,
+    NdisNsiObjectInterfaceLookUp,
+    NdisNsiObjectIfRcvAddress,
+    NdisNsiObjectStackIfEntry,
+    NdisNsiObjectInvertedIfStackEntry,
+    NdisNsiObjectNetwork,
+    NdisNsiObjectCompartment,
+    NdisNsiObjectThread,
+    NdisNsiObjectSession,
+    NdisNsiObjectInterfacePersist,
+    NdisNsiObjectCompartmentLookup,
+    NdisNsiObjectInterfaceInformationRaw,
+    NdisNsiObjectInterfaceEnumRaw,
+    NdisNsiObjectStackIfEnum,
+    NdisNsiObjectInterfaceIsolationInfo,
+    NdisNsiObjectJob,
+    NdisNsiObjectMaximum
+} NDIS_NSI_OBJECT_INDEX, *PNDIS_NSI_OBJECT_INDEX;
+
+typedef struct _NDIS_NSI_INTERFACE_INFORMATION_RW
+{
+    // rw fields
+    GUID                        NetworkGuid;
+    NET_IF_ADMIN_STATUS         ifAdminStatus;
+    NDIS_IF_COUNTED_STRING      ifAlias;
+    NDIS_IF_PHYSICAL_ADDRESS    ifPhysAddress;
+    NDIS_IF_COUNTED_STRING      ifL2NetworkInfo;
+}NDIS_NSI_INTERFACE_INFORMATION_RW, *PNDIS_NSI_INTERFACE_INFORMATION_RW;
+
+#define NDIS_SIZEOF_NSI_INTERFACE_INFORMATION_RW_REVISION_1      \
+        RTL_SIZEOF_THROUGH_FIELD(NDIS_NSI_INTERFACE_INFORMATION_RW, ifPhysAddress)
+
+typedef NDIS_INTERFACE_INFORMATION NDIS_NSI_INTERFACE_INFORMATION_ROD, *PNDIS_NSI_INTERFACE_INFORMATION_ROD;
+
+//
+// Copied from ndiscomp.h
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+EXPORT
+COMPARTMENT_ID
+NdisGetThreadObjectCompartmentId(
+    _In_ PETHREAD ThreadObject
+    );
+
+_IRQL_requires_(PASSIVE_LEVEL)
+EXPORT
+NTSTATUS
+NdisSetThreadObjectCompartmentId(
+    _In_ PETHREAD ThreadObject,
+    _In_ NET_IF_COMPARTMENT_ID CompartmentId
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+__inline
+COMPARTMENT_ID
+NdisGetCurrentThreadCompartmentId(
+    VOID
+    )
+{
+    return NdisGetThreadObjectCompartmentId(PsGetCurrentThread());
+}
+
+_IRQL_requires_(PASSIVE_LEVEL)
+__inline
+NTSTATUS
+NdisSetCurrentThreadCompartmentId(
+    _In_ COMPARTMENT_ID CompartmentId
+    )
+{
+    return
+        NdisSetThreadObjectCompartmentId(PsGetCurrentThread(), CompartmentId);
+}
+
+#endif // _NSI_HELPER_H
diff --git a/examples/drivers/windows/otLwf/otLwf.inf b/examples/drivers/windows/otLwf/otLwf.inf
new file mode 100644
index 0000000..fa90a24
--- /dev/null
+++ b/examples/drivers/windows/otLwf/otLwf.inf
@@ -0,0 +1,106 @@
+;
+;  Copyright (c) 2016, The OpenThread Authors.
+;  All rights reserved.
+;
+;  Redistribution and use in source and binary forms, with or without
+;  modification, are permitted provided that the following conditions are met:
+;  1. Redistributions of source code must retain the above copyright
+;     notice, this list of conditions and the following disclaimer.
+;  2. Redistributions in binary form must reproduce the above copyright
+;     notice, this list of conditions and the following disclaimer in the
+;     documentation and/or other materials provided with the distribution.
+;  3. Neither the name of the copyright holder nor the
+;     names of its contributors may be used to endorse or promote products
+;     derived from this software without specific prior written permission.
+;
+;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+;  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+;  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+;  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+;  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+;  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+;  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+;  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;  POSSIBILITY OF SUCH DAMAGE.
+;
+
+[Version]
+Signature           = "$Windows NT$"
+Class               = NetService
+ClassGUID           = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider            = %OpenThread%
+DriverVer           = 
+PnpLockDown         = 1
+CatalogFile         = otLwf.cat
+
+[Manufacturer]
+%OpenThread%        = OpenThread,NT$ARCH$
+
+[OpenThread.NT$ARCH$]
+%otlwf.DeviceDesc%  = otlwf.ndi, otLwf
+
+;-------------------------------------------------------------------------------
+; OpenThread NDIS Filter Driver
+;-------------------------------------------------------------------------------
+[otlwf.ndi]
+Characteristics     = 0x40000
+NetCfgInstanceId    = "{B3A3845A-164E-4727-B12E-32B8DCE1F6CD}"
+AddReg              = otlwf.Reg
+Copyfiles           = otLwf.CopyFiles
+
+[otlwf.ndi.Services]
+AddService          = otLwf, , otlwf.Service
+
+[otlwf.ndi.Remove.Services]
+; The SPSVCINST_STOPSERVICE flag instructs SCM to stop the NT service
+; before uninstalling the driver.
+DelService          =   otLwf,  0x200 ; SPSVCINST_STOPSERVICE
+
+;-------------------------------------------------------------------------------
+; OpenThread NDIS Filter Common
+;-------------------------------------------------------------------------------
+[otlwf.Reg]
+HKR, Ndi,               Service,            ,           "otLwf"
+HKR, Ndi,               CoServices,         0x00010000, "otLwf"
+HKR, Ndi,               FilterClass,        ,           ms_medium_converter_top
+HKR, Ndi,               FilterType,         0x00010001, 0x00000002
+HKR, Ndi,               FilterRunType,      0x00010001, 0x00000002   ;OPTIONAL filter 
+HKR, Ndi\Interfaces,    UpperRange,         ,           "noupper"
+HKR, Ndi\Interfaces,    LowerRange,         ,           "nolower"
+HKR, Ndi\Interfaces,    FilterMediaTypes,   ,           "802.15.4"
+
+;-------------------------------------------------------------------------------
+; Driver and Service Section
+;-------------------------------------------------------------------------------
+[otLwf.CopyFiles]
+otLwf.sys,,,2
+
+[otlwf.Service]
+DisplayName         = %otlwf.Service.DispName%
+ServiceType         = 1 ;SERVICE_KERNEL_DRIVER
+StartType           = 1 ;SERVICE_SYSTEM_START
+ErrorControl        = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary       = %12%\otLwf.sys
+LoadOrderGroup      = NDIS
+Description         = %otlwf.DeviceDesc%
+
+[SourceDisksNames]
+1 = %otlwf.DeviceDesc%,"",,
+
+[SourceDisksFiles]
+; TODO: Include any related files that should be installed with your driver.
+otLwf.sys = 1
+
+[DestinationDirs]
+DefaultDestDir = 12
+otLwf.CopyFiles = 12
+
+;-------------------------------------------------------------------------------
+; Localizable Strings
+;-------------------------------------------------------------------------------
+[Strings]
+OpenThread              = "OpenThread"
+otlwf.DeviceDesc        = "OpenThread NDIS LightWeight Filter"
+otlwf.Service.DispName  = "OpenThread NDIS LightWeight Filter"
diff --git a/examples/drivers/windows/otLwf/precomp.c b/examples/drivers/windows/otLwf/precomp.c
new file mode 100644
index 0000000..ceeb0d6
--- /dev/null
+++ b/examples/drivers/windows/otLwf/precomp.c
@@ -0,0 +1 @@
+#include "precomp.h"
diff --git a/examples/drivers/windows/otLwf/precomp.h b/examples/drivers/windows/otLwf/precomp.h
new file mode 100644
index 0000000..31ef930
--- /dev/null
+++ b/examples/drivers/windows/otLwf/precomp.h
@@ -0,0 +1,126 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  Precompiled header for otLwf project.
+ */
+
+#pragma warning(disable:4201)  // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4204)  // nonstandard extension used : non-constant aggregate initializer
+#pragma warning(disable:28175) // The 'MajorFunction' member of _DRIVER_OBJECT should not be accessed by a driver:
+                               // Access to this member may be permitted for certain classes of drivers.
+#pragma warning(disable:28301) // No annotations for first declaration of *
+
+#include <ntifs.h>
+#include <ndis.h>
+#include <wdmsec.h>
+#include <rtlrefcount.h>
+#include <netiodef.h>
+#include <nsihelper.h>
+#include <netioapi.h>
+#include <bcrypt.h>
+
+VOID  
+RtlCopyBufferToMdl(  
+    _In_reads_bytes_(BytesToCopy) CONST VOID *Buffer,  
+    _Inout_ PMDL MdlChain,  
+    _In_ SIZE_T MdlOffset,  
+    _In_ SIZE_T BytesToCopy,  
+    _Out_ SIZE_T* BytesCopied  
+    );
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <openthread-windows-config.h>
+#include <openthread-core-config.h>
+#include <openthread/openthread.h>
+#include <openthread/border_router.h>
+#include <openthread/dataset_ftd.h>
+#include <openthread/thread_ftd.h>
+#include <openthread/icmp6.h>
+#include <openthread/ip6.h>
+#include <openthread/tasklet.h>
+#include <openthread/commissioner.h>
+#include <openthread/joiner.h>
+#include <openthread/dhcp6_server.h>
+#include <openthread/dhcp6_client.h>
+#include <common/code_utils.hpp>
+#include <openthread/platform/logging.h>
+#include <openthread/platform/logging-windows.h>
+#include <openthread/platform/radio.h>
+#include <openthread/platform/misc.h>
+#include <openthread/platform/alarm.h>
+#include <openthread/platform/settings.h>
+#include <openthread/platform/messagepool.h>
+#include <ncp/spinel.h>
+
+#include <otLwfIoctl.h>
+
+#ifdef _KERNEL_MODE
+#define CODE_SEG(segment) __declspec(code_seg(segment))
+#else
+#define CODE_SEG(segment) 
+#endif
+
+#define PAGED CODE_SEG("PAGE") _IRQL_always_function_max_(PASSIVE_LEVEL)
+#define PAGEDX CODE_SEG("PAGE")
+#define INITCODE CODE_SEG("INIT")
+
+typedef struct _MS_FILTER MS_FILTER, *PMS_FILTER;
+
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct UDPHeader
+{
+    USHORT SourcePort;
+    USHORT DestinationPort;
+    USHORT TotalLength;
+    USHORT Checksum;
+
+} UDPHeader;
+
+#pragma pack(pop)
+
+//#define DEBUG_TIMING
+//#define DEBUG_ALLOC
+#define LOG_BUFFERS
+//#define FORCE_SYNCHRONOUS_RECEIVE
+#define COMMAND_INIT_RETRY
+
+#include "driver.h"
+#include "device.h"
+#include "iocontrol.h"
+#include "radio.h"
+#include "filter.h"
+#include "command.h"
+#include "thread.h"
+#include "tunnel.h"
diff --git a/examples/drivers/windows/otLwf/radio.c b/examples/drivers/windows/otLwf/radio.c
new file mode 100644
index 0000000..78c2659
--- /dev/null
+++ b/examples/drivers/windows/otLwf/radio.c
@@ -0,0 +1,930 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the logging function required for the OpenThread library.
+ */
+
+#include "precomp.h"
+#include "radio.tmh"
+
+void 
+LogMac(
+    _In_ PCSTR szDir,
+    _In_ PMS_FILTER pFilter,
+    ULONG frameLength,
+    _In_reads_bytes_(frameLength) PUCHAR frame
+    );
+
+const char MacSend[] = "MAC_SEND";
+const char MacRecv[] = "MAC_RECV";
+
+#define LogMacSend(pFilter, frameLength, frame) LogMac(MacSend, pFilter, frameLength, frame)
+#define LogMacRecv(pFilter, frameLength, frame) LogMac(MacRecv, pFilter, frameLength, frame)
+
+void 
+otPlatReset(
+    _In_ otInstance *otCtx
+    )
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! resetting...", &pFilter->InterfaceGuid);
+
+    // Indicate to the miniport
+    (void)otLwfCmdResetDevice(pFilter, TRUE);
+
+    // Finalize previous OpenThread instance
+    otLwfReleaseInstance(pFilter);
+
+    // Reset radio layer
+    pFilter->otRadioState = OT_RADIO_STATE_DISABLED;
+    pFilter->otCurrentListenChannel = 0xFF;
+    pFilter->otPromiscuous = false;
+    pFilter->otPendingMacOffloadEnabled = FALSE;
+
+    // Reinitialize the OpenThread library
+    pFilter->otCachedRole = OT_DEVICE_ROLE_DISABLED;
+    pFilter->otCtx = otInstanceInit(pFilter->otInstanceBuffer + sizeof(PMS_FILTER), &pFilter->otInstanceSize);
+    ASSERT(pFilter->otCtx);
+
+    // Make sure our helper function returns the right pointer for the filter, given the openthread instance
+    NT_ASSERT(otCtxToFilter(pFilter->otCtx) == pFilter);
+
+    // Disable Icmp (ping) handling
+    otIcmp6SetEchoEnabled(pFilter->otCtx, FALSE);
+
+    // Register callbacks with OpenThread
+    otSetStateChangedCallback(pFilter->otCtx, otLwfStateChangedCallback, pFilter);
+    otIp6SetReceiveCallback(pFilter->otCtx, otLwfReceiveIp6DatagramCallback, pFilter);
+
+    // Query the current addresses from TCPIP and cache them
+    (void)otLwfInitializeAddresses(pFilter);
+
+    // Initialze media connect state to disconnected
+    otLwfIndicateLinkState(pFilter, MediaConnectStateDisconnected);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+otPlatResetReason 
+otPlatGetResetReason(
+    _In_ otInstance *otCtx
+    )
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return pFilter->cmdResetReason;
+}
+
+VOID 
+otLwfRadioGetFactoryAddress(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    NTSTATUS status;
+    PVOID SpinelBuffer = NULL;
+    uint8_t *hwAddress = NULL;
+
+    RtlZeroMemory(&pFilter->otFactoryAddress, sizeof(pFilter->otFactoryAddress));
+
+    // Query the MP for the address
+    status =
+        otLwfCmdGetProp(
+            pFilter,
+            &SpinelBuffer,
+            SPINEL_PROP_HWADDR,
+            SPINEL_DATATYPE_EUI64_S,
+            &hwAddress
+        );
+    if (!NT_SUCCESS(status) || hwAddress == NULL)
+    {
+        LogError(DRIVER_DEFAULT, "Get SPINEL_PROP_HWADDR failed, %!STATUS!", status);
+        return;
+    }
+
+    NT_ASSERT(SpinelBuffer);
+    memcpy(&pFilter->otFactoryAddress, hwAddress, sizeof(pFilter->otFactoryAddress));
+    FILTER_FREE_MEM(SpinelBuffer);
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! cached factory Extended Mac Address: %llX", &pFilter->InterfaceGuid, pFilter->otFactoryAddress);
+}
+
+VOID 
+otLwfRadioInit(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    NT_ASSERT(pFilter->DeviceStatus == OTLWF_DEVICE_STATUS_RADIO_MODE);
+
+    // Initialize the OpenThread radio capability flags
+    pFilter->otRadioCapabilities = 0;
+    if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO_ACK_TIMEOUT) != 0)
+        pFilter->otRadioCapabilities |= OT_RADIO_CAPS_ACK_TIMEOUT;
+    if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO_MAC_RETRY_AND_COLLISION_AVOIDANCE) != 0)
+        pFilter->otRadioCapabilities |= OT_RADIO_CAPS_TRANSMIT_RETRIES;
+    if ((pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO_ENERGY_SCAN) != 0)
+        pFilter->otRadioCapabilities |= OT_RADIO_CAPS_ENERGY_SCAN;
+
+    pFilter->otRadioState = OT_RADIO_STATE_DISABLED;
+    pFilter->otCurrentListenChannel = 0xFF;
+    pFilter->otPromiscuous = false;
+
+    pFilter->otReceiveFrame.mPsdu = pFilter->otReceiveMessage;
+    pFilter->otTransmitFrame.mPsdu = pFilter->otTransmitMessage;
+    
+    pFilter->otPendingMacOffloadEnabled = FALSE;
+
+    // Cache the factory address
+    otLwfRadioGetFactoryAddress(pFilter);
+    
+    LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_DISABLED.", pFilter);
+    
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otPlatRadioGetIeeeEui64(otInstance *otCtx, uint8_t *aIeeeEui64)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    memcpy(aIeeeEui64, &pFilter->otFactoryAddress, sizeof(ULONGLONG));
+}
+
+void otPlatRadioSetPanId(_In_ otInstance *otCtx, uint16_t panid)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! set PanID: %X", &pFilter->InterfaceGuid, panid);
+
+    pFilter->otPanID = panid;
+
+    if (pFilter->otRadioState != OT_RADIO_STATE_DISABLED &&
+        pFilter->otPanID != 0xFFFF)
+    {
+        // Indicate to the miniport
+        status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_MAC_15_4_PANID,
+                SPINEL_DATATYPE_UINT16_S,
+                panid
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_15_4_PANID failed, %!STATUS!", status);
+        }
+    }
+}
+
+void otPlatRadioSetExtendedAddress(_In_ otInstance *otCtx, uint8_t *address)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+    spinel_eui64_t extAddr;
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! set Extended Mac Address: %llX", &pFilter->InterfaceGuid, *(ULONGLONG*)address);
+
+    pFilter->otExtendedAddress = *(ULONGLONG*)address;
+
+    for (size_t i = 0; i < OT_EXT_ADDRESS_SIZE; i++)
+    {
+        extAddr.bytes[i] = address[7 - i];
+    }
+
+    // Indicate to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_15_4_LADDR,
+            SPINEL_DATATYPE_EUI64_S,
+            &extAddr
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_15_4_LADDR failed, %!STATUS!", status);
+    }
+}
+
+void otPlatRadioSetShortAddress(_In_ otInstance *otCtx, uint16_t address)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! set Short Mac Address: %X", &pFilter->InterfaceGuid, address);
+
+    pFilter->otShortAddress = address;
+
+    if (pFilter->otRadioState != OT_RADIO_STATE_DISABLED)
+    {
+        // Indicate to the miniport
+        status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_MAC_15_4_SADDR,
+                SPINEL_DATATYPE_UINT16_S,
+                address
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_15_4_SADDR failed, %!STATUS!", status);
+        }
+    }
+}
+
+void otPlatRadioSetPromiscuous(_In_ otInstance *otCtx, bool aEnable)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    pFilter->otPromiscuous = (BOOLEAN)aEnable;
+
+    // Indicate to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_PROMISCUOUS_MODE,
+            SPINEL_DATATYPE_UINT8_S,
+            aEnable != 0 ? SPINEL_MAC_PROMISCUOUS_MODE_NETWORK : SPINEL_MAC_PROMISCUOUS_MODE_OFF
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_PROMISCUOUS_MODE failed, %!STATUS!", status);
+    }
+}
+
+bool otPlatRadioIsEnabled(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return pFilter->otRadioState != OT_RADIO_STATE_DISABLED;
+}
+
+otError otPlatRadioEnable(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    NT_ASSERT(pFilter->otRadioState <= OT_RADIO_STATE_SLEEP);
+    if (pFilter->otRadioState > OT_RADIO_STATE_SLEEP) return OT_ERROR_BUSY;
+
+    pFilter->otRadioState = OT_RADIO_STATE_SLEEP;
+
+    // Indicate to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_PHY_ENABLED,
+            SPINEL_DATATYPE_BOOL_S,
+            TRUE
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_PHY_ENABLED (true) failed, %!STATUS!", status);
+    }
+
+    LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_SLEEP.", pFilter);
+
+    if (pFilter->otPanID != 0xFFFF)
+    {
+        // Indicate PANID to the miniport
+        status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_MAC_15_4_PANID,
+                SPINEL_DATATYPE_UINT16_S,
+                pFilter->otPanID
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_15_4_PANID failed, %!STATUS!", status);
+        }
+    }
+
+    // Indicate Short address to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_15_4_SADDR,
+            SPINEL_DATATYPE_UINT16_S,
+            pFilter->otShortAddress
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_15_4_SADDR failed, %!STATUS!", status);
+    }
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatRadioDisable(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    // First make sure we are in the Sleep state if we weren't already
+    if (pFilter->otRadioState > OT_RADIO_STATE_SLEEP)
+    {
+        (void)otPlatRadioSleep(otCtx);
+    }
+
+    pFilter->otRadioState = OT_RADIO_STATE_DISABLED;
+
+    // Indicate to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_PHY_ENABLED,
+            SPINEL_DATATYPE_BOOL_S,
+            FALSE
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_PHY_ENABLED (false) failed, %!STATUS!", status);
+    }
+
+    LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_DISABLED.", pFilter);
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatRadioSleep(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // If we were in the transmit state, cancel the transmit
+    if (pFilter->otRadioState == OT_RADIO_STATE_TRANSMIT)
+    {
+        pFilter->otLastTransmitError = OT_ERROR_ABORT;
+        otLwfRadioTransmitFrameDone(pFilter);
+    }
+
+    if (pFilter->otRadioState != OT_RADIO_STATE_SLEEP)
+    {
+        pFilter->otRadioState = OT_RADIO_STATE_SLEEP;
+        LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_SLEEP.", pFilter);
+
+        // Indicate to the miniport
+        NTSTATUS status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_MAC_RAW_STREAM_ENABLED,
+                SPINEL_DATATYPE_BOOL_S,
+                FALSE
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_RAW_STREAM_ENABLED (false) failed, %!STATUS!", status);
+        }
+    }
+
+    return OT_ERROR_NONE;
+}
+
+otError otPlatRadioReceive(_In_ otInstance *otCtx, uint8_t aChannel)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    
+    NT_ASSERT(pFilter->otRadioState != OT_RADIO_STATE_DISABLED);
+    if (pFilter->otRadioState == OT_RADIO_STATE_DISABLED) return OT_ERROR_BUSY;
+    
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p", pFilter);
+
+    // Update current channel if different
+    if (pFilter->otCurrentListenChannel != aChannel)
+    {
+        NTSTATUS status;
+
+        NT_ASSERT(aChannel >= 11 && aChannel <= 26);
+
+        LogInfo(DRIVER_DEFAULT, "Filter %p new Listen Channel = %u.", pFilter, aChannel);
+        pFilter->otCurrentListenChannel = aChannel;
+
+        // Indicate to the miniport
+        status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_PHY_CHAN,
+                SPINEL_DATATYPE_UINT8_S,
+                aChannel
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_PHY_CHAN failed, %!STATUS!", status);
+        }
+    }
+
+    // Only transition to the receive state if we were sleeping; otherwise we
+    // are already in receive or transmit state.
+    if (pFilter->otRadioState == OT_RADIO_STATE_SLEEP)
+    {
+        pFilter->otRadioState = OT_RADIO_STATE_RECEIVE;
+        LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_RECEIVE.", pFilter);
+
+        NTSTATUS status =
+            otLwfCmdSetProp(
+                pFilter,
+                SPINEL_PROP_MAC_RAW_STREAM_ENABLED,
+                SPINEL_DATATYPE_BOOL_S,
+                TRUE
+            );
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_RAW_STREAM_ENABLED (true) failed, %!STATUS!", status);
+        }
+
+        // Set the event to indicate we can process NBLs
+        KeSetEvent(&pFilter->EventWorkerThreadProcessNBLs, 0, FALSE);
+    }
+    
+    LogFuncExit(DRIVER_DATA_PATH);
+
+    return OT_ERROR_NONE;
+}
+
+otRadioFrame *otPlatRadioGetTransmitBuffer(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return &pFilter->otTransmitFrame;
+}
+
+int8_t otPlatRadioGetRssi(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    UNREFERENCED_PARAMETER(pFilter);
+    return 0;
+}
+
+otRadioCaps otPlatRadioGetCaps(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    return otCtxToFilter(otCtx)->otRadioCapabilities;
+}
+
+bool otPlatRadioGetPromiscuous(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return pFilter->otPromiscuous;
+}
+
+VOID 
+otLwfRadioReceiveFrame(
+    _In_ PMS_FILTER pFilter,
+    _In_ otError errorCode
+    )
+{    
+    NT_ASSERT(pFilter->otReceiveFrame.mChannel >= 11 && pFilter->otReceiveFrame.mChannel <= 26);
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p", pFilter);
+
+    LogMacRecv(pFilter, pFilter->otReceiveFrame.mLength, pFilter->otReceiveFrame.mPsdu);
+
+    if (pFilter->otRadioState > OT_RADIO_STATE_DISABLED)
+    {
+        otPlatRadioReceiveDone(pFilter->otCtx, &pFilter->otReceiveFrame, errorCode);
+    }
+    else
+    {
+        LogVerbose(DRIVER_DATA_PATH, "Mac frame dropped.");
+    }
+    
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+otError otPlatRadioTransmit(_In_ otInstance *otCtx, _In_ otRadioFrame *aFrame)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    otError error = OT_ERROR_BUSY;
+
+    UNREFERENCED_PARAMETER(aFrame);
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p", pFilter);
+
+    NT_ASSERT(pFilter->otRadioState == OT_RADIO_STATE_RECEIVE);
+    if (pFilter->otRadioState == OT_RADIO_STATE_RECEIVE)
+    {
+        error = OT_ERROR_NONE;
+        pFilter->otRadioState = OT_RADIO_STATE_TRANSMIT;
+    
+        LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_TRANSMIT.", pFilter);
+    }
+
+    LogFuncExitMsg(DRIVER_DATA_PATH, "%u", error);
+
+    return error;
+}
+
+VOID otLwfRadioTransmitFrame(_In_ PMS_FILTER pFilter)
+{
+    NT_ASSERT(pFilter->otRadioState == OT_RADIO_STATE_TRANSMIT);
+
+    LogMacSend(pFilter, pFilter->otTransmitFrame.mLength, pFilter->otTransmitFrame.mPsdu);
+
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p", pFilter);
+
+    otLwfCmdSendMacFrameAsync(pFilter, &pFilter->otTransmitFrame);
+
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+VOID 
+otLwfRadioTransmitFrameDone(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntryMsg(DRIVER_DATA_PATH, "Filter: %p", pFilter);
+
+    if (pFilter->otRadioState == OT_RADIO_STATE_TRANSMIT)
+    {
+        pFilter->SendPending = FALSE;
+
+        // Now that we are completing a send, fall back to receive state and set the event
+        pFilter->otRadioState = OT_RADIO_STATE_RECEIVE;
+        LogInfo(DRIVER_DEFAULT, "Filter %p RadioState = OT_RADIO_STATE_RECEIVE.", pFilter);
+        KeSetEvent(&pFilter->EventWorkerThreadProcessNBLs, 0, FALSE);
+
+        if (pFilter->otLastTransmitError != OT_ERROR_NONE &&
+            pFilter->otLastTransmitError != OT_ERROR_CHANNEL_ACCESS_FAILURE &&
+            pFilter->otLastTransmitError != OT_ERROR_NO_ACK)
+        {
+            pFilter->otLastTransmitError = OT_ERROR_ABORT;
+        }
+
+        if (((pFilter->otTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) ||
+            pFilter->otLastTransmitError != OT_ERROR_NONE)
+        {
+            otPlatRadioTxDone(pFilter->otCtx, &pFilter->otTransmitFrame, NULL, pFilter->otLastTransmitError);
+        }
+        else
+        {
+            otPlatRadioTxDone(pFilter->otCtx, &pFilter->otTransmitFrame, &pFilter->otReceiveFrame, pFilter->otLastTransmitError);
+        }
+    }
+
+    LogFuncExit(DRIVER_DATA_PATH);
+}
+
+void otPlatRadioEnableSrcMatch(_In_ otInstance *otCtx, bool aEnable)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Ignore if we are already in the correct state
+    if (aEnable == pFilter->otPendingMacOffloadEnabled) return;
+
+    // Cache the new value
+    pFilter->otPendingMacOffloadEnabled = aEnable ? TRUE : FALSE;
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_ENABLED,
+            SPINEL_DATATYPE_BOOL_S,
+            (aEnable ? TRUE : FALSE)
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SRC_MATCH_ENABLED failed, %!STATUS!", status);
+    }
+}
+
+otError otPlatRadioAddSrcMatchShortEntry(_In_ otInstance *otCtx, const uint16_t aShortAddress)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdInsertProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES,
+            SPINEL_DATATYPE_UINT16_S,
+            aShortAddress
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Insert SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES failed, %!STATUS!", status);
+    }
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatRadioAddSrcMatchExtEntry(_In_ otInstance *otCtx, const uint8_t *aExtAddress)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdInsertProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES,
+            SPINEL_DATATYPE_EUI64_S,
+            aExtAddress
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Insert SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES failed, %!STATUS!", status);
+    }
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatRadioClearSrcMatchShortEntry(_In_ otInstance *otCtx, const uint16_t aShortAddress)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdRemoveProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES,
+            SPINEL_DATATYPE_UINT16_S,
+            aShortAddress
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Remove SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES failed, %!STATUS!", status);
+    }
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatRadioClearSrcMatchExtEntry(_In_ otInstance *otCtx, const uint8_t *aExtAddress)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdRemoveProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES,
+            SPINEL_DATATYPE_EUI64_S,
+            aExtAddress
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Remove SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES failed, %!STATUS!", status);
+    }
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+void otPlatRadioClearSrcMatchShortEntries(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES,
+            NULL
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES failed, %!STATUS!", status);
+    }
+}
+
+void otPlatRadioClearSrcMatchExtEntries(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Indicate to the miniport
+    NTSTATUS status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES,
+            NULL
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES failed, %!STATUS!", status);
+    }
+}
+
+otError otPlatRadioEnergyScan(_In_ otInstance *otCtx, uint8_t aScanChannel, uint16_t aScanDuration)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    NTSTATUS status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SCAN_MASK,
+            SPINEL_DATATYPE_UINT8_S,
+            aScanChannel
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SCAN_MASK failed, %!STATUS!", status);
+        goto error;
+    }
+
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SCAN_PERIOD,
+            SPINEL_DATATYPE_UINT16_S,
+            aScanDuration
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SCAN_PERIOD failed, %!STATUS!", status);
+        goto error;
+    }
+
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_MAC_SCAN_STATE,
+            SPINEL_DATATYPE_UINT8_S,
+            SPINEL_SCAN_STATE_ENERGY
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_MAC_SCAN_STATE failed, %!STATUS!", status);
+        goto error;
+    }
+
+error:
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+void otPlatRadioSetDefaultTxPower(_In_ otInstance *otCtx, int8_t aPower)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+
+    // Indicate to the miniport
+    status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_PHY_TX_POWER,
+            SPINEL_DATATYPE_INT8_S,
+            aPower
+        );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Set SPINEL_PROP_PHY_TX_POWER failed, %!STATUS!", status);
+    }
+}
+
+int8_t otPlatRadioGetReceiveSensitivity(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    NTSTATUS status;
+    int8_t receiveSensitivity;
+
+    status =
+        otLwfCmdGetProp(
+            pFilter,
+            NULL,
+            SPINEL_PROP_PHY_RX_SENSITIVITY,
+            SPINEL_DATATYPE_INT8_S,
+            &receiveSensitivity
+        );
+
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "Get SPINEL_PROP_PHY_RX_SENSITIVITY, failed, %!STATUS!", status);
+        return -100;  // return default value -100dBm
+    }
+
+    return receiveSensitivity;
+}
+
+inline USHORT getDstShortAddress(const UCHAR *frame)
+{
+    return (((USHORT)frame[IEEE802154_DSTADDR_OFFSET + 1]) << 8) | frame[IEEE802154_DSTADDR_OFFSET];
+}
+
+inline USHORT getSrcShortAddress(ULONG frameLength, _In_reads_bytes_(frameLength) PUCHAR frame, ULONG offset)
+{
+    return (offset + 1 < frameLength) ? ((((USHORT)frame[offset + 1]) << 8) | frame[offset]) : 0;
+}
+
+inline ULONGLONG getDstExtAddress(const UCHAR *frame)
+{
+    return *(ULONGLONG*)(frame + IEEE802154_DSTADDR_OFFSET);
+}
+
+inline ULONGLONG getSrcExtAddress(ULONG frameLength, _In_reads_bytes_(frameLength) PUCHAR frame, ULONG offset)
+{
+    return (offset + 7 < frameLength) ? (*(ULONGLONG*)(frame + offset)) : 0;
+}
+
+void 
+LogMac(
+    _In_ PCSTR szDir,
+    _In_ PMS_FILTER pFilter,
+    ULONG frameLength,
+    _In_reads_bytes_(frameLength) PUCHAR frame
+    )
+{
+    if (frameLength < 6) return;
+
+    NT_ASSERT(frame);
+
+    UCHAR AckRequested = (frame[0] & IEEE802154_ACK_REQUEST) != 0 ? 1 : 0;
+    UCHAR FramePending = (frame[0] & IEEE802154_FRAME_PENDING) != 0 ? 1 : 0;
+
+    switch (frame[1] & (IEEE802154_DST_ADDR_MASK | IEEE802154_SRC_ADDR_MASK))
+    {
+    case IEEE802154_DST_ADDR_NONE | IEEE802154_SRC_ADDR_NONE:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: null => null (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_NONE | IEEE802154_SRC_ADDR_SHORT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %X => null (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcShortAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET), frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_NONE | IEEE802154_SRC_ADDR_EXT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %llX => null (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcExtAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET), frameLength, AckRequested, FramePending);
+        break;
+        
+    case IEEE802154_DST_ADDR_SHORT | IEEE802154_SRC_ADDR_NONE:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: null => %X (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getDstShortAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_SHORT | IEEE802154_SRC_ADDR_SHORT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %X => %X (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcShortAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET+2), getDstShortAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_SHORT | IEEE802154_SRC_ADDR_EXT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %llX => %X (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcExtAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET+2), getDstShortAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+        
+    case IEEE802154_DST_ADDR_EXT | IEEE802154_SRC_ADDR_NONE:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: null => %llX (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getDstExtAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_EXT | IEEE802154_SRC_ADDR_SHORT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %X => %llX (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcShortAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET+8), getDstExtAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+    case IEEE802154_DST_ADDR_EXT | IEEE802154_SRC_ADDR_EXT:
+        LogVerbose(DRIVER_DATA_PATH, "Filter: %p, %s: %llX => %llX (%u bytes, AckReq=%u, FramePending=%u)", 
+            pFilter, szDir, getSrcExtAddress(frameLength, frame, IEEE802154_DSTADDR_OFFSET+8), getDstExtAddress(frame), frameLength, AckRequested, FramePending);
+        break;
+    }
+    
+#ifdef LOG_BUFFERS
+    otLogBuffer(frame, frameLength);
+#endif
+}
diff --git a/examples/drivers/windows/otLwf/radio.h b/examples/drivers/windows/otLwf/radio.h
new file mode 100644
index 0000000..310d4d7
--- /dev/null
+++ b/examples/drivers/windows/otLwf/radio.h
@@ -0,0 +1,92 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the structures and functions for the OpenThread radio interface.
+ */
+
+#ifndef _OTLWF_RADIO_H
+#define _OTLWF_RADIO_H
+
+enum
+{
+    IEEE802154_MIN_LENGTH         = 5,
+    IEEE802154_MAX_LENGTH         = 127,
+    IEEE802154_ACK_LENGTH         = 5,
+
+    IEEE802154_BROADCAST          = 0xffff,
+
+    IEEE802154_FRAME_TYPE_ACK     = 2 << 0,
+    IEEE802154_FRAME_TYPE_MACCMD  = 3 << 0,
+    IEEE802154_FRAME_TYPE_MASK    = 7 << 0,
+
+    IEEE802154_SECURITY_ENABLED   = 1 << 3,
+    IEEE802154_FRAME_PENDING      = 1 << 4,
+    IEEE802154_ACK_REQUEST        = 1 << 5,
+    IEEE802154_PANID_COMPRESSION  = 1 << 6,
+
+    IEEE802154_DST_ADDR_NONE      = 0 << 2,
+    IEEE802154_DST_ADDR_SHORT     = 2 << 2,
+    IEEE802154_DST_ADDR_EXT       = 3 << 2,
+    IEEE802154_DST_ADDR_MASK      = 3 << 2,
+
+    IEEE802154_SRC_ADDR_NONE      = 0 << 6,
+    IEEE802154_SRC_ADDR_SHORT     = 2 << 6,
+    IEEE802154_SRC_ADDR_EXT       = 3 << 6,
+    IEEE802154_SRC_ADDR_MASK      = 3 << 6,
+
+    IEEE802154_DSN_OFFSET         = 2,
+    IEEE802154_DSTPAN_OFFSET      = 3,
+    IEEE802154_DSTADDR_OFFSET     = 5,
+
+    IEEE802154_SEC_LEVEL_MASK     = 7 << 0,
+
+    IEEE802154_KEY_ID_MODE_0      = 0 << 3,
+    IEEE802154_KEY_ID_MODE_1      = 1 << 3,
+    IEEE802154_KEY_ID_MODE_2      = 2 << 3,
+    IEEE802154_KEY_ID_MODE_3      = 3 << 3,
+    IEEE802154_KEY_ID_MODE_MASK   = 3 << 3,
+
+    IEEE802154_MACCMD_DATA_REQ    = 4,
+};
+
+// Initializes the radio layer
+VOID otLwfRadioInit(_In_ PMS_FILTER pFilter);
+
+// Indicates a received frame from the radio layer
+VOID otLwfRadioReceiveFrame(_In_ PMS_FILTER pFilter, _In_ otError errorCode);
+
+// Indicates the transmit frame is ready to send to the radio layer
+VOID otLwfRadioTransmitFrame(_In_ PMS_FILTER pFilter);
+
+// Indicates the transmit frame finished sending
+VOID otLwfRadioTransmitFrameDone(_In_ PMS_FILTER pFilter);
+
+#endif  //_OTLWF_RADIO_H
diff --git a/examples/drivers/windows/otLwf/settings.c b/examples/drivers/windows/otLwf/settings.c
new file mode 100644
index 0000000..41aefaa
--- /dev/null
+++ b/examples/drivers/windows/otLwf/settings.c
@@ -0,0 +1,609 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the settings functions required for the OpenThread library.
+ */
+
+#include "precomp.h"
+#include "settings.tmh"
+
+void otPlatSettingsInit(otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    DECLARE_CONST_UNICODE_STRING(SubKeyName, L"OpenThread");
+
+    OBJECT_ATTRIBUTES attributes;
+    ULONG disposition;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    InitializeObjectAttributes(
+        &attributes,
+        (PUNICODE_STRING)&SubKeyName,
+        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+        pFilter->InterfaceRegKey,
+        NULL);
+
+    // Create/Open the 'OpenThread' sub key
+    NTSTATUS status =
+        ZwCreateKey(
+            &pFilter->otSettingsRegKey,
+            KEY_ALL_ACCESS,
+            &attributes,
+            0,
+            NULL,
+            REG_OPTION_NON_VOLATILE,
+            &disposition);
+
+    NT_ASSERT(NT_SUCCESS(status));
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "ZwCreateKey for 'OpenThread' key failed, %!STATUS!", status);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+uint16_t FilterCountSettings(_In_ PMS_FILTER pFilter, uint16_t aKey)
+{
+    HANDLE regKey = NULL;
+    OBJECT_ATTRIBUTES attributes;
+    DECLARE_UNICODE_STRING_SIZE(Name, 8);
+    UCHAR InfoBuffer[128] = {0};
+    PKEY_FULL_INFORMATION pInfo = (PKEY_FULL_INFORMATION)InfoBuffer;
+    ULONG InfoLength = sizeof(InfoBuffer);
+
+    // Convert 'aKey' to a string
+    RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name);
+
+    InitializeObjectAttributes(
+        &attributes,
+        &Name,
+        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+        pFilter->otSettingsRegKey,
+        NULL);
+
+    // Open the registry key
+    NTSTATUS status =
+        ZwOpenKey(
+            &regKey,
+            KEY_ALL_ACCESS,
+            &attributes);
+
+    if (!NT_SUCCESS(status))
+    {
+        // Key doesn't exist, return a count of 0
+        goto error;
+    }
+
+    // Query the key info from the registry
+    status =
+        ZwQueryKey(
+            regKey,
+            KeyValueFullInformation,
+            pInfo,
+            InfoLength,
+            &InfoLength);
+
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "ZwQueryKey for %S value failed, %!STATUS!", Name.Buffer, status);
+        goto error;
+    }
+
+error:
+
+    if (regKey) ZwClose(regKey);
+
+    return (uint16_t)pInfo->Values;
+}
+
+NTSTATUS FilterReadSetting(_In_ PMS_FILTER pFilter, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
+{
+    HANDLE regKey = NULL;
+    OBJECT_ATTRIBUTES attributes;
+    DECLARE_UNICODE_STRING_SIZE(Name, 20);
+    PKEY_VALUE_PARTIAL_INFORMATION pInfo = NULL;
+    ULONG InfoLength = sizeof(*pInfo) + *aValueLength;
+
+    // Convert 'aKey' to a string
+    RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name);
+
+    InitializeObjectAttributes(
+        &attributes,
+        &Name,
+        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+        pFilter->otSettingsRegKey,
+        NULL);
+
+    // Open the registry key
+    NTSTATUS status =
+        ZwOpenKey(
+            &regKey,
+            KEY_ALL_ACCESS,
+            &attributes);
+
+    if (!NT_SUCCESS(status))
+    {
+        // Key doesn't exist
+        goto error;
+    }
+
+    // Convert 'aIndex' to a string
+    RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name);
+
+    // Allocate buffer for query
+    pInfo = FILTER_ALLOC_MEM(pFilter->FilterHandle, InfoLength);
+    if (pInfo == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto error;
+    }
+
+    // Query the data
+    status = ZwQueryValueKey(
+        regKey,
+        &Name,
+        KeyValuePartialInformation,
+        pInfo,
+        InfoLength,
+        &InfoLength);
+
+    if (!NT_SUCCESS(status))
+    {
+        LogVerbose(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+        goto error;
+    }
+
+    NT_ASSERT(*aValueLength >= pInfo->DataLength);
+    *aValueLength = (uint16_t)pInfo->DataLength;
+    if (aValue)
+    {
+        memcpy(aValue, pInfo->Data, pInfo->DataLength);
+    }
+
+error:
+
+    if (pInfo) FILTER_FREE_MEM(pInfo);
+    if (regKey) ZwClose(regKey);
+
+    return status;
+}
+
+NTSTATUS FilterWriteSetting(_In_ PMS_FILTER pFilter, uint16_t aKey, int aIndex, const uint8_t *aValue, uint16_t aValueLength)
+{
+    HANDLE regKey = NULL;
+    OBJECT_ATTRIBUTES attributes;
+    DECLARE_UNICODE_STRING_SIZE(Name, 20);
+
+    // Convert 'aKey' to a string
+    RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name);
+
+    InitializeObjectAttributes(
+        &attributes,
+        &Name,
+        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+        pFilter->otSettingsRegKey,
+        NULL);
+
+    // Create/Open the registry key
+    NTSTATUS status =
+        ZwCreateKey(
+            &regKey,
+            KEY_ALL_ACCESS,
+            &attributes,
+            0,
+            NULL,
+            REG_OPTION_NON_VOLATILE,
+            NULL);
+
+    NT_ASSERT(NT_SUCCESS(status));
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "ZwCreateKey for %S key failed, %!STATUS!", Name.Buffer, status);
+        goto error;
+    }
+
+    // Convert 'aIndex' to a string
+    RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name);
+
+    // Write the data to the registry
+    status =
+        ZwSetValueKey(
+            regKey,
+            &Name,
+            0,
+            REG_BINARY,
+            (PVOID)aValue,
+            aValueLength);
+
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "ZwSetValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+        goto error;
+    }
+
+error:
+
+    if (regKey) ZwClose(regKey);
+
+    return status;
+}
+
+NTSTATUS FilterDeleteSetting(_In_ PMS_FILTER pFilter, uint16_t aKey, int aIndex)
+{
+    HANDLE regKey = NULL;
+    OBJECT_ATTRIBUTES attributes;
+    DECLARE_UNICODE_STRING_SIZE(Name, 20);
+
+    // Convert 'aKey' to a string
+    RtlIntegerToUnicodeString((ULONG)aKey, 16, &Name);
+
+    InitializeObjectAttributes(
+        &attributes,
+        &Name,
+        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+        pFilter->otSettingsRegKey,
+        NULL);
+
+    // Open the registry key
+    NTSTATUS status =
+        ZwOpenKey(
+            &regKey,
+            KEY_ALL_ACCESS,
+            &attributes);
+
+    if (!NT_SUCCESS(status))
+    {
+        // Key doesn't exist
+        goto error;
+    }
+
+    // If 'aIndex' is -1 then delete the whole key, otherwise delete the individual value
+    if (aIndex == -1)
+    {
+        // Delete the registry key
+        status = ZwDeleteKey(regKey);
+    }
+    else
+    {
+        UCHAR KeyInfoBuffer[128] = { 0 };
+        PKEY_FULL_INFORMATION pKeyInfo = (PKEY_FULL_INFORMATION)KeyInfoBuffer;
+        ULONG KeyInfoLength = sizeof(KeyInfoBuffer);
+
+        // When deleting an individual value, since order doesn't matter, we will actually
+        // copy the last value over the one being deleted and then delete the last value; so
+        // we maintain a contiguous list of numbered values
+
+        // Query the number of values
+        // Note: Can't use helper function because we already have the key open
+        status =
+            ZwQueryKey(
+                regKey,
+                KeyValueFullInformation,
+                pKeyInfo,
+                KeyInfoLength,
+                &KeyInfoLength);
+
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "ZwQueryKey for %S value failed, %!STATUS!", Name.Buffer, status);
+            goto error;
+        }
+
+        if ((ULONG)aIndex >= pKeyInfo->Values)
+        {
+            // Attempt to delete beyond the end of the list
+            status = STATUS_OBJECT_NAME_NOT_FOUND;
+            goto error;
+        }
+        else if (pKeyInfo->Values == 1)
+        {
+            // Deleting the only value on the key, go ahead and delete the entire key
+            status = ZwDeleteKey(regKey);
+        }
+        else if (pKeyInfo->Values - 1 != (ULONG)aIndex)
+        {
+            // We aren't deleting the last value so we need to copy the last value
+            // over this one, and then delete the last one.
+
+            PKEY_VALUE_PARTIAL_INFORMATION pValueInfo = NULL;
+            ULONG ValueInfoLength = 0;
+
+            // Convert pKeyInfo->Values-1 to a string
+            RtlIntegerToUnicodeString(pKeyInfo->Values - 1, 16, &Name);
+
+            // Query the key data buffer size
+            status = ZwQueryValueKey(
+                regKey,
+                &Name,
+                KeyValuePartialInformation,
+                pValueInfo,
+                0,
+                &ValueInfoLength);
+
+            NT_ASSERT(status != STATUS_SUCCESS);
+            if (status != STATUS_BUFFER_TOO_SMALL)
+            {
+                LogVerbose(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+                goto error;
+            }
+
+            pValueInfo = FILTER_ALLOC_MEM(pFilter->FilterHandle, ValueInfoLength);
+            if (pValueInfo == NULL)
+            {
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                goto error;
+            }
+
+            // Query the data buffer
+            status = ZwQueryValueKey(
+                regKey,
+                &Name,
+                KeyValuePartialInformation,
+                pValueInfo,
+                ValueInfoLength,
+                &ValueInfoLength);
+
+            if (!NT_SUCCESS(status))
+            {
+                LogError(DRIVER_DEFAULT, "ZwQueryValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+                goto cleanup;
+            }
+
+            // Delete the registry value
+            status =
+                ZwDeleteValueKey(
+                    regKey,
+                    &Name);
+
+            if (!NT_SUCCESS(status))
+            {
+                LogError(DRIVER_DEFAULT, "ZwDeleteValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+                goto cleanup;
+            }
+
+            // Convert 'aIndex' to a string
+            RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name);
+
+            // Write the data to the registry key we are deleting
+            status =
+                ZwSetValueKey(
+                    regKey,
+                    &Name,
+                    0,
+                    REG_BINARY,
+                    (PVOID)pValueInfo->Data,
+                    pValueInfo->DataLength);
+
+            if (!NT_SUCCESS(status))
+            {
+                LogError(DRIVER_DEFAULT, "ZwSetValueKey for %S value failed, %!STATUS!", Name.Buffer, status);
+                goto cleanup;
+            }
+
+        cleanup:
+
+            if (pValueInfo) FILTER_FREE_MEM(pValueInfo);
+        }
+        else
+        {
+            // Deleting the last value in the list (but not the only value)
+            // Just delete the value directly. No need to copy any others.
+
+            // Convert 'aIndex' to a string
+            RtlIntegerToUnicodeString((ULONG)aIndex, 16, &Name);
+
+            // Delete the registry value
+            status =
+                ZwDeleteValueKey(
+                    regKey,
+                    &Name);
+        }
+    }
+
+error:
+
+    if (regKey) ZwClose(regKey);
+
+    return status;
+}
+
+otError otPlatSettingsBeginChange(otInstance *otCtx)
+{
+    UNREFERENCED_PARAMETER(otCtx);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatSettingsCommitChange(otInstance *otCtx)
+{
+    UNREFERENCED_PARAMETER(otCtx);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatSettingsAbandonChange(otInstance *otCtx)
+{
+    UNREFERENCED_PARAMETER(otCtx);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatSettingsGet(otInstance *otCtx, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    NTSTATUS status = 
+        FilterReadSetting(
+            pFilter,
+            aKey,
+            aIndex,
+            aValue,
+            aValueLength);
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
+}
+
+otError otPlatSettingsSet(otInstance *otCtx, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    NTSTATUS status = 
+        FilterWriteSetting(
+            pFilter,
+            aKey,
+            0,
+            aValue,
+            aValueLength);
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatSettingsAdd(otInstance *otCtx, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    uint16_t count = FilterCountSettings(pFilter, aKey);
+
+    NTSTATUS status =
+        FilterWriteSetting(
+            pFilter,
+            aKey,
+            count,
+            aValue,
+            aValueLength);
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+otError otPlatSettingsDelete(otInstance *otCtx, uint16_t aKey, int aIndex)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    NTSTATUS status =
+        FilterDeleteSetting(
+            pFilter,
+            aKey,
+            aIndex);
+
+    return NT_SUCCESS(status) ? OT_ERROR_NONE : OT_ERROR_FAILED;
+}
+
+void otPlatSettingsWipe(otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    // Delete all subkeys of 'OpenThread'
+    if (pFilter->otSettingsRegKey)
+    {
+        NTSTATUS status = STATUS_SUCCESS;
+        ULONG index = 0;
+        UCHAR keyInfo[sizeof(KEY_BASIC_INFORMATION) + 64];
+
+        while (status == STATUS_SUCCESS)
+        {
+            ULONG size = sizeof(keyInfo);
+            status =
+                ZwEnumerateKey(
+                    pFilter->otSettingsRegKey,
+                    index,
+                    KeyBasicInformation,
+                    keyInfo,
+                    size,
+                    &size);
+
+            bool deleted = false;
+            if (NT_SUCCESS(status))
+            {
+                HANDLE subKey = NULL;
+                OBJECT_ATTRIBUTES attributes;
+                PKEY_BASIC_INFORMATION pKeyInfo = (PKEY_BASIC_INFORMATION)keyInfo;
+
+                UNICODE_STRING subKeyName = 
+                {
+                    (USHORT)pKeyInfo->NameLength,
+                    (USHORT)pKeyInfo->NameLength,
+                    pKeyInfo->Name
+                };
+
+                InitializeObjectAttributes(
+                    &attributes,
+                    &subKeyName,
+                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                    pFilter->otSettingsRegKey,
+                    NULL);
+
+                // Open the sub key
+                status =
+                    ZwOpenKey(
+                        &subKey,
+                        KEY_ALL_ACCESS,
+                        &attributes);
+
+                if (NT_SUCCESS(status))
+                {
+                    // Delete the key
+                    status = ZwDeleteKey(subKey);
+                    if (!NT_SUCCESS(status))
+                    {
+                        LogError(DRIVER_DEFAULT, "ZwDeleteKey for subkey failed, %!STATUS!", status);
+                    }
+                    else
+                    {
+                        deleted = true;
+                    }
+
+                    // Close handle
+                    ZwClose(subKey);
+                }
+                else
+                {
+                    LogError(DRIVER_DEFAULT, "ZwOpenKey for subkey failed, %!STATUS!", status);
+                }
+            }
+
+            // Only increment index if we didn't delete
+            if (!deleted)
+            {
+                index++;
+            }
+        }
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/otLwf/thread.c b/examples/drivers/windows/otLwf/thread.c
new file mode 100644
index 0000000..2139f45
--- /dev/null
+++ b/examples/drivers/windows/otLwf/thread.c
@@ -0,0 +1,822 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the Thread mode (Radio Miniport) functions required for the OpenThread library.
+ */
+
+#include "precomp.h"
+#include "thread.tmh"
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfInitializeThreadMode(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    NT_ASSERT(pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_RADIO);
+
+    do
+    {
+        KeInitializeEvent(
+            &pFilter->SendNetBufferListComplete,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+
+        // Initialize the event processing
+        pFilter->EventWorkerThread = NULL;
+        NdisAllocateSpinLock(&pFilter->EventsLock);
+        InitializeListHead(&pFilter->AddressChangesHead);
+        InitializeListHead(&pFilter->NBLsHead);
+        InitializeListHead(&pFilter->MacFramesHead);
+        InitializeListHead(&pFilter->EventIrpListHead);
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadStopEvent,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadWaitTimeUpdated,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadProcessTasklets,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadProcessAddressChanges,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadProcessNBLs,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadProcessMacFrames,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadProcessIrp,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        KeInitializeEvent(
+            &pFilter->EventWorkerThreadEnergyScanComplete,
+            SynchronizationEvent, // auto-clearing event
+            FALSE                 // event initially non-signalled
+            );
+        pFilter->EventHighPrecisionTimer = 
+            ExAllocateTimer(
+                otLwfEventProcessingTimer, 
+                pFilter, 
+                EX_TIMER_HIGH_RESOLUTION
+                );
+        if (pFilter->EventHighPrecisionTimer == NULL)
+        {
+            LogError(DRIVER_DEFAULT, "Failed to allocate timer!");
+            break;
+        }
+
+        // Query the interface state (best effort, since it might not be supported)
+        BOOLEAN IfUp = FALSE;
+        Status = otLwfCmdGetProp(pFilter, NULL, SPINEL_PROP_NET_IF_UP, SPINEL_DATATYPE_BOOL_S, &IfUp);
+        if (!NT_SUCCESS(Status))
+        {
+            LogVerbose(DRIVER_DEFAULT, "Failed to query SPINEL_PROP_INTERFACE_TYPE, %!STATUS!", Status);
+            Status = NDIS_STATUS_SUCCESS;
+        }
+        else
+        {
+            NT_ASSERT(IfUp == FALSE);
+        }
+
+        // Initialize the event processing thread
+        if (!NT_SUCCESS(otLwfEventProcessingStart(pFilter)))
+        {
+            Status = NDIS_STATUS_RESOURCES;
+            break;
+        }
+
+    } while (FALSE);
+
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        // Stop event processing thread
+        otLwfEventProcessingStop(pFilter);
+
+        // Stop and free the timer
+        if (pFilter->EventHighPrecisionTimer)
+        {
+            ExDeleteTimer(pFilter->EventHighPrecisionTimer, TRUE, FALSE, NULL);
+        }
+    }
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    return Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfUninitializeThreadMode(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    // Stop event processing thread
+    otLwfEventProcessingStop(pFilter);
+    
+    // Free timer
+    if (pFilter->EventHighPrecisionTimer)
+    {
+        ExDeleteTimer(pFilter->EventHighPrecisionTimer, TRUE, FALSE, NULL);
+        pFilter->EventHighPrecisionTimer = NULL;
+    }
+
+    // Close handle to settings registry key
+    if (pFilter->otSettingsRegKey)
+    {
+        ZwClose(pFilter->otSettingsRegKey);
+        pFilter->otSettingsRegKey = NULL;
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+#if DEBUG_ALLOC
+PMS_FILTER
+otLwfFindFromCurrentThread()
+{
+    PMS_FILTER pOutput = NULL;
+    HANDLE CurThreadId = PsGetCurrentThreadId();
+
+    NdisAcquireSpinLock(&FilterListLock);
+
+    for (PLIST_ENTRY Link = FilterModuleList.Flink; Link != &FilterModuleList; Link = Link->Flink)
+    {
+        PMS_FILTER pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink);
+
+        if (pFilter->otThreadId == CurThreadId)
+        {
+            pOutput = pFilter;
+            break;
+        }
+    }
+
+    NdisReleaseSpinLock(&FilterListLock);
+
+    NT_ASSERT(pOutput);
+    return pOutput;
+}
+#endif
+
+#define OTPLAT_CALLOC_TAG 'OTDM'
+#define BUFFER_POOL_TAG 'OTBP'
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+otLwfReleaseInstance(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    if (pFilter->otCtx != NULL)
+    {
+        otInstanceFinalize(pFilter->otCtx);
+        pFilter->otCtx = NULL;
+
+#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
+
+        // Free all the pools as there should be no outstanding
+        // references to the buffers any more.
+        BufferPool *curPool = pFilter->otBufferPoolHead;
+        while (curPool != NULL)
+        {
+            BufferPool *nextPool = curPool->Next;
+            ExFreePoolWithTag(curPool, BUFFER_POOL_TAG);
+            curPool = nextPool;
+        }
+
+#endif
+
+#if DEBUG_ALLOC
+
+        NT_ASSERT(pFilter->otOutstandingAllocationCount == 0);
+        NT_ASSERT(pFilter->otOutstandingMemoryAllocated == 0);
+        PLIST_ENTRY Link = pFilter->otOutStandingAllocations.Flink;
+        while (Link != &pFilter->otOutStandingAllocations)
+        {
+            OT_ALLOC* AllocHeader = CONTAINING_RECORD(Link, OT_ALLOC, Link);
+            Link = Link->Flink;
+
+            LogVerbose(DRIVER_DEFAULT, "Leaked Alloc ID:%u", AllocHeader->ID);
+
+            ExFreePoolWithTag(AllocHeader, OTPLAT_CALLOC_TAG);
+        }
+
+#endif
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+//
+// OpenThread Platform functions
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void *otPlatCAlloc(size_t aNum, size_t aSize)
+{
+    size_t totalSize = aNum * aSize;
+#if DEBUG_ALLOC
+    totalSize += sizeof(OT_ALLOC);
+#endif
+    PVOID mem = ExAllocatePoolWithTag(PagedPool, totalSize, OTPLAT_CALLOC_TAG);
+    if (mem)
+    {
+        RtlZeroMemory(mem, totalSize);
+#if DEBUG_ALLOC
+        PMS_FILTER pFilter = otLwfFindFromCurrentThread();
+        //LogVerbose(DRIVER_DEFAULT, "otPlatAlloc(%u) = ID:%u %p", (ULONG)totalSize, pFilter->otAllocationID, mem);
+
+        OT_ALLOC* AllocHeader = (OT_ALLOC*)mem;
+        AllocHeader->Length = (LONG)totalSize;
+        AllocHeader->ID = pFilter->otAllocationID++;
+        InsertTailList(&pFilter->otOutStandingAllocations, &AllocHeader->Link);
+
+        InterlockedIncrement(&pFilter->otOutstandingAllocationCount);
+        InterlockedAdd(&pFilter->otOutstandingMemoryAllocated, AllocHeader->Length);
+        
+        mem = (PUCHAR)(mem) + sizeof(OT_ALLOC);
+#endif
+    }
+    return mem;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void otPlatFree(_In_opt_ void *aPtr)
+{
+    if (aPtr == NULL) return;
+#if DEBUG_ALLOC
+    aPtr = (PUCHAR)(aPtr) - sizeof(OT_ALLOC);
+    //LogVerbose(DRIVER_DEFAULT, "otPlatFree(%p)", aPtr);
+    OT_ALLOC* AllocHeader = (OT_ALLOC*)aPtr;
+
+    PMS_FILTER pFilter = otLwfFindFromCurrentThread();
+    InterlockedDecrement(&pFilter->otOutstandingAllocationCount);
+    InterlockedAdd(&pFilter->otOutstandingMemoryAllocated, -AllocHeader->Length);
+    RemoveEntryList(&AllocHeader->Link);
+#endif
+    ExFreePoolWithTag(aPtr, OTPLAT_CALLOC_TAG);
+}
+
+#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BufferPool* AllocBufferPool(_In_ PMS_FILTER pFilter)
+{
+    // Allocate the memory
+    BufferPool* bufPool = (BufferPool*)ExAllocatePoolWithTag(PagedPool, pFilter->otBufferPoolByteSize, BUFFER_POOL_TAG);
+    if (bufPool == NULL)
+    {
+        LogWarning(DRIVER_DEFAULT, "Failed to allocate new buffer pool!");
+        return NULL;
+    }
+
+    // Zero out the memory
+    RtlZeroMemory(bufPool, pFilter->otBufferPoolByteSize);
+
+    // Set all mNext for the buffers
+    otMessage* prevBuf = (otMessage*)bufPool->Buffers;
+    for (uint16_t i = 1; i < pFilter->otBufferPoolBufferCount; i++)
+    {
+        otMessage* curBuf =
+            (otMessage*)&bufPool->Buffers[i * pFilter->otBufferSize];
+
+        prevBuf->mNext = curBuf;
+        prevBuf = curBuf;
+    }
+
+    LogVerbose(DRIVER_DEFAULT, "Allocated new buffer pool (%d bytes)!", pFilter->otBufferPoolByteSize);
+
+    return bufPool;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+otMessage* GetNextFreeBufferFromPool(_In_ PMS_FILTER pFilter)
+{
+    // Immediately return if we have hit our limit
+    if (pFilter->otBuffersLeft == 0) return NULL;
+
+    // If we don't have any free buffers left, allocate another pool
+    if (pFilter->otFreeBuffers == NULL)
+    {
+        BufferPool *newPool = AllocBufferPool(pFilter);
+        if (newPool == NULL) return NULL; // Out of physical memory
+
+        // Push on top of the pool list
+        newPool->Next = pFilter->otBufferPoolHead;
+        pFilter->otBufferPoolHead = newPool;
+
+        // Set the free buffer list
+        pFilter->otFreeBuffers = (otMessage*)newPool->Buffers;
+    }
+
+    // Pop the top free buffer
+    otMessage* buffer = pFilter->otFreeBuffers;
+    pFilter->otFreeBuffers = pFilter->otFreeBuffers->mNext;
+    pFilter->otBuffersLeft--;
+    buffer->mNext = NULL;
+    return buffer;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void otPlatMessagePoolInit(_In_ otInstance *otCtx, uint16_t aMinNumFreeBuffers, size_t aBufferSize)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+    UNREFERENCED_PARAMETER(aMinNumFreeBuffers);
+
+    // Initialize parameters
+    pFilter->otBufferSize = (uint16_t)aBufferSize;
+    pFilter->otBufferPoolByteSize = (uint16_t)(kPageSize * kPagesPerBufferPool);
+    pFilter->otBufferPoolBufferCount = (uint16_t)((pFilter->otBufferPoolByteSize - sizeof(BufferPool)) / aBufferSize);
+    pFilter->otBuffersLeft = kMaxPagesForBufferPools * pFilter->otBufferPoolBufferCount;
+
+    // Allocate first pool
+    pFilter->otBufferPoolHead = AllocBufferPool(pFilter);
+    ASSERT(pFilter->otBufferPoolHead); // Should this API allow for failure ???
+
+    // Set initial free buffer list
+    pFilter->otFreeBuffers = (otMessage*)pFilter->otBufferPoolHead->Buffers;
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+otMessage *otPlatMessagePoolNew(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return GetNextFreeBufferFromPool(pFilter);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void otPlatMessagePoolFree(_In_ otInstance *otCtx, _In_ otMessage *aBuffer)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+
+    // Put buffer back on the list
+    aBuffer->mNext = pFilter->otFreeBuffers;
+    pFilter->otFreeBuffers = aBuffer;
+    pFilter->otBuffersLeft++;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+uint16_t otPlatMessagePoolNumFreeBuffers(_In_ otInstance *otCtx)
+{
+    NT_ASSERT(otCtx);
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    return pFilter->otBuffersLeft;
+}
+
+#endif
+
+uint32_t otPlatRandomGet()
+{
+    LARGE_INTEGER Counter = KeQueryPerformanceCounter(NULL);
+    return (uint32_t)RtlRandomEx(&Counter.LowPart);
+}
+
+otError otPlatRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength)
+{
+    // Just use the system-preferred random number generator algorithm
+    NTSTATUS status = 
+        BCryptGenRandom(
+            NULL, 
+            aOutput, 
+            (ULONG)aOutputLength, 
+            BCRYPT_USE_SYSTEM_PREFERRED_RNG
+            );
+    NT_ASSERT(NT_SUCCESS(status));
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "BCryptGenRandom failed, %!STATUS!", status);
+        return OT_ERROR_FAILED;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+void otTaskletsSignalPending(_In_ otInstance *otCtx)
+{
+    LogVerbose(DRIVER_DEFAULT, "otTaskletsSignalPending");
+    PMS_FILTER pFilter = otCtxToFilter(otCtx);
+    otLwfEventProcessingIndicateNewTasklet(pFilter);
+}
+
+// Process a role state change
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfProcessRoleStateChange(
+    _In_ PMS_FILTER             pFilter
+    )
+{
+    otDeviceRole prevRole = pFilter->otCachedRole;
+    pFilter->otCachedRole = otThreadGetDeviceRole(pFilter->otCtx);
+    if (prevRole == pFilter->otCachedRole) return;
+
+    LogInfo(DRIVER_DEFAULT, "Interface %!GUID! new role: %!otDeviceRole!", &pFilter->InterfaceGuid, pFilter->otCachedRole);
+
+    // Make sure we are in the correct media connect state
+    otLwfIndicateLinkState(
+        pFilter, 
+        IsAttached(pFilter->otCachedRole) ? 
+            MediaConnectStateConnected : 
+            MediaConnectStateDisconnected);
+}
+
+void otLwfStateChangedCallback(uint32_t aFlags, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+
+    //
+    // Process the notification internally
+    //
+
+    if ((aFlags & OT_CHANGED_IP6_ADDRESS_ADDED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_IP6_ADDRESS_ADDED", pFilter);
+        otLwfRadioAddressesUpdated(pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_IP6_ADDRESS_REMOVED", pFilter);
+        otLwfRadioAddressesUpdated(pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_RLOC_ADDED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_RLOC_ADDED", pFilter);
+        otLwfRadioAddressesUpdated(pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_RLOC_REMOVED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_RLOC_REMOVED", pFilter);
+        otLwfRadioAddressesUpdated(pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_ROLE", pFilter);
+        otLwfProcessRoleStateChange(pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_PARTITION_ID) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_PARTITION_ID", pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER", pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_CHILD_ADDED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_CHILD_ADDED", pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_CHILD_REMOVED) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_CHILD_REMOVED", pFilter);
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_NETDATA) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_NETDATA", pFilter);
+        otIp6SlaacUpdate(pFilter->otCtx, pFilter->otAutoAddresses, ARRAYSIZE(pFilter->otAutoAddresses), otIp6CreateRandomIid, NULL);
+
+#if OPENTHREAD_ENABLE_DHCP6_SERVER
+        otDhcp6ServerUpdate(pFilter->otCtx);
+#endif  // OPENTHREAD_ENABLE_DHCP6_SERVER
+
+#if OPENTHREAD_ENABLE_DHCP6_CLIENT
+        otDhcp6ClientUpdate(pFilter->otCtx, pFilter->otDhcpAddresses, ARRAYSIZE(pFilter->otDhcpAddresses), NULL);
+#endif  // OPENTHREAD_ENABLE_DHCP6_CLIENT
+    }
+
+    if ((aFlags & OT_CHANGED_THREAD_ML_ADDR) != 0)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Filter %p received OT_CHANGED_THREAD_ML_ADDR", pFilter);
+    }
+    
+    //
+    // Queue the notification for clients
+    //
+
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_STATE_CHANGE;
+        NotifEntry->Notif.StateChangePayload.Flags = aFlags;
+
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfActiveScanCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_ACTIVE_SCAN;
+
+        if (aResult)
+        {
+            NotifEntry->Notif.ActiveScanPayload.Valid = TRUE;
+            NotifEntry->Notif.ActiveScanPayload.Results = *aResult;
+        }
+        else
+        {
+            NotifEntry->Notif.ActiveScanPayload.Valid = FALSE;
+        }
+        
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfEnergyScanCallback(_In_ otEnergyScanResult *aResult, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_ENERGY_SCAN;
+
+        if (aResult)
+        {
+            NotifEntry->Notif.EnergyScanPayload.Valid = TRUE;
+            NotifEntry->Notif.EnergyScanPayload.Results = *aResult;
+        }
+        else
+        {
+            NotifEntry->Notif.EnergyScanPayload.Valid = FALSE;
+        }
+        
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfDiscoverCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_DISCOVER;
+
+        if (aResult)
+        {
+            NotifEntry->Notif.DiscoverPayload.Valid = TRUE;
+            NotifEntry->Notif.DiscoverPayload.Results = *aResult;
+        }
+        else
+        {
+            NotifEntry->Notif.DiscoverPayload.Valid = FALSE;
+        }
+        
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfCommissionerEnergyReportCallback(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_COMMISSIONER_ENERGY_REPORT;
+
+        // Limit the number of reports if necessary
+        if (aEnergyListLength > MAX_ENERGY_REPORT_LENGTH) aEnergyListLength = MAX_ENERGY_REPORT_LENGTH;
+        
+        NotifEntry->Notif.CommissionerEnergyReportPayload.ChannelMask = aChannelMask;
+        NotifEntry->Notif.CommissionerEnergyReportPayload.EnergyListLength = aEnergyListLength;
+        memcpy(NotifEntry->Notif.CommissionerEnergyReportPayload.EnergyList, aEnergyList, aEnergyListLength);
+        
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfCommissionerPanIdConflictCallback(uint16_t aPanId, uint32_t aChannelMask, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_COMMISSIONER_PANID_QUERY;
+        
+        NotifEntry->Notif.CommissionerPanIdQueryPayload.PanId = aPanId;
+        NotifEntry->Notif.CommissionerPanIdQueryPayload.ChannelMask = aChannelMask;
+        
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+void otLwfJoinerCallback(otError aError, _In_ void *aContext)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PMS_FILTER pFilter = (PMS_FILTER)aContext;
+    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+    if (NotifEntry)
+    {
+        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+        NotifEntry->Notif.NotifType = OTLWF_NOTIF_JOINER_COMPLETE;
+
+        NotifEntry->Notif.JoinerCompletePayload.Error = aError;
+
+        otLwfIndicateNotification(NotifEntry);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfThreadValueIs(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    )
+{
+    LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value for %s", pFilter, spinel_prop_key_to_cstr(key));
+
+    if (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)
+    {
+        uint8_t scanChannel;
+        int8_t maxRssi;
+        spinel_ssize_t ret;
+
+        ret = spinel_datatype_unpack(
+            value_data_ptr,
+            value_data_len,
+            "Cc",
+            &scanChannel,
+            &maxRssi);
+
+        NT_ASSERT(ret > 0);
+        if (ret > 0)
+        {
+            LogInfo(DRIVER_DEFAULT, "Filter: %p, completed energy scan: Rssi:%d", pFilter, maxRssi);
+            otLwfEventProcessingIndicateEnergyScanResult(pFilter, maxRssi);
+        }
+    }
+    else if (key == SPINEL_PROP_STREAM_RAW)
+    {
+        if (value_data_len < 256)
+        {
+            otLwfEventProcessingIndicateNewMacFrameCommand(
+                pFilter,
+                DispatchLevel,
+                value_data_ptr,
+                (uint8_t)value_data_len);
+        }
+    }
+    else if (key == SPINEL_PROP_STREAM_DEBUG)
+    {
+        const uint8_t* output = NULL;
+        UINT output_len = 0;
+        spinel_ssize_t ret;
+
+        ret = spinel_datatype_unpack(
+            value_data_ptr,
+            value_data_len,
+            SPINEL_DATATYPE_DATA_S,
+            &output,
+            &output_len);
+
+        NT_ASSERT(ret > 0);
+        if (ret > 0 && output && output_len <= (UINT)ret)
+        {
+            if (strnlen((char*)output, output_len) != output_len)
+            {
+                LogInfo(DRIVER_DEFAULT, "DEVICE: %s", (char*)output);
+            }
+            else if (output_len < 128)
+            {
+                char strOutput[128] = { 0 };
+                memcpy(strOutput, output, output_len);
+                LogInfo(DRIVER_DEFAULT, "DEVICE: %s", strOutput);
+            }
+        }
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfThreadValueInserted(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    )
+{
+    LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value Inserted for %s", pFilter, spinel_prop_key_to_cstr(key));
+
+    UNREFERENCED_PARAMETER(pFilter);
+    UNREFERENCED_PARAMETER(DispatchLevel);
+    UNREFERENCED_PARAMETER(key);
+    UNREFERENCED_PARAMETER(value_data_ptr);
+    UNREFERENCED_PARAMETER(value_data_len);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/otLwf/thread.h b/examples/drivers/windows/otLwf/thread.h
new file mode 100644
index 0000000..4621b34
--- /dev/null
+++ b/examples/drivers/windows/otLwf/thread.h
@@ -0,0 +1,191 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the functions for the otLwf Filter Thread mode.
+ */
+
+#ifndef _THREAD_H_
+#define _THREAD_H_
+
+// Helper function that converts an otInstance pointer to a MS_FILTER pointer
+__inline PMS_FILTER otCtxToFilter(_In_ otInstance* otCtx)
+{
+    return *(PMS_FILTER*)((PUCHAR)otCtx - sizeof(PMS_FILTER));
+}
+
+// Helper function to indicate if a role means it is attached or not
+_inline BOOLEAN IsAttached(_In_ otDeviceRole role)
+{
+    return role > OT_DEVICE_ROLE_DETACHED;
+}
+
+//
+// Initialization functions
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfInitializeThreadMode(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfUninitializeThreadMode(
+    _In_ PMS_FILTER pFilter
+    );
+
+//
+// Clean up otInstance
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+otLwfReleaseInstance(
+    _In_ PMS_FILTER pFilter
+    );
+
+//
+// Event Processing Functions
+//
+
+EXT_CALLBACK otLwfEventProcessingTimer;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfEventProcessingStart(
+    _In_ PMS_FILTER             pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingStop(
+    _In_ PMS_FILTER             pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewWaitTime(
+    _In_ PMS_FILTER             pFilter,
+    _In_ ULONG                  waitTime
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewTasklet(
+    _In_ PMS_FILTER             pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateAddressChange(
+    _In_ PMS_FILTER             pFilter,
+    _In_ MIB_NOTIFICATION_TYPE  NotificationType,
+    _In_ PIN6_ADDR              pAddr
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewNetBufferLists(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_ PNET_BUFFER_LIST       NetBufferLists
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNewMacFrameCommand(
+    _In_ PMS_FILTER             pFilter,
+    _In_ BOOLEAN                DispatchLevel,
+    _In_reads_bytes_(BufferLength) 
+         const uint8_t*         Buffer,
+    _In_ uint8_t                BufferLength
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateNetBufferListsCancelled(
+    _In_ PMS_FILTER             pFilter,
+    _In_ PVOID                  CancelId
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+otLwfEventProcessingIndicateIrp(
+    _In_ PMS_FILTER pFilter,
+    _In_ PIRP       Irp
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfEventProcessingIndicateEnergyScanResult(
+    _In_ PMS_FILTER pFilter,
+    _In_ CHAR       MaxRssi
+    );
+
+//
+// OpenThread callbacks
+//
+
+void otLwfStateChangedCallback(uint32_t aFlags, _In_ void *aContext);
+void otLwfReceiveIp6DatagramCallback(_In_ otMessage *aMessage, _In_ void *aContext);
+void otLwfActiveScanCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext);
+void otLwfEnergyScanCallback(_In_ otEnergyScanResult *aResult, _In_ void *aContext);
+void otLwfDiscoverCallback(_In_ otActiveScanResult *aResult, _In_ void *aContext);
+void otLwfCommissionerEnergyReportCallback(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext);
+void otLwfCommissionerPanIdConflictCallback(uint16_t aPanId, uint32_t aChannelMask, _In_ void *aContext);
+void otLwfJoinerCallback(otError aError, _In_ void *aContext);
+
+//
+// Value Callbacks
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfThreadValueIs(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfThreadValueInserted(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    );
+
+#endif  //_THREAD_H_
diff --git a/examples/drivers/windows/otLwf/tunnel.c b/examples/drivers/windows/otLwf/tunnel.c
new file mode 100644
index 0000000..770f635
--- /dev/null
+++ b/examples/drivers/windows/otLwf/tunnel.c
@@ -0,0 +1,671 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file implements the Tunnel mode (Thread Miniport) functions required for the OpenThread library.
+ */
+
+#include "precomp.h"
+#include "tunnel.tmh"
+
+KSTART_ROUTINE otLwfTunWorkerThread;
+
+SPINEL_CMD_HANDLER otLwfIrpCommandHandler;
+
+typedef struct _SPINEL_IRP_CMD_CONTEXT
+{
+    PMS_FILTER              pFilter;
+    PIRP                    Irp;
+    SPINEL_IRP_CMD_HANDLER *Handler;
+    spinel_tid_t            tid;
+} SPINEL_IRP_CMD_CONTEXT;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfTunInitialize(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+    HANDLE threadHandle = NULL;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    NT_ASSERT(pFilter->DeviceCapabilities & OTLWF_DEVICE_CAP_THREAD_1_0);
+    
+    KeInitializeEvent(
+        &pFilter->TunWorkerThreadStopEvent,
+        SynchronizationEvent, // auto-clearing event
+        FALSE                 // event initially non-signalled
+        );
+    KeInitializeEvent(
+        &pFilter->TunWorkerThreadAddressChangedEvent,
+        SynchronizationEvent, // auto-clearing event
+        FALSE                 // event initially non-signalled
+        );
+
+    // Start the worker thread
+    Status = PsCreateSystemThread(
+                &threadHandle,                  // ThreadHandle
+                THREAD_ALL_ACCESS,              // DesiredAccess
+                NULL,                           // ObjectAttributes
+                NULL,                           // ProcessHandle
+                NULL,                           // ClientId
+                otLwfTunWorkerThread,           // StartRoutine
+                pFilter                         // StartContext
+                );
+    if (!NT_SUCCESS(Status))
+    {
+        LogError(DRIVER_DEFAULT, "PsCreateSystemThread failed, %!STATUS!", Status);
+        goto error;
+    }
+
+    // Grab the object reference to the worker thread
+    Status = ObReferenceObjectByHandle(
+                threadHandle,
+                THREAD_ALL_ACCESS,
+                *PsThreadType,
+                KernelMode,
+                &pFilter->TunWorkerThread,
+                NULL
+                );
+    if (!NT_VERIFYMSG("ObReferenceObjectByHandle can't fail with a valid kernel handle", NT_SUCCESS(Status)))
+    {
+        LogError(DRIVER_DEFAULT, "ObReferenceObjectByHandle failed, %!STATUS!", Status);
+        KeSetEvent(&pFilter->TunWorkerThreadStopEvent, IO_NO_INCREMENT, FALSE);
+    }
+
+    // Make sure to enable RLOC passthrough
+    Status =
+        otLwfCmdSetProp(
+            pFilter,
+            SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU,
+            SPINEL_DATATYPE_BOOL_S,
+            TRUE
+        );
+    if (!NT_SUCCESS(Status))
+    {
+        LogError(DRIVER_DEFAULT, "Enabling RLOC pass through failed, %!STATUS!", Status);
+        goto error;
+    }
+
+    // TODO - Query other values and capabilities
+
+error:
+
+    if (!NT_SUCCESS(Status))
+    {
+        otLwfTunUninitialize(pFilter);
+    }
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    return Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfTunUninitialize(
+    _In_ PMS_FILTER pFilter
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    // Clean up worker thread
+    if (pFilter->TunWorkerThread)
+    {
+        LogInfo(DRIVER_DEFAULT, "Stopping tunnel worker thread and waiting for it to complete.");
+
+        // Send event to shutdown worker thread
+        KeSetEvent(&pFilter->TunWorkerThreadStopEvent, 0, FALSE);
+
+        // Wait for worker thread to finish
+        KeWaitForSingleObject(
+            pFilter->TunWorkerThread,
+            Executive,
+            KernelMode,
+            FALSE,
+            NULL
+            );
+
+        // Free worker thread
+        ObDereferenceObject(pFilter->TunWorkerThread);
+        pFilter->TunWorkerThread = NULL;
+
+        LogInfo(DRIVER_DEFAULT, "Tunnel worker thread cleaned up.");
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+// Worker thread for processing all tunnel events
+_Use_decl_annotations_
+VOID
+otLwfTunWorkerThread(
+    PVOID   Context
+    )
+{
+    PMS_FILTER pFilter = (PMS_FILTER)Context;
+    NT_ASSERT(pFilter);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PKEVENT WaitEvents[] = 
+    { 
+        &pFilter->TunWorkerThreadStopEvent,
+        &pFilter->TunWorkerThreadAddressChangedEvent
+    };
+
+    LogFuncExit(DRIVER_DEFAULT);
+    
+    while (true)
+    {
+        // Wait for event to stop or process event to fire
+        NTSTATUS status = 
+            KeWaitForMultipleObjects(
+                ARRAYSIZE(WaitEvents), 
+                (PVOID*)WaitEvents, 
+                WaitAny, 
+                Executive, 
+                KernelMode, 
+                FALSE, 
+                NULL, 
+                NULL);
+
+        // If it is the first event, then we are shutting down. Exit loop and terminate thread
+        if (status == STATUS_WAIT_0)
+        {
+            LogInfo(DRIVER_DEFAULT, "Received tunnel worker thread shutdown event.");
+            break;
+        }
+        else if (status == STATUS_WAIT_0 + 1) // TunWorkerThreadAddressChangedEvent fired
+        {
+            PVOID DataBuffer = NULL;
+            const uint8_t* value_data_ptr = NULL;
+            spinel_size_t value_data_len = 0;
+            
+            // Query the current addresses
+            status = 
+                otLwfCmdGetProp(
+                    pFilter,
+                    &DataBuffer,
+                    SPINEL_PROP_IPV6_ADDRESS_TABLE,
+                    SPINEL_DATATYPE_DATA_S,
+                    &value_data_ptr,
+                    &value_data_len);
+            if (NT_SUCCESS(status))
+            {
+                uint32_t aNotifFlags = 0;
+                otLwfTunAddressesUpdated(pFilter, value_data_ptr, value_data_len, &aNotifFlags);
+
+                // Send notification
+                if (aNotifFlags != 0)
+                {
+                    PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+                    if (NotifEntry)
+                    {
+                        RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+                        NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+                        NotifEntry->Notif.NotifType = OTLWF_NOTIF_STATE_CHANGE;
+                        NotifEntry->Notif.StateChangePayload.Flags = aNotifFlags;
+
+                        otLwfIndicateNotification(NotifEntry);
+                    }
+                }
+            }
+            else
+            {
+                LogWarning(DRIVER_DEFAULT, "Failed to query addresses, %!STATUS!", status);
+            }
+
+            if (DataBuffer) FILTER_FREE_MEM(DataBuffer);
+        }
+        else
+        {
+            LogWarning(DRIVER_DEFAULT, "Unexpected wait result, %!STATUS!", status);
+        }
+    }
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+otLwfIrpCommandHandler(
+    _In_ PMS_FILTER pFilter,
+    _In_ PVOID Context,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_reads_bytes_(DataLength) const uint8_t* Data,
+    _In_ spinel_size_t DataLength
+    )
+{
+    SPINEL_IRP_CMD_CONTEXT* CmdContext = (SPINEL_IRP_CMD_CONTEXT*)Context;
+    PIO_STACK_LOCATION  IrpSp = IoGetCurrentIrpStackLocation(CmdContext->Irp);
+    
+    ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
+    PVOID OutBuffer = CmdContext->Irp->AssociatedIrp.SystemBuffer;
+    ULONG OutBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+    ULONG OrigOutBufferLength = OutBufferLength;
+
+    NTSTATUS status;
+
+    UNREFERENCED_PARAMETER(pFilter);
+
+    // Clear the cancel routine
+    IoSetCancelRoutine(CmdContext->Irp, NULL);
+    
+    if (Data == NULL)
+    {
+        status = STATUS_CANCELLED;
+        OutBufferLength = 0;
+    }
+    else if (Command == SPINEL_CMD_PROP_VALUE_IS && Key == SPINEL_PROP_LAST_STATUS)
+    {
+        spinel_status_t spinel_status = SPINEL_STATUS_OK;
+        spinel_ssize_t packed_len = spinel_datatype_unpack(Data, DataLength, "i", &spinel_status);
+        if (packed_len < 0 || (ULONG)packed_len > DataLength)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+        else
+        {
+            status = ThreadErrorToNtstatus(SpinelStatusToThreadError(spinel_status));
+        }
+    }
+    else if (CmdContext->Handler)
+    {
+        status = CmdContext->Handler(Key, Data, DataLength, OutBuffer, &OutBufferLength);
+    }
+    else // No handler, so no output
+    {
+        status = STATUS_SUCCESS;
+        OutBufferLength = 0;
+    }
+
+    // Clear any leftover output buffer
+    if (OutBufferLength < OrigOutBufferLength)
+    {
+        RtlZeroMemory((PUCHAR)OutBuffer + OutBufferLength, OrigOutBufferLength - OutBufferLength);
+    }
+
+    LogVerbose(DRIVER_IOCTL, "Completing Irp=%p, with %!STATUS! for %s (Out:%u)", 
+                CmdContext->Irp, status, IoCtlString(IoControlCode), OutBufferLength);
+
+    // Complete the IRP
+    CmdContext->Irp->IoStatus.Information = OutBufferLength;
+    CmdContext->Irp->IoStatus.Status = status;
+    IoCompleteRequest(CmdContext->Irp, IO_NO_INCREMENT);
+
+    FILTER_FREE_MEM(Context);
+}
+
+_Function_class_(DRIVER_CANCEL)
+_Requires_lock_held_(_Global_cancel_spin_lock_)
+_Releases_lock_(_Global_cancel_spin_lock_)
+_IRQL_requires_min_(DISPATCH_LEVEL)
+_IRQL_requires_(DISPATCH_LEVEL)
+VOID
+otLwfTunCancelIrp(
+    _Inout_ struct _DEVICE_OBJECT *DeviceObject,
+    _Inout_ _IRQL_uses_cancel_ struct _IRP *Irp
+    )
+{
+    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
+    SPINEL_IRP_CMD_CONTEXT* CmdContext = (SPINEL_IRP_CMD_CONTEXT*)IrpStack->Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    LogFuncEntryMsg(DRIVER_IOCTL, "Irp=%p", Irp);
+
+    IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+    // Try to cancel pending command
+    otLwfCmdCancel(
+        CmdContext->pFilter, 
+        (Irp->CancelIrql == DISPATCH_LEVEL) ? TRUE : FALSE, 
+        CmdContext->tid);
+
+    LogFuncExit(DRIVER_IOCTL);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunSendCommandForIrp(
+    _In_ PMS_FILTER pFilter,
+    _In_ PIRP Irp,
+    _In_opt_ SPINEL_IRP_CMD_HANDLER *Handler,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    ...
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    SPINEL_IRP_CMD_CONTEXT *pContext = NULL;
+    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+    // Create the context structure
+    pContext = FILTER_ALLOC_MEM(pFilter->FilterHandle, sizeof(SPINEL_IRP_CMD_CONTEXT));
+    if (pContext == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        LogWarning(DRIVER_DEFAULT, "Failed to allocate irp cmd context");
+        goto exit;
+    }
+
+    pContext->pFilter = pFilter;
+    pContext->Irp = Irp;
+    pContext->Handler = Handler;
+
+    NT_ASSERT(IrpStack->Context == NULL);
+    IrpStack->Context = pContext;
+
+    // Set the cancel routine
+    IoSetCancelRoutine(Irp, otLwfTunCancelIrp);
+    
+    va_list args;
+    va_start(args, pack_format);
+    status = 
+        otLwfCmdSendAsyncV(
+            pFilter, 
+            otLwfIrpCommandHandler, 
+            pContext, 
+            &pContext->tid,
+            Command, 
+            Key, 
+            MaxDataLength, 
+            pack_format, 
+            args);
+    va_end(args);
+
+    // Remove the handler entry from the list
+    if (!NT_SUCCESS(status))
+    {
+        // Clear the cancel routine
+        IoSetCancelRoutine(Irp, NULL);
+
+        FILTER_FREE_MEM(pContext);
+    }
+
+exit:
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfTunValueIs(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    )
+{
+    uint32_t aNotifFlags = 0;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value for %s", pFilter, spinel_prop_key_to_cstr(key));
+
+    if (key == SPINEL_PROP_NET_ROLE)
+    {
+        uint8_t value;
+        spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT8_S, &value);
+
+        LogInfo(DRIVER_DEFAULT, "Interface %!GUID! new spinel role: %u", &pFilter->InterfaceGuid, value);
+
+        // Make sure we are in the correct media connect state
+        otLwfIndicateLinkState(
+            pFilter,
+            value > SPINEL_NET_ROLE_DETACHED ?
+            MediaConnectStateConnected :
+            MediaConnectStateDisconnected);
+
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_ROLE;
+    }
+    else if (key == SPINEL_PROP_IPV6_LL_ADDR)
+    {
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_LL_ADDR;
+    }
+    else if (key == SPINEL_PROP_IPV6_ML_ADDR)
+    {
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_ML_ADDR;
+    }
+    else if (key == SPINEL_PROP_NET_PARTITION_ID)
+    {
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_PARTITION_ID;
+    }
+    else if (key == SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER)
+    {
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER;
+    }
+    else if (key == SPINEL_PROP_IPV6_ADDRESS_TABLE)
+    {
+        KeSetEvent(&pFilter->TunWorkerThreadAddressChangedEvent, IO_NO_INCREMENT, FALSE);
+    }
+    else if (key == SPINEL_PROP_THREAD_CHILD_TABLE)
+    {
+        // TODO - Update cached children
+        // TODO - Send notification
+    }
+    else if (key == SPINEL_PROP_THREAD_ON_MESH_NETS)
+    {
+        // TODO - Slaac
+
+        // Set flag to indicate we should send a notification
+        aNotifFlags = OT_CHANGED_THREAD_NETDATA;
+    }
+    else if ((key == SPINEL_PROP_STREAM_NET) || (key == SPINEL_PROP_STREAM_NET_INSECURE))
+    {
+        const uint8_t* frame_ptr = NULL;
+        UINT frame_len = 0;
+        spinel_ssize_t ret;
+
+        ret = spinel_datatype_unpack(
+            value_data_ptr,
+            value_data_len,
+            SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S,
+            &frame_ptr,
+            &frame_len,
+            NULL,
+            NULL);
+
+        NT_ASSERT(ret > 0);
+        if (ret > 0)
+        {
+            otLwfTunReceiveIp6Packet(
+                pFilter,
+                DispatchLevel,
+                (SPINEL_PROP_STREAM_NET_INSECURE == key) ? FALSE : TRUE,
+                frame_ptr,
+                frame_len);
+        }
+    }
+    else if (key == SPINEL_PROP_MAC_SCAN_STATE)
+    {
+        // TODO - If pending scan, send notification of completion
+    }
+    else if (key == SPINEL_PROP_STREAM_RAW)
+    {
+        // May be used in the future
+    }
+    else if (key == SPINEL_PROP_STREAM_DEBUG)
+    {
+        const uint8_t* output = NULL;
+        UINT output_len = 0;
+        spinel_ssize_t ret;
+
+        ret = spinel_datatype_unpack(
+            value_data_ptr,
+            value_data_len,
+            SPINEL_DATATYPE_DATA_S,
+            &output,
+            &output_len);
+
+        NT_ASSERT(ret > 0);
+        if (ret > 0 && output && output_len <= (UINT)ret)
+        {
+            if (strnlen((char*)output, output_len) != output_len)
+            {
+                LogInfo(DRIVER_DEFAULT, "DEVICE: %s", (char*)output);
+            }
+            else if (output_len < 128)
+            {
+                char strOutput[128] = { 0 };
+                memcpy(strOutput, output, output_len);
+                LogInfo(DRIVER_DEFAULT, "DEVICE: %s", strOutput);
+            }
+        }
+    }
+
+    // Send notification
+    if (aNotifFlags != 0)
+    {
+        PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+        if (NotifEntry)
+        {
+            RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+            NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+            NotifEntry->Notif.NotifType = OTLWF_NOTIF_STATE_CHANGE;
+            NotifEntry->Notif.StateChangePayload.Flags = aNotifFlags;
+
+            otLwfIndicateNotification(NotifEntry);
+        }
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfTunValueInserted(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    )
+{
+    LogFuncEntryMsg(DRIVER_DEFAULT, "[%p] received Value Inserted for %s", pFilter, spinel_prop_key_to_cstr(key));
+
+    UNREFERENCED_PARAMETER(pFilter);
+    UNREFERENCED_PARAMETER(DispatchLevel);
+    UNREFERENCED_PARAMETER(value_data_ptr);
+    UNREFERENCED_PARAMETER(value_data_len);
+
+    if (key == SPINEL_PROP_MAC_SCAN_BEACON)
+    {
+        PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+        if (NotifEntry)
+        {
+            RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+            NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+            NotifEntry->Notif.NotifType = OTLWF_NOTIF_ACTIVE_SCAN;
+            NotifEntry->Notif.ActiveScanPayload.Valid = TRUE;
+
+            const uint8_t *aExtAddr = NULL;
+            const uint8_t *aExtPanId = NULL;
+            const char *aNetworkName = NULL;
+            unsigned int xpanid_len = 0;
+
+            if (try_spinel_datatype_unpack(
+                value_data_ptr,
+                value_data_len,
+                SPINEL_DATATYPE_MAC_SCAN_RESULT_S(
+                    SPINEL_802_15_4_DATATYPE_MAC_SCAN_RESULT_V1_S,
+                    SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V1_S
+                ),
+                &NotifEntry->Notif.ActiveScanPayload.Results.mChannel,
+                &NotifEntry->Notif.ActiveScanPayload.Results.mRssi,
+                &aExtAddr,
+                NULL, // saddr (don't care)
+                &NotifEntry->Notif.ActiveScanPayload.Results.mPanId,
+                &NotifEntry->Notif.ActiveScanPayload.Results.mLqi,
+                NULL, // proto (don't care)
+                NULL, // flags (don't care)
+                &aNetworkName,
+                &aExtPanId,
+                &xpanid_len
+            ) &&
+                aExtAddr != NULL && aExtPanId != NULL && aNetworkName != NULL &&
+                xpanid_len == OT_EXT_PAN_ID_SIZE)
+            {
+                memcpy_s(NotifEntry->Notif.ActiveScanPayload.Results.mExtAddress.m8,
+                    sizeof(NotifEntry->Notif.ActiveScanPayload.Results.mExtAddress.m8),
+                    aExtAddr, sizeof(otExtAddress));
+                memcpy_s(NotifEntry->Notif.ActiveScanPayload.Results.mExtendedPanId.m8,
+                    sizeof(NotifEntry->Notif.ActiveScanPayload.Results.mExtendedPanId.m8),
+                    aExtPanId, sizeof(otExtendedPanId));
+                strcpy_s(NotifEntry->Notif.ActiveScanPayload.Results.mNetworkName.m8,
+                    sizeof(NotifEntry->Notif.ActiveScanPayload.Results.mNetworkName.m8),
+                    aNetworkName);
+                otLwfIndicateNotification(NotifEntry);
+            }
+            else
+            {
+                FILTER_FREE_MEM(NotifEntry);
+            }
+        }
+    }
+    else if (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)
+    {
+        PFILTER_NOTIFICATION_ENTRY NotifEntry = FILTER_ALLOC_NOTIF(pFilter);
+        if (NotifEntry)
+        {
+            RtlZeroMemory(NotifEntry, sizeof(FILTER_NOTIFICATION_ENTRY));
+            NotifEntry->Notif.InterfaceGuid = pFilter->InterfaceGuid;
+            NotifEntry->Notif.NotifType = OTLWF_NOTIF_ENERGY_SCAN;
+            NotifEntry->Notif.EnergyScanPayload.Valid = TRUE;
+
+            if (try_spinel_datatype_unpack(
+                value_data_ptr,
+                value_data_len,
+                "Cc",
+                &NotifEntry->Notif.EnergyScanPayload.Results.mChannel,
+                &NotifEntry->Notif.EnergyScanPayload.Results.mMaxRssi
+            ))
+            {
+                otLwfIndicateNotification(NotifEntry);
+            }
+            else
+            {
+                FILTER_FREE_MEM(NotifEntry);
+            }
+        }
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/otLwf/tunnel.h b/examples/drivers/windows/otLwf/tunnel.h
new file mode 100644
index 0000000..8311207
--- /dev/null
+++ b/examples/drivers/windows/otLwf/tunnel.h
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the functions for the otLwf Filter Tunnel mode.
+ */
+
+#ifndef _TUNNEL_H_
+#define _TUNNEL_H_
+
+//
+// Initialization
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS 
+otLwfTunInitialize(
+    _In_ PMS_FILTER pFilter
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void 
+otLwfTunUninitialize(
+    _In_ PMS_FILTER pFilter
+    );
+
+//
+// Irp Commands
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+otLwfTunSendCommandForIrp(
+    _In_ PMS_FILTER pFilter,
+    _In_ PIRP Irp,
+    _In_opt_ SPINEL_IRP_CMD_HANDLER *Handler,
+    _In_ UINT Command,
+    _In_ spinel_prop_key_t Key,
+    _In_ ULONG MaxDataLength,
+    _In_opt_ const char *pack_format, 
+    ...
+    );
+
+//
+// Value Callbacks
+//
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfTunValueIs(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+otLwfTunValueInserted(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ spinel_prop_key_t key,
+    _In_reads_bytes_(value_data_len) const uint8_t* value_data_ptr,
+    _In_ spinel_size_t value_data_len
+    );
+
+// Called in response to receiving a Spinel Ip6 packet command
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void 
+otLwfTunReceiveIp6Packet(
+    _In_ PMS_FILTER pFilter,
+    _In_ BOOLEAN DispatchLevel,
+    _In_ BOOLEAN Secure,
+    _In_reads_bytes_(BufferLength) const uint8_t* Buffer,
+    _In_ UINT BufferLength
+    );
+
+#endif  //_TUNNEL_H_
diff --git a/examples/drivers/windows/otNodeApi/dllmain.cpp b/examples/drivers/windows/otNodeApi/dllmain.cpp
new file mode 100644
index 0000000..beae2f6
--- /dev/null
+++ b/examples/drivers/windows/otNodeApi/dllmain.cpp
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "dllmain.tmh"
+
+BOOL 
+__stdcall 
+DllMain(
+    HINSTANCE hinstDll, 
+    DWORD dwReason, 
+    LPVOID /* lpvReserved */
+    )
+{
+    switch (dwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hinstDll);
+        WPP_INIT_TRACING(L"otNodeApi");
+        break;
+
+    case DLL_PROCESS_DETACH:
+        Unload();
+        WPP_CLEANUP();
+        break;
+
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+        break;
+    }
+
+    return TRUE;
+}
+
diff --git a/examples/drivers/windows/otNodeApi/otNodeApi.cpp b/examples/drivers/windows/otNodeApi/otNodeApi.cpp
new file mode 100644
index 0000000..381412d
--- /dev/null
+++ b/examples/drivers/windows/otNodeApi/otNodeApi.cpp
@@ -0,0 +1,2375 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "precomp.h"
+#include "otNodeApi.tmh"
+
+#define DEBUG_PING 1
+
+#define GUID_FORMAT "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
+#define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
+
+typedef DWORD (*fp_otvmpOpenHandle)(_Out_ HANDLE* phandle);
+typedef VOID  (*fp_otvmpCloseHandle)(_In_ HANDLE handle);
+typedef DWORD (*fp_otvmpAddVirtualBus)(_In_ HANDLE handle, _Inout_ ULONG* pBusNumber, _Out_ ULONG* pIfIndex);
+typedef DWORD (*fp_otvmpRemoveVirtualBus)(_In_ HANDLE handle, ULONG BusNumber);
+typedef DWORD (*fp_otvmpSetAdapterTopologyGuid)(_In_ HANDLE handle, DWORD BusNumber, _In_ const GUID* pTopologyGuid);
+typedef void (*fp_otvmpListenerCallback)(_In_opt_ PVOID aContext, _In_ ULONG SourceInterfaceIndex, _In_reads_bytes_(FrameLength) PUCHAR FrameBuffer, _In_ UCHAR FrameLength, _In_ UCHAR Channel);
+typedef HANDLE (*fp_otvmpListenerCreate)(_In_ const GUID* pAdapterTopologyGuid);
+typedef void (*fp_otvmpListenerDestroy)(_In_opt_ HANDLE pHandle);
+typedef void(*fp_otvmpListenerRegister)(_In_ HANDLE pHandle, _In_opt_ fp_otvmpListenerCallback Callback, _In_opt_ PVOID Context);
+
+fp_otvmpOpenHandle              otvmpOpenHandle = nullptr;
+fp_otvmpCloseHandle             otvmpCloseHandle = nullptr;
+fp_otvmpAddVirtualBus           otvmpAddVirtualBus = nullptr;
+fp_otvmpRemoveVirtualBus        otvmpRemoveVirtualBus = nullptr;
+fp_otvmpSetAdapterTopologyGuid  otvmpSetAdapterTopologyGuid = nullptr;
+fp_otvmpListenerCreate          otvmpListenerCreate = nullptr;
+fp_otvmpListenerDestroy         otvmpListenerDestroy = nullptr;
+fp_otvmpListenerRegister        otvmpListenerRegister = nullptr;
+
+HMODULE gVmpModule = nullptr;
+HANDLE  gVmpHandle = nullptr;
+
+ULONG gNextBusNumber = 1;
+GUID gTopologyGuid = {0};
+
+volatile LONG gNumberOfInterfaces = 0;
+CRITICAL_SECTION gCS;
+vector<otNode*> gNodes;
+HANDLE gDeviceArrivalEvent = nullptr;
+
+otApiInstance *gApiInstance = nullptr;
+
+_Success_(return == OT_ERROR_NONE)
+otError otNodeParsePrefix(const char *aStrPrefix, _Out_ otIp6Prefix *aPrefix)
+{
+    char *prefixLengthStr;
+    char *endptr;
+
+    if ((prefixLengthStr = (char*)strchr(aStrPrefix, '/')) == NULL)
+    {
+        printf("invalid prefix (%s)!\r\n", aStrPrefix);
+        return OT_ERROR_INVALID_ARGS;
+    }
+
+    *prefixLengthStr++ = '\0';
+    
+    auto error = otIp6AddressFromString(aStrPrefix, &aPrefix->mPrefix);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("ipaddr (%s) to string failed, 0x%x!\r\n", aStrPrefix, error);
+        return error;
+    }
+
+    aPrefix->mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
+    
+    if (*endptr != '\0')
+    {
+        printf("invalid prefix ending (%s)!\r\n", aStrPrefix);
+        return OT_ERROR_PARSE;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+void OTCALL otNodeDeviceAvailabilityChanged(bool aAdded, const GUID *, void *)
+{
+    if (aAdded) SetEvent(gDeviceArrivalEvent);
+}
+
+otApiInstance* GetApiInstance()
+{
+    if (gApiInstance == nullptr)
+    { 
+        WSADATA wsaData;
+        int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
+        if (result != 0)
+        {
+            printf("WSAStartup failed!\r\n");
+            return nullptr;
+        }
+
+        gApiInstance = otApiInit();
+        if (gApiInstance == nullptr)
+        {
+            printf("otApiInit failed!\r\n");
+            Unload();
+            return nullptr;
+        }
+
+        InitializeCriticalSection(&gCS);
+
+        gVmpModule = LoadLibrary(TEXT("otvmpapi.dll"));
+        if (gVmpModule == nullptr)
+        {
+            printf("LoadLibrary(\"otvmpapi\") failed!\r\n");
+            Unload();
+            return nullptr;
+        }
+
+        otvmpOpenHandle             = (fp_otvmpOpenHandle)GetProcAddress(gVmpModule, "otvmpOpenHandle");
+        otvmpCloseHandle            = (fp_otvmpCloseHandle)GetProcAddress(gVmpModule, "otvmpCloseHandle");
+        otvmpAddVirtualBus          = (fp_otvmpAddVirtualBus)GetProcAddress(gVmpModule, "otvmpAddVirtualBus");
+        otvmpRemoveVirtualBus       = (fp_otvmpRemoveVirtualBus)GetProcAddress(gVmpModule, "otvmpRemoveVirtualBus");
+        otvmpSetAdapterTopologyGuid = (fp_otvmpSetAdapterTopologyGuid)GetProcAddress(gVmpModule, "otvmpSetAdapterTopologyGuid");
+        otvmpListenerCreate         = (fp_otvmpListenerCreate)GetProcAddress(gVmpModule, "otvmpListenerCreate");
+        otvmpListenerDestroy        = (fp_otvmpListenerDestroy)GetProcAddress(gVmpModule, "otvmpListenerDestroy");
+        otvmpListenerRegister       = (fp_otvmpListenerRegister)GetProcAddress(gVmpModule, "otvmpListenerRegister");
+
+        assert(otvmpOpenHandle);
+        assert(otvmpCloseHandle);
+        assert(otvmpAddVirtualBus);
+        assert(otvmpRemoveVirtualBus);
+        assert(otvmpSetAdapterTopologyGuid);
+        assert(otvmpListenerCreate);
+        assert(otvmpListenerDestroy);
+        assert(otvmpListenerRegister);
+
+        if (otvmpOpenHandle == nullptr) printf("otvmpOpenHandle is null!\r\n");
+        if (otvmpCloseHandle == nullptr) printf("otvmpCloseHandle is null!\r\n");
+        if (otvmpAddVirtualBus == nullptr) printf("otvmpAddVirtualBus is null!\r\n");
+        if (otvmpRemoveVirtualBus == nullptr) printf("otvmpRemoveVirtualBus is null!\r\n");
+        if (otvmpSetAdapterTopologyGuid == nullptr) printf("otvmpSetAdapterTopologyGuid is null!\r\n");
+        if (otvmpListenerCreate == nullptr) printf("otvmpListenerCreate is null!\r\n");
+        if (otvmpListenerDestroy == nullptr) printf("otvmpListenerDestroy is null!\r\n");
+        if (otvmpListenerRegister == nullptr) printf("otvmpListenerRegister is null!\r\n");
+
+        (VOID)otvmpOpenHandle(&gVmpHandle);
+        if (gVmpHandle == nullptr)
+        {
+            printf("otvmpOpenHandle failed!\r\n");
+            Unload();
+            return nullptr;
+        }
+
+        auto status = UuidCreate(&gTopologyGuid);
+        if (status != NO_ERROR)
+        {
+            printf("UuidCreate failed, 0x%x!\r\n", status);
+            Unload();
+            return nullptr;
+        }
+
+        auto offset = getenv("INSTANCE");
+        if (offset)
+        {
+            gNextBusNumber = (atoi(offset) * 32) % 1000 + 1;
+        }
+        else
+        {
+            srand(gTopologyGuid.Data1);
+            gNextBusNumber = rand() % 1000 + 1;
+        }
+
+        gDeviceArrivalEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+
+        // Set callback to wait for device arrival notifications
+        otSetDeviceAvailabilityChangedCallback(gApiInstance, otNodeDeviceAvailabilityChanged, nullptr);
+
+        printf("New topology created\r\n" GUID_FORMAT " [%d]\r\n\r\n", GUID_ARG(gTopologyGuid), gNextBusNumber);
+    }
+
+    InterlockedIncrement(&gNumberOfInterfaces);
+
+    return gApiInstance;
+}
+
+void ReleaseApiInstance()
+{
+    if (0 == InterlockedDecrement(&gNumberOfInterfaces))
+    {
+        // Uninitialize everything else if this is the last ref
+        Unload();
+    }
+}
+
+void Unload()
+{
+    if (gNumberOfInterfaces != 0)
+    {
+        printf("Unloaded with %d outstanding nodes!\r\n", gNumberOfInterfaces);
+    }
+
+    if (gApiInstance)
+    {
+        otSetDeviceAvailabilityChangedCallback(gApiInstance, nullptr, nullptr);
+
+        if (gDeviceArrivalEvent != nullptr)
+        {
+            CloseHandle(gDeviceArrivalEvent);
+            gDeviceArrivalEvent = nullptr;
+        }
+
+        if (gVmpHandle != nullptr)
+        {
+            otvmpCloseHandle(gVmpHandle);
+            gVmpHandle = nullptr;
+        }
+
+        if (gVmpModule != nullptr)
+        {
+            FreeLibrary(gVmpModule);
+            gVmpModule = nullptr;
+        }
+
+        DeleteCriticalSection(&gCS);
+
+        otApiFinalize(gApiInstance);
+        gApiInstance = nullptr;
+
+        WSACleanup();
+
+        printf("Topology destroyed\r\n");
+    }
+}
+
+int Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
+{
+    size_t hexLength = strlen(aHex);
+    const char *hexEnd = aHex + hexLength;
+    uint8_t *cur = aBin;
+    uint8_t numChars = hexLength & 1;
+    uint8_t byte = 0;
+
+    if ((hexLength + 1) / 2 > aBinLength)
+    {
+        return -1;
+    }
+
+    while (aHex < hexEnd)
+    {
+        if ('A' <= *aHex && *aHex <= 'F')
+        {
+            byte |= 10 + (*aHex - 'A');
+        }
+        else if ('a' <= *aHex && *aHex <= 'f')
+        {
+            byte |= 10 + (*aHex - 'a');
+        }
+        else if ('0' <= *aHex && *aHex <= '9')
+        {
+            byte |= *aHex - '0';
+        }
+        else
+        {
+            return -1;
+        }
+
+        aHex++;
+        numChars++;
+
+        if (numChars >= 2)
+        {
+            numChars = 0;
+            *cur++ = byte;
+            byte = 0;
+        }
+        else
+        {
+            byte <<= 4;
+        }
+    }
+
+    return static_cast<int>(cur - aBin);
+}
+
+typedef struct otPingHandler
+{
+    otNode*         mParentNode;
+    bool            mActive;
+    otIp6Address    mAddress;
+    SOCKET          mSocket;
+    CHAR            mRecvBuffer[1500];
+    WSAOVERLAPPED   mOverlapped;
+    PTP_WAIT        mThreadpoolWait;
+    WSABUF          mWSARecvBuffer;
+    DWORD           mNumBytesReceived;
+    SOCKADDR_IN6    mSourceAddr6;
+    int             mSourceAddr6Len;
+
+} otPingHandler;
+
+typedef struct otNode
+{
+    uint32_t                mId;
+    DWORD                   mBusIndex;
+    DWORD                   mInterfaceIndex;
+    otInstance*             mInstance;
+    HANDLE                  mEnergyScanEvent;
+    HANDLE                  mPanIdConflictEvent;
+    CRITICAL_SECTION        mCS;
+    vector<otPingHandler*>  mPingHandlers;
+    vector<void*>           mMemoryToFree;
+} otNode;
+
+const char* otDeviceRoleToString(otDeviceRole role)
+{
+    switch (role)
+    {
+    case OT_DEVICE_ROLE_DISABLED: return "disabled";
+    case OT_DEVICE_ROLE_DETACHED: return "detached";
+    case OT_DEVICE_ROLE_CHILD:    return "child";
+    case OT_DEVICE_ROLE_ROUTER:   return "router";
+    case OT_DEVICE_ROLE_LEADER:   return "leader";
+    default:                      return "invalid";
+    }
+}
+
+const USHORT CertificationPingPort = htons(12345);
+
+const IN6_ADDR LinkLocalAllNodesAddress    = { { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } };
+const IN6_ADDR LinkLocalAllRoutersAddress  = { { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02 } };
+const IN6_ADDR RealmLocalAllNodesAddress   = { { 0xFF, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } };
+const IN6_ADDR RealmLocalAllRoutersAddress = { { 0xFF, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02 } };
+const IN6_ADDR RealmLocalSpecialAddress    = { { 0xFF, 0x33, 0, 0x40, 0xfd, 0xde, 0xad, 0, 0xbe, 0xef, 0, 0, 0, 0, 0, 0x01 } };
+
+void
+CALLBACK 
+PingHandlerRecvCallback(
+    _Inout_     PTP_CALLBACK_INSTANCE /* Instance */,
+    _Inout_opt_ PVOID                 Context,
+    _Inout_     PTP_WAIT              /* Wait */,
+    _In_        TP_WAIT_RESULT        /* WaitResult */
+    )
+{
+    otPingHandler *aPingHandler = (otPingHandler*)Context;
+    if (aPingHandler == NULL) return;
+    
+    // Get the result of the IO operation
+    DWORD cbTransferred = 0;
+    DWORD dwFlags = 0;
+    if (!WSAGetOverlappedResult(
+            aPingHandler->mSocket,
+            &aPingHandler->mOverlapped,
+            &cbTransferred,
+            TRUE,
+            &dwFlags))
+    {
+        int result = WSAGetLastError();
+        // Only log if we are shutting down
+        if (result != WSAENOTSOCK && result != ERROR_OPERATION_ABORTED)
+            printf("WSAGetOverlappedResult failed, 0x%x\r\n", result);
+        return;
+    }
+
+    int result;
+
+    // Make sure it didn't come from our address
+    if (memcmp(&aPingHandler->mSourceAddr6.sin6_addr, &aPingHandler->mAddress, sizeof(IN6_ADDR)) != 0)
+    {
+        bool shouldReply = true;
+
+        // TODO - Fix this hack...
+        auto RecvDest = (const otIp6Address*)aPingHandler->mRecvBuffer;
+        if (memcmp(RecvDest, &LinkLocalAllRoutersAddress, sizeof(IN6_ADDR)) == 0 ||
+            memcmp(RecvDest, &RealmLocalAllRoutersAddress, sizeof(IN6_ADDR)) == 0)
+        {
+            auto Role = otThreadGetDeviceRole(aPingHandler->mParentNode->mInstance);
+            if (Role != OT_DEVICE_ROLE_LEADER && Role != OT_DEVICE_ROLE_ROUTER)
+                shouldReply = false;
+        }
+
+        if (shouldReply)
+        {
+#if DEBUG_PING
+            CHAR szIpAddress[46] = { 0 };
+            RtlIpv6AddressToStringA(&aPingHandler->mSourceAddr6.sin6_addr, szIpAddress);
+            printf("%d: received ping (%d bytes) from %s\r\n", aPingHandler->mParentNode->mId, cbTransferred, szIpAddress);
+#endif
+
+            // Send the received data back
+            result = 
+                sendto(
+                    aPingHandler->mSocket, 
+                    aPingHandler->mRecvBuffer, cbTransferred, 0, 
+                    (SOCKADDR*)&aPingHandler->mSourceAddr6, aPingHandler->mSourceAddr6Len
+                    );
+            if (result == SOCKET_ERROR)
+            {
+                printf("sendto failed, 0x%x\r\n", WSAGetLastError());
+            }
+        }
+    }
+    
+    // Start the otpool waiting on the overlapped event
+    SetThreadpoolWait(aPingHandler->mThreadpoolWait, aPingHandler->mOverlapped.hEvent, nullptr);
+
+    // Post another recv
+    dwFlags = MSG_PARTIAL;
+    aPingHandler->mSourceAddr6Len = sizeof(aPingHandler->mSourceAddr6);
+    result = 
+        WSARecvFrom(
+            aPingHandler->mSocket, 
+            &aPingHandler->mWSARecvBuffer, 1, &aPingHandler->mNumBytesReceived, &dwFlags, 
+            (SOCKADDR*)&aPingHandler->mSourceAddr6, &aPingHandler->mSourceAddr6Len, 
+            &aPingHandler->mOverlapped, nullptr
+            );
+    if (result != SOCKET_ERROR)
+    {
+        // Not pending, so manually trigger the event for the Threadpool to execute
+        SetEvent(aPingHandler->mOverlapped.hEvent);
+    }
+    else
+    {
+        result = WSAGetLastError();
+        if (result != WSA_IO_PENDING)
+        {
+            printf("WSARecvFrom failed, 0x%x\r\n", result);
+        }
+    }
+}
+
+bool IsMeshLocalEID(otNode *aNode, const otIp6Address *aAddress)
+{
+    auto ML_EID = otThreadGetMeshLocalEid(aNode->mInstance);
+    if (ML_EID == nullptr) return false;
+    bool result = memcmp(ML_EID->mFields.m8, aAddress->mFields.m8, sizeof(otIp6Address)) == 0;
+    otFreeMemory(ML_EID);
+    return result;
+}
+
+void AddPingHandler(otNode *aNode, const otIp6Address *aAddress)
+{
+    otPingHandler *aPingHandler = new otPingHandler();
+    aPingHandler->mParentNode = aNode;
+    aPingHandler->mAddress = *aAddress;
+    aPingHandler->mSocket = INVALID_SOCKET;
+    aPingHandler->mOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    aPingHandler->mWSARecvBuffer = { 1500, aPingHandler->mRecvBuffer };
+    aPingHandler->mActive = true;
+    aPingHandler->mThreadpoolWait = 
+        CreateThreadpoolWait(
+            PingHandlerRecvCallback,
+            aPingHandler,
+            nullptr
+            );
+    
+    SOCKADDR_IN6 addr6 = { 0 };
+    addr6.sin6_family = AF_INET6;
+    addr6.sin6_port = CertificationPingPort;
+    memcpy(&addr6.sin6_addr, aAddress, sizeof(IN6_ADDR));
+    
+#if DEBUG_PING
+    CHAR szIpAddress[46] = { 0 };
+    RtlIpv6AddressToStringA(&addr6.sin6_addr, szIpAddress);
+
+    printf("%d: starting ping handler for %s\r\n", aNode->mId, szIpAddress);
+#endif
+
+    // Put the current thead in the correct compartment
+    bool RevertCompartmentOnExit = false;
+    ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
+    if (OriginalCompartmentID != otGetCompartmentId(aNode->mInstance))
+    {
+        DWORD dwError = ERROR_SUCCESS;
+        if ((dwError = SetCurrentThreadCompartmentId(otGetCompartmentId(aNode->mInstance))) != ERROR_SUCCESS)
+        {
+            printf("SetCurrentThreadCompartmentId failed, 0x%x\r\n", dwError);
+        }
+        RevertCompartmentOnExit = true;
+    }
+
+    int result;
+    DWORD Flag = FALSE;
+    IPV6_MREQ MCReg;
+    MCReg.ipv6mr_interface = otGetDeviceIfIndex(aNode->mInstance);
+
+    if (aPingHandler->mOverlapped.hEvent == nullptr ||
+        aPingHandler->mThreadpoolWait == nullptr)
+    {
+        goto exit;
+    }
+    
+    // Create the socket
+    aPingHandler->mSocket = WSASocketW(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
+    if (aPingHandler->mSocket == INVALID_SOCKET)
+    {
+        printf("WSASocket failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+
+    // Bind the socket to the address
+    result = bind(aPingHandler->mSocket, (sockaddr*)&addr6, sizeof(addr6));
+    if (result == SOCKET_ERROR)
+    {
+        printf("bind failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+    
+    // Block our own sends from getting called as receives
+    result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&Flag, sizeof(Flag));
+    if (result == SOCKET_ERROR)
+    {
+        printf("setsockopt (IPV6_MULTICAST_LOOP) failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+
+    // Bind to the multicast addresses
+    if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr))
+    {
+        // All nodes address
+        MCReg.ipv6mr_multiaddr = LinkLocalAllNodesAddress;
+        result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
+        if (result == SOCKET_ERROR)
+        {
+            printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+
+        // All routers address
+        MCReg.ipv6mr_multiaddr = LinkLocalAllRoutersAddress;
+        result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
+        if (result == SOCKET_ERROR)
+        {
+            printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+    }
+    else if (IsMeshLocalEID(aNode, aAddress))
+    {
+        // All nodes address
+        MCReg.ipv6mr_multiaddr = RealmLocalAllNodesAddress;
+        result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
+        if (result == SOCKET_ERROR)
+        {
+            printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+        
+        // All routers address
+        MCReg.ipv6mr_multiaddr = RealmLocalAllRoutersAddress;
+        result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
+        if (result == SOCKET_ERROR)
+        {
+            printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+        
+        // Special realm local address
+        MCReg.ipv6mr_multiaddr = RealmLocalSpecialAddress;
+        result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
+        if (result == SOCKET_ERROR)
+        {
+            printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+    }
+    
+    // Start the otpool waiting on the overlapped event
+    SetThreadpoolWait(aPingHandler->mThreadpoolWait, aPingHandler->mOverlapped.hEvent, nullptr);
+
+    // Start the receive
+    Flag = MSG_PARTIAL;
+    aPingHandler->mSourceAddr6Len = sizeof(aPingHandler->mSourceAddr6);
+    result = 
+        WSARecvFrom(
+            aPingHandler->mSocket, 
+            &aPingHandler->mWSARecvBuffer, 1, &aPingHandler->mNumBytesReceived, &Flag, 
+            (SOCKADDR*)&aPingHandler->mSourceAddr6, &aPingHandler->mSourceAddr6Len, 
+            &aPingHandler->mOverlapped, nullptr
+            );
+    if (result != SOCKET_ERROR)
+    {
+        // Not pending, so manually trigger the event for the Threadpool to execute
+        SetEvent(aPingHandler->mOverlapped.hEvent);
+    }
+    else
+    {
+        result = WSAGetLastError();
+        if (result != WSA_IO_PENDING)
+        {
+            printf("WSARecvFrom failed, 0x%x\r\n", result);
+            goto exit;
+        }
+    }
+
+    aNode->mPingHandlers.push_back(aPingHandler);
+    aPingHandler = nullptr;
+
+exit:
+    
+    // Revert the comparment if necessary
+    if (RevertCompartmentOnExit)
+    {
+        (VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
+    }
+
+    // Clean up ping handler if necessary
+    if (aPingHandler)
+    {
+        if (aPingHandler->mThreadpoolWait != nullptr)
+        {
+            if (aPingHandler->mSocket != INVALID_SOCKET) 
+                closesocket(aPingHandler->mSocket);
+            WaitForThreadpoolWaitCallbacks(aPingHandler->mThreadpoolWait, TRUE);
+            CloseThreadpoolWait(aPingHandler->mThreadpoolWait);
+        }
+        if (aPingHandler->mOverlapped.hEvent)
+        {
+            CloseHandle(aPingHandler->mOverlapped.hEvent);
+        }
+        delete aPingHandler;
+    }
+}
+
+void HandleAddressChanges(otNode *aNode)
+{
+    otLogFuncEntry();
+    auto addrs = otIp6GetUnicastAddresses(aNode->mInstance);
+
+    EnterCriticalSection(&aNode->mCS);
+        
+    // Invalidate all handlers
+    for (ULONG i = 0; i < aNode->mPingHandlers.size(); i++)
+        aNode->mPingHandlers[i]->mActive = false;
+
+    // Search for matches
+    for (auto addr = addrs; addr; addr = addr->mNext)
+    {
+        bool found = false;
+        for (ULONG i = 0; i < aNode->mPingHandlers.size(); i++)
+            if (!aNode->mPingHandlers[i]->mActive &&
+                memcmp(&addr->mAddress, &aNode->mPingHandlers[i]->mAddress, sizeof(otIp6Address)) == 0)
+            {
+                found = true;
+                aNode->mPingHandlers[i]->mActive = true;
+                break;
+            }
+        if (!found) AddPingHandler(aNode, &addr->mAddress);
+    }
+
+    vector<otPingHandler*> pingHandlersToDelete;
+        
+    // Release all left over handlers
+    for (int i = aNode->mPingHandlers.size() - 1; i >= 0; i--)
+        if (aNode->mPingHandlers[i]->mActive == false)
+        {
+            auto aPingHandler = aNode->mPingHandlers[i];
+
+#if DEBUG_PING
+            CHAR szIpAddress[46] = { 0 };
+            RtlIpv6AddressToStringA((PIN6_ADDR)&aPingHandler->mAddress, szIpAddress);
+            printf("%d: removing ping handler for %s\r\n", aNode->mId, szIpAddress);
+#endif
+
+            aNode->mPingHandlers.erase(aNode->mPingHandlers.begin() + i);
+                
+            shutdown(aPingHandler->mSocket, SD_BOTH);
+            closesocket(aPingHandler->mSocket);
+            
+            pingHandlersToDelete.push_back(aPingHandler);
+        }
+
+    LeaveCriticalSection(&aNode->mCS);
+
+    for each (auto aPingHandler in pingHandlersToDelete)
+    {
+        WaitForThreadpoolWaitCallbacks(aPingHandler->mThreadpoolWait, TRUE);
+        CloseThreadpoolWait(aPingHandler->mThreadpoolWait);
+        CloseHandle(aPingHandler->mOverlapped.hEvent);
+
+        delete aPingHandler;
+    }
+
+    if (addrs) otFreeMemory(addrs);
+
+    otLogFuncExit();
+}
+
+void OTCALL otNodeStateChangedCallback(uint32_t aFlags, void *aContext)
+{
+    otLogFuncEntry();
+    otNode* aNode = (otNode*)aContext;
+
+    if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
+    {
+        auto Role = otThreadGetDeviceRole(aNode->mInstance);
+        printf("%d: new role: %s\r\n", aNode->mId, otDeviceRoleToString(Role));
+    }
+
+    if ((aFlags & OT_CHANGED_IP6_ADDRESS_ADDED) != 0 || (aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) != 0 ||
+        (aFlags & OT_CHANGED_THREAD_RLOC_ADDED) != 0 || (aFlags & OT_CHANGED_THREAD_RLOC_REMOVED) != 0)
+    {
+        HandleAddressChanges(aNode);
+    }
+    otLogFuncExit();
+}
+
+OTNODEAPI int32_t OTCALL otNodeLog(const char *aMessage)
+{
+    LogInfo(OT_API, "%s", aMessage);
+    return 0;
+}
+
+OTNODEAPI otNode* OTCALL otNodeInit(uint32_t id)
+{
+    otLogFuncEntry();
+
+    auto ApiInstance = GetApiInstance();
+    if (ApiInstance == nullptr)
+    {
+        printf("GetApiInstance failed!\r\n");
+        otLogFuncExitMsg("GetApiInstance failed");
+        return nullptr;
+    }
+
+    bool BusAdded = false;
+    DWORD newBusIndex;
+    NET_IFINDEX ifIndex = {};
+    NET_LUID ifLuid = {};
+    GUID ifGuid = {};
+    otNode *node = nullptr;
+    
+    DWORD dwError;
+    DWORD tries = 0;
+    while (tries < 1000)
+    {
+        newBusIndex = (gNextBusNumber + tries) % 1000;
+        if (newBusIndex == 0) newBusIndex++;
+
+        dwError = otvmpAddVirtualBus(gVmpHandle, &newBusIndex, &ifIndex);
+        if (dwError == ERROR_SUCCESS)
+        {
+            BusAdded = true;
+            gNextBusNumber = newBusIndex + 1;
+            break;
+        }
+        else if (dwError == ERROR_INVALID_PARAMETER || dwError == ERROR_FILE_NOT_FOUND)
+        {
+            tries++;
+        }
+        else
+        {
+            printf("otvmpAddVirtualBus failed, 0x%x!\r\n", dwError);
+            otLogFuncExitMsg("otvmpAddVirtualBus failed");
+            goto error;
+        }
+    }
+
+    if (tries == 1000)
+    {
+        printf("otvmpAddVirtualBus failed to find an empty bus!\r\n");
+        otLogFuncExitMsg("otvmpAddVirtualBus failed to find an empty bus");
+        goto error;
+    }
+
+    if ((dwError = otvmpSetAdapterTopologyGuid(gVmpHandle, newBusIndex, &gTopologyGuid)) != ERROR_SUCCESS)
+    {
+        printf("otvmpSetAdapterTopologyGuid failed, 0x%x!\r\n", dwError);
+        otLogFuncExitMsg("otvmpSetAdapterTopologyGuid failed");
+        goto error;
+    }
+
+    if (ERROR_SUCCESS != ConvertInterfaceIndexToLuid(ifIndex, &ifLuid))
+    {
+        printf("ConvertInterfaceIndexToLuid(%u) failed!\r\n", ifIndex);
+        otLogFuncExitMsg("ConvertInterfaceIndexToLuid failed");
+        goto error;
+    }
+
+    if (ERROR_SUCCESS != ConvertInterfaceLuidToGuid(&ifLuid, &ifGuid))
+    {
+        printf("ConvertInterfaceLuidToGuid failed!\r\n");
+        otLogFuncExitMsg("ConvertInterfaceLuidToGuid failed");
+        goto error;
+    }
+
+    // Keep trying for up to 30 seconds
+    auto StartTick = GetTickCount64();
+    otInstance *instance = nullptr;
+    do
+    {
+        instance = otInstanceInit(ApiInstance, &ifGuid);
+        if (instance != nullptr) break;
+
+        auto waitTimeMs = (30 * 1000 - (LONGLONG)(GetTickCount64() - StartTick));
+        if (waitTimeMs <= 0) break;
+        auto waitResult = WaitForSingleObject(gDeviceArrivalEvent, (DWORD)waitTimeMs);
+        if (waitResult != WAIT_OBJECT_0) break;
+
+    } while (true);
+
+    if (instance == nullptr)
+    {
+        printf("otInstanceInit failed!\r\n");
+        otLogFuncExitMsg("otInstanceInit failed");
+        goto error;
+    }
+
+    GUID DeviceGuid = otGetDeviceGuid(instance);
+    uint32_t Compartment = otGetCompartmentId(instance);
+
+    node = new otNode();
+    printf("%d: New Device " GUID_FORMAT " in compartment %d\r\n", id, GUID_ARG(DeviceGuid), Compartment);
+
+    node->mId = id;
+    node->mInterfaceIndex = ifIndex;
+    node->mBusIndex = newBusIndex;
+    node->mInstance = instance;
+
+    node->mEnergyScanEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+    node->mPanIdConflictEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+    EnterCriticalSection(&gCS);
+    gNodes.push_back(node);
+    LeaveCriticalSection(&gCS);
+
+    InitializeCriticalSection(&node->mCS);
+
+    // Reset any previously saved settings
+    otInstanceFactoryReset(instance);
+
+    otSetStateChangedCallback(instance, otNodeStateChangedCallback, node);
+
+    HandleAddressChanges(node);
+
+    otLogFuncExitMsg("success. [%d] = %!GUID!", id, &DeviceGuid);
+
+error:
+
+    if (node == nullptr)
+    {
+        if (BusAdded)
+        {
+            otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
+        }
+
+        ReleaseApiInstance();
+    }
+
+    return node;
+}
+
+OTNODEAPI int32_t OTCALL otNodeFinalize(otNode* aNode)
+{
+    otLogFuncEntry();
+    if (aNode != nullptr)
+    {
+        printf("%d: Removing Device\r\n", aNode->mId);
+
+        // Free any memory that we allocated now
+        for each (auto mem in aNode->mMemoryToFree)
+            free(mem);
+
+        // Clean up callbacks
+        CloseHandle(aNode->mPanIdConflictEvent);
+        CloseHandle(aNode->mEnergyScanEvent);
+        otSetStateChangedCallback(aNode->mInstance, nullptr, nullptr);
+
+        EnterCriticalSection(&gCS);
+        for (uint32_t i = 0; i < gNodes.size(); i++)
+        {
+            if (gNodes[i] == aNode)
+            {
+                gNodes.erase(gNodes.begin() + i);
+                break;
+            }
+        }
+        LeaveCriticalSection(&gCS);
+
+        // Free the instance
+        otFreeMemory(aNode->mInstance);
+        aNode->mInstance = nullptr;
+
+        // Free the ping handlers
+        HandleAddressChanges(aNode);
+        assert(aNode->mPingHandlers.size() == 0);
+        if (aNode->mPingHandlers.size() != 0) printf("%d left over ping handlers!!!", (int)aNode->mPingHandlers.size());
+        
+        DeleteCriticalSection(&aNode->mCS);
+
+        // Delete the virtual bus
+        otvmpRemoveVirtualBus(gVmpHandle, aNode->mBusIndex);
+        delete aNode;
+        
+        ReleaseApiInstance();
+    }
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetMode(otNode* aNode, const char *aMode)
+{
+    otLogFuncEntryMsg("[%d] %s", aNode->mId, aMode);
+    printf("%d: mode %s\r\n", aNode->mId, aMode);
+
+    otLinkModeConfig linkMode = {0};
+
+    const char *index = aMode;
+    while (*index)
+    {
+        switch (*index)
+        {
+        case 'r':
+            linkMode.mRxOnWhenIdle = true;
+            break;
+        case 's':
+            linkMode.mSecureDataRequests = true;
+            break;
+        case 'd':
+            linkMode.mDeviceType = true;
+            break;
+        case 'n':
+            linkMode.mNetworkData = true;
+            break;
+        }
+
+        index++;
+    }
+
+    auto result = otThreadSetLinkMode(aNode->mInstance, linkMode);
+    
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeInterfaceUp(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: ifconfig up\r\n", aNode->mId);
+
+    auto error = otIp6SetEnabled(aNode->mInstance, true);
+    
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: ifconfig down\r\n", aNode->mId);
+
+    (void)otIp6SetEnabled(aNode->mInstance, false);
+    
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: thread start\r\n", aNode->mId);
+
+    auto error = otThreadSetEnabled(aNode->mInstance, true);
+    
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: thread stop\r\n", aNode->mId);
+
+    (void)otThreadSetEnabled(aNode->mInstance, false);
+    
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeCommissionerStart(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: commissioner start\r\n", aNode->mId);
+
+    auto error = otCommissionerStart(aNode->mInstance);
+    
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeCommissionerJoinerAdd(otNode* aNode, const char *aExtAddr, const char *aPSKd)
+{
+    otLogFuncEntryMsg("[%d] %s %s", aNode->mId, aExtAddr, aPSKd);
+    printf("%d: commissioner joiner add %s %s\r\n", aNode->mId, aExtAddr, aPSKd);
+
+    const uint32_t kDefaultJoinerTimeout = 120;
+
+    otError error;
+    
+    if (strcmp(aExtAddr, "*") == 0)
+    {
+        error = otCommissionerAddJoiner(aNode->mInstance, nullptr, aPSKd, kDefaultJoinerTimeout);
+    }
+    else
+    {
+        otExtAddress extAddr;
+        if (Hex2Bin(aExtAddr, extAddr.m8, sizeof(extAddr)) != sizeof(extAddr))
+            return OT_ERROR_PARSE;
+
+        error = otCommissionerAddJoiner(aNode->mInstance, &extAddr, aPSKd, kDefaultJoinerTimeout);
+    }
+    
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeCommissionerStop(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: commissioner stop\r\n", aNode->mId);
+
+    (void)otCommissionerStop(aNode->mInstance);
+    
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeJoinerStart(otNode* aNode, const char *aPSKd, const char *aProvisioningUrl)
+{
+    otLogFuncEntryMsg("[%d] %s %s", aNode->mId, aPSKd, aProvisioningUrl);
+    printf("%d: joiner start %s %s\r\n", aNode->mId, aPSKd, aProvisioningUrl);
+
+    // TODO: handle the joiner completion callback
+    auto error = otJoinerStart(aNode->mInstance, aPSKd, aProvisioningUrl, NULL, NULL, NULL, NULL, NULL, NULL);
+    
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeJoinerStop(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: joiner stop\r\n", aNode->mId);
+
+    (void)otJoinerStop(aNode->mInstance);
+    
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeClearWhitelist(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: whitelist clear\r\n", aNode->mId);
+
+    otLinkClearWhitelist(aNode->mInstance);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: whitelist enable\r\n", aNode->mId);
+
+    otLinkSetWhitelistEnabled(aNode->mInstance, true);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: whitelist disable\r\n", aNode->mId);
+
+    otLinkSetWhitelistEnabled(aNode->mInstance, false);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeAddWhitelist(otNode* aNode, const char *aExtAddr, int8_t aRssi)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    if (aRssi == 0)
+        printf("%d: whitelist add %s\r\n", aNode->mId, aExtAddr);
+    else printf("%d: whitelist add %s %d\r\n", aNode->mId, aExtAddr, aRssi);
+
+    uint8_t extAddr[8];
+    if (Hex2Bin(aExtAddr, extAddr, sizeof(extAddr)) != sizeof(extAddr))
+        return OT_ERROR_PARSE;
+
+    otError error;
+    if (aRssi == 0)
+    {
+        error = otLinkAddWhitelist(aNode->mInstance, extAddr);
+    }
+    else
+    {
+        error = otLinkAddWhitelistRssi(aNode->mInstance, extAddr, aRssi);
+    }
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeRemoveWhitelist(otNode* aNode, const char *aExtAddr)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: whitelist remove %s\r\n", aNode->mId, aExtAddr);
+
+    uint8_t extAddr[8];
+    if (Hex2Bin(aExtAddr, extAddr, sizeof(extAddr)) != sizeof(extAddr))
+        return OT_ERROR_INVALID_ARGS;
+
+    otLinkRemoveWhitelist(aNode->mInstance, extAddr);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetRloc16(aNode->mInstance);
+    printf("%d: rloc16\r\n%04x\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetHashMacAddress(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    otExtAddress aHashMacAddress = {};
+    otLinkGetJoinerId(aNode->mInstance, &aHashMacAddress);
+    char* str = (char*)malloc(18);
+    if (str != nullptr)
+    {
+        aNode->mMemoryToFree.push_back(str);
+        for (int i = 0; i < 8; i++)
+            sprintf_s(str + i * 2, 18 - (2 * i), "%02x", aHashMacAddress.m8[i]);
+        printf("%d: hashmacaddr\r\n%s\r\n", aNode->mId, str);
+    }
+    otLogFuncExit();
+    return str;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetAddr64(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto extAddr = otLinkGetExtendedAddress(aNode->mInstance);
+    char* str = (char*)malloc(18);
+    if (str != nullptr)
+    {
+        aNode->mMemoryToFree.push_back(str);
+        for (int i = 0; i < 8; i++)
+            sprintf_s(str + i * 2, 18 - (2 * i), "%02x", extAddr[i]);
+        printf("%d: extaddr\r\n%s\r\n", aNode->mId, str);
+    }
+    otFreeMemory(extAddr);
+    otLogFuncExit();
+    return str;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetChannel(otNode* aNode, uint8_t aChannel)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: channel %d\r\n", aNode->mId, aChannel);
+    auto result = otLinkSetChannel(aNode->mInstance, aChannel);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otLinkGetChannel(aNode->mInstance);
+    printf("%d: channel\r\n%d\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetMasterkey(otNode* aNode, const char *aMasterkey)
+{
+    otLogFuncEntryMsg("[%d] %s", aNode->mId, aMasterkey);
+    printf("%d: masterkey %s\r\n", aNode->mId, aMasterkey);
+
+    int keyLength;
+    otMasterKey key;
+    if ((keyLength = Hex2Bin(aMasterkey, key.m8, sizeof(key.m8))) != OT_MASTER_KEY_SIZE)
+    {
+        printf("invalid length key %d\r\n", keyLength);
+        return OT_ERROR_PARSE;
+    }
+
+    auto error = otThreadSetMasterKey(aNode->mInstance, &key);
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto aMasterKey = otThreadGetMasterKey(aNode->mInstance);
+    uint8_t strLength = 2*sizeof(otMasterKey) + 1;
+    char* str = (char*)malloc(strLength);
+    if (str != nullptr)
+    {
+        aNode->mMemoryToFree.push_back(str);
+        for (int i = 0; i < sizeof(otMasterKey); i++)
+            sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aMasterKey->m8[i]);
+        printf("%d: masterkey\r\n%s\r\n", aNode->mId, str);
+    }
+    otFreeMemory(aMasterKey);
+    otLogFuncExit();
+    return str;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetPSKc(otNode* aNode, const char *aPSKc)
+{
+    otLogFuncEntryMsg("[%d] %s", aNode->mId, aPSKc);
+    printf("%d: pskc %s\r\n", aNode->mId, aPSKc);
+
+    uint8_t pskc[OT_PSKC_MAX_SIZE];
+    if (Hex2Bin(aPSKc, pskc, sizeof(pskc)) != OT_PSKC_MAX_SIZE)
+    {
+        printf("invalid pskc %s\r\n", aPSKc);
+        return OT_ERROR_PARSE;
+    }
+
+    auto error = otThreadSetPSKc(aNode->mInstance, pskc);
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetPSKc(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto aPSKc = otThreadGetPSKc(aNode->mInstance);
+    uint8_t strLength = 2 * OT_PSKC_MAX_SIZE + 1;
+    char* str = (char*)malloc(strLength);
+    if (str != nullptr)
+    {
+        aNode->mMemoryToFree.push_back(str);
+        for (int i = 0; i < OT_PSKC_MAX_SIZE; i++)
+            sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aPSKc[i]);
+        printf("%d: pskc\r\n%s\r\n", aNode->mId, str);
+    }
+    otFreeMemory(aPSKc);
+    otLogFuncExit();
+    return str;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetKeySequenceCounter(aNode->mInstance);
+    printf("%d: keysequence\r\n%d\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetKeySequenceCounter(otNode* aNode, uint32_t aSequence)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: keysequence counter %d\r\n", aNode->mId, aSequence);
+    otThreadSetKeySequenceCounter(aNode->mInstance, aSequence);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetKeySwitchGuardTime(otNode* aNode, uint32_t aKeySwitchGuardTime)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: keysequence guardtime %d\r\n", aNode->mId, aKeySwitchGuardTime);
+    otThreadSetKeySwitchGuardTime(aNode->mInstance, aKeySwitchGuardTime);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetNetworkIdTimeout(otNode* aNode, uint8_t aTimeout)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: networkidtimeout %d\r\n", aNode->mId, aTimeout);
+    otThreadSetNetworkIdTimeout(aNode->mInstance, aTimeout);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetNetworkName(otNode* aNode, const char *aName)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: networkname %s\r\n", aNode->mId, aName);
+    auto result = otThreadSetNetworkName(aNode->mInstance, aName);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetNetworkName(aNode->mInstance);
+    aNode->mMemoryToFree.push_back((char*)result);
+    printf("%d: networkname\r\n%s\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI uint16_t OTCALL otNodeGetPanId(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otLinkGetPanId(aNode->mInstance);
+    printf("%d: panid\r\n0x%04x\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetPanId(otNode* aNode, uint16_t aPanId)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: panid 0x%04x\r\n", aNode->mId, aPanId);
+    auto result = otLinkSetPanId(aNode->mInstance, aPanId);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetLocalLeaderPartitionId(aNode->mInstance);
+    printf("%d: leaderpartitionid\r\n0x%04x\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetPartitionId(otNode* aNode, uint32_t aPartitionId)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: leaderpartitionid 0x%04x\r\n", aNode->mId, aPartitionId);
+    otThreadSetLocalLeaderPartitionId(aNode->mInstance, aPartitionId);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetRouterUpgradeThreshold(otNode* aNode, uint8_t aThreshold)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: routerupgradethreshold %d\r\n", aNode->mId, aThreshold);
+    otThreadSetRouterUpgradeThreshold(aNode->mInstance, aThreshold);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetRouterDowngradeThreshold(otNode* aNode, uint8_t aThreshold)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: routerdowngradethreshold %d\r\n", aNode->mId, aThreshold);
+    otThreadSetRouterDowngradeThreshold(aNode->mInstance, aThreshold);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeReleaseRouterId(otNode* aNode, uint8_t aRouterId)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: releaserouterid %d\r\n", aNode->mId, aRouterId);
+    auto result = otThreadReleaseRouterId(aNode->mInstance, aRouterId);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto role = otThreadGetDeviceRole(aNode->mInstance);
+    auto result = _strdup(otDeviceRoleToString(role));
+    aNode->mMemoryToFree.push_back(result);
+    printf("%d: state\r\n%s\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetState(otNode* aNode, const char *aState)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: state %s\r\n", aNode->mId, aState);
+
+    otError error;
+    if (strcmp(aState, "detached") == 0)
+    {
+        error = otThreadBecomeDetached(aNode->mInstance);
+    }
+    else if (strcmp(aState, "child") == 0)
+    {
+        error = otThreadBecomeChild(aNode->mInstance);
+    }
+    else if (strcmp(aState, "router") == 0)
+    {
+        error = otThreadBecomeRouter(aNode->mInstance);
+    }
+    else if (strcmp(aState, "leader") == 0)
+    {
+        error = otThreadBecomeLeader(aNode->mInstance);
+    }
+    else
+    {
+        error = OT_ERROR_INVALID_ARGS;
+    }
+    otLogFuncExit();
+    return error;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetChildTimeout(aNode->mInstance);
+    printf("%d: childtimeout\r\n%d\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetTimeout(otNode* aNode, uint32_t aTimeout)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: childtimeout %d\r\n", aNode->mId, aTimeout);
+    otThreadSetChildTimeout(aNode->mInstance, aTimeout);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetLeaderWeight(aNode->mInstance);
+    printf("%d: leaderweight\r\n%d\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetWeight(otNode* aNode, uint8_t aWeight)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: leaderweight %d\r\n", aNode->mId, aWeight);
+    otThreadSetLocalLeaderWeight(aNode->mInstance, aWeight);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeAddIpAddr(otNode* aNode, const char *aAddr)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: add ipaddr %s\r\n", aNode->mId, aAddr);
+
+    otNetifAddress aAddress;
+    auto error = otIp6AddressFromString(aAddr, &aAddress.mAddress);
+    if (error != OT_ERROR_NONE) return error;
+
+    aAddress.mPrefixLength = 64;
+    aAddress.mPreferred = true;
+    aAddress.mValid = true;
+    auto result = otIp6AddUnicastAddress(aNode->mInstance, &aAddress);
+    otLogFuncExit();
+    return result;
+}
+
+inline uint16_t Swap16(uint16_t v)
+{
+    return
+        (((v & 0x00ffU) << 8) & 0xff00) |
+        (((v & 0xff00U) >> 8) & 0x00ff);
+}
+
+OTNODEAPI const char* OTCALL otNodeGetAddrs(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: ipaddr\r\n", aNode->mId);
+
+    auto addrs = otIp6GetUnicastAddresses(aNode->mInstance);
+    if (addrs == nullptr) return nullptr;
+
+    char* str = (char*)malloc(512);
+    if (str != nullptr)
+    {
+        aNode->mMemoryToFree.push_back(str);
+        RtlZeroMemory(str, 512);
+
+        char* cur = str;
+    
+        for (const otNetifAddress *addr = addrs; addr; addr = addr->mNext)
+        {
+            if (cur != str)
+            {
+                *cur = '\n';
+                cur++;
+            }
+
+            auto last = cur;
+
+            cur += 
+                sprintf_s(
+                    cur, 512 - (cur - str),
+                    "%x:%x:%x:%x:%x:%x:%x:%x",
+                    Swap16(addr->mAddress.mFields.m16[0]),
+                    Swap16(addr->mAddress.mFields.m16[1]),
+                    Swap16(addr->mAddress.mFields.m16[2]),
+                    Swap16(addr->mAddress.mFields.m16[3]),
+                    Swap16(addr->mAddress.mFields.m16[4]),
+                    Swap16(addr->mAddress.mFields.m16[5]),
+                    Swap16(addr->mAddress.mFields.m16[6]),
+                    Swap16(addr->mAddress.mFields.m16[7]));
+
+            printf("%s\r\n", last);
+        }
+    }
+
+    otFreeMemory(addrs);
+    otLogFuncExit();
+
+    return str;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetContextReuseDelay(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    auto result = otThreadGetContextIdReuseDelay(aNode->mInstance);
+    printf("%d: contextreusedelay\r\n%d\r\n", aNode->mId, result);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetContextReuseDelay(otNode* aNode, uint32_t aDelay)
+{
+    otLogFuncEntryMsg("[%d] %d", aNode->mId, aDelay);
+    printf("%d: contextreusedelay %d\r\n", aNode->mId, aDelay);
+    otThreadSetContextIdReuseDelay(aNode->mInstance, aDelay);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeAddPrefix(otNode* aNode, const char *aPrefix, const char *aFlags, const char *aPreference)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: prefix add %s %s %s\r\n", aNode->mId, aPrefix, aFlags, aPreference);
+
+    otBorderRouterConfig config = {0};
+
+    auto error = otNodeParsePrefix(aPrefix, &config.mPrefix);
+    if (error != OT_ERROR_NONE) return error;
+    
+    const char *index = aFlags;
+    while (*index)
+    {
+        switch (*index)
+        {
+        case 'p':
+            config.mPreferred = true;
+            break;
+        case 'a':
+            config.mSlaac = true;
+            break;
+        case 'd':
+            config.mDhcp = true;
+            break;
+        case 'c':
+            config.mConfigure = true;
+            break;
+        case 'r':
+            config.mDefaultRoute = true;
+            break;
+        case 'o':
+            config.mOnMesh = true;
+            break;
+        case 's':
+            config.mStable = true;
+            break;
+        default:
+            return OT_ERROR_INVALID_ARGS;
+        }
+
+        index++;
+    }
+    
+    if (strcmp(aPreference, "high") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
+    }
+    else if (strcmp(aPreference, "med") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_MED;
+    }
+    else if (strcmp(aPreference, "low") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_LOW;
+    }
+    else
+    {
+        return OT_ERROR_INVALID_ARGS;
+    }
+
+    auto result = otBorderRouterAddOnMeshPrefix(aNode->mInstance, &config);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeRemovePrefix(otNode* aNode, const char *aPrefix)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+
+    otIp6Prefix prefix;
+    auto error = otNodeParsePrefix(aPrefix, &prefix);
+    if (error != OT_ERROR_NONE) return error;
+
+    auto result = otBorderRouterRemoveOnMeshPrefix(aNode->mInstance, &prefix);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeAddRoute(otNode* aNode, const char *aPrefix, const char *aPreference)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    otExternalRouteConfig config = {0};
+
+    auto error = otNodeParsePrefix(aPrefix, &config.mPrefix);
+    if (error != OT_ERROR_NONE) return error;
+    
+    if (strcmp(aPreference, "high") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
+    }
+    else if (strcmp(aPreference, "med") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_MED;
+    }
+    else if (strcmp(aPreference, "low") == 0)
+    {
+        config.mPreference = OT_ROUTE_PREFERENCE_LOW;
+    }
+    else
+    {
+        return OT_ERROR_INVALID_ARGS;
+    }
+
+    auto result = otBorderRouterAddRoute(aNode->mInstance, &config);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeRemoveRoute(otNode* aNode, const char *aPrefix)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+
+    otIp6Prefix prefix;
+    auto error = otNodeParsePrefix(aPrefix, &prefix);
+    if (error != OT_ERROR_NONE) return error;
+
+    auto result = otBorderRouterRemoveRoute(aNode->mInstance, &prefix);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeRegisterNetdata(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: registernetdata\r\n", aNode->mId);
+    auto result = otBorderRouterRegister(aNode->mInstance);
+    otLogFuncExit();
+    return result;
+}
+
+void OTCALL otNodeCommissionerEnergyReportCallback(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext)
+{
+    otNode* aNode = (otNode*)aContext;
+
+    printf("Energy: 0x%08x\r\n", aChannelMask);
+    for (uint8_t i = 0; i < aEnergyListLength; i++)
+        printf("%d ", aEnergyList[i]);
+    printf("\r\n");
+
+    SetEvent(aNode->mEnergyScanEvent);
+}
+
+OTNODEAPI int32_t OTCALL otNodeEnergyScan(otNode* aNode, uint32_t aMask, uint8_t aCount, uint16_t aPeriod, uint16_t aDuration, const char *aAddr)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: energy scan 0x%x %d %d %d %s\r\n", aNode->mId, aMask, aCount, aPeriod, aDuration, aAddr);
+
+    otIp6Address address = {0};
+    auto error = otIp6AddressFromString(aAddr, &address);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("otIp6AddressFromString(%s) failed, 0x%x!\r\n", aAddr, error);
+        return error;
+    }
+    
+    ResetEvent(aNode->mEnergyScanEvent);
+
+    error = otCommissionerEnergyScan(aNode->mInstance, aMask, aCount, aPeriod, aDuration, &address, otNodeCommissionerEnergyReportCallback, aNode);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("otCommissionerEnergyScan failed, 0x%x!\r\n", error);
+        return error;
+    }
+
+    auto result = WaitForSingleObject(aNode->mEnergyScanEvent, 8000) == WAIT_OBJECT_0 ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
+    otLogFuncExit();
+    return result;
+}
+
+void OTCALL otNodeCommissionerPanIdConflictCallback(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
+{
+    otNode* aNode = (otNode*)aContext;
+    printf("Conflict: 0x%04x, 0x%08x\r\n", aPanId, aChannelMask);
+    SetEvent(aNode->mPanIdConflictEvent);
+}
+
+OTNODEAPI int32_t OTCALL otNodePanIdQuery(otNode* aNode, uint16_t aPanId, uint32_t aMask, const char *aAddr)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    printf("%d: panid query 0x%04x 0x%x %s\r\n", aNode->mId, aPanId, aMask, aAddr);
+
+    otIp6Address address = {0};
+    auto error = otIp6AddressFromString(aAddr, &address);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("otIp6AddressFromString(%s) failed, 0x%x!\r\n", aAddr, error);
+        return error;
+    }
+    
+    ResetEvent(aNode->mPanIdConflictEvent);
+
+    error = otCommissionerPanIdQuery(aNode->mInstance, aPanId, aMask, &address, otNodeCommissionerPanIdConflictCallback, aNode);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("otCommissionerPanIdQuery failed, 0x%x!\r\n", error);
+        return error;
+    }
+
+    auto result = WaitForSingleObject(aNode->mPanIdConflictEvent, 8000) == WAIT_OBJECT_0 ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeScan(otNode* aNode)
+{
+    otLogFuncEntryMsg("[%d]", aNode->mId);
+    UNREFERENCED_PARAMETER(aNode);
+    otLogFuncExit();
+    return nullptr;
+}
+
+OTNODEAPI uint32_t OTCALL otNodePing(otNode* aNode, const char *aAddr, uint16_t aSize, uint32_t aMinReplies, uint16_t aTimeout)
+{
+    otLogFuncEntryMsg("[%d] %s (%d bytes)", aNode->mId, aAddr, aSize);
+    printf("%d: ping %s (%d bytes)\r\n", aNode->mId, aAddr, aSize);
+
+    // Convert string to destination address
+    otIp6Address otDestinationAddress = {0};
+    auto error = otIp6AddressFromString(aAddr, &otDestinationAddress);
+    if (error != OT_ERROR_NONE)
+    {
+        printf("otIp6AddressFromString(%s) failed!\r\n", aAddr);
+        return 0;
+    }
+    
+    // Get ML-EID as source address for ping
+    auto otSourceAddress = otThreadGetMeshLocalEid(aNode->mInstance);
+
+    sockaddr_in6 SourceAddress = { AF_INET6, (USHORT)(CertificationPingPort + 1) };
+    sockaddr_in6 DestinationAddress = { AF_INET6, CertificationPingPort };
+
+    memcpy(&SourceAddress.sin6_addr, otSourceAddress, sizeof(IN6_ADDR));
+    memcpy(&DestinationAddress.sin6_addr, &otDestinationAddress, sizeof(IN6_ADDR));
+
+    otFreeMemory(otSourceAddress);
+    otSourceAddress = nullptr;
+    
+    // Put the current thead in the correct compartment
+    bool RevertCompartmentOnExit = false;
+    ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
+    if (OriginalCompartmentID != otGetCompartmentId(aNode->mInstance))
+    {
+        DWORD dwError = ERROR_SUCCESS;
+        if ((dwError = SetCurrentThreadCompartmentId(otGetCompartmentId(aNode->mInstance))) != ERROR_SUCCESS)
+        {
+            printf("SetCurrentThreadCompartmentId failed, 0x%x\r\n", dwError);
+        }
+        RevertCompartmentOnExit = true;
+    }
+
+    int result = 0;
+
+    auto SendBuffer = (PCHAR)malloc(aSize);
+    auto RecvBuffer = (PCHAR)malloc(aSize);
+
+    WSABUF WSARecvBuffer = { aSize, RecvBuffer };
+    
+    WSAOVERLAPPED Overlapped = { 0 };
+    Overlapped.hEvent = WSACreateEvent();
+
+    DWORD numberOfReplies = 0;
+    bool isPending = false;
+    DWORD Flags;
+    DWORD cbReceived;
+    int cbDestinationAddress = sizeof(DestinationAddress);
+    DWORD hopLimit = 64;
+
+    SOCKET Socket = WSASocketW(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
+    if (Socket == INVALID_SOCKET)
+    {
+        printf("WSASocket failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+
+    // Bind the socket to the address
+    result = bind(Socket, (sockaddr*)&SourceAddress, sizeof(SourceAddress));
+    if (result == SOCKET_ERROR)
+    {
+        printf("bind failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+    
+    // Set the multicast hop limit to 64
+    result = setsockopt(Socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hopLimit, sizeof(hopLimit));
+    if (result == SOCKET_ERROR)
+    {
+        printf("setsockopt (IPV6_MULTICAST_HOPS) failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+
+    // Initialize the send buffer pattern.
+    for (uint32_t i = 0; i < aSize; i++)
+        SendBuffer[i] = (char)('a' + (i % 23));
+
+    // Hack to retrieve destination on other end
+    memcpy_s(SendBuffer, aSize, &otDestinationAddress, sizeof(IN6_ADDR));
+
+    // Send the buffer
+    result = sendto(Socket, SendBuffer, aSize, 0, (SOCKADDR*)&DestinationAddress, sizeof(DestinationAddress));
+    if (result == SOCKET_ERROR)
+    {
+        printf("sendto failed, 0x%x\r\n", WSAGetLastError());
+        goto exit;
+    }
+
+    auto StartTick = GetTickCount64();
+    
+    while (numberOfReplies < aMinReplies)
+    {
+        Flags = 0; //MSG_PARTIAL;
+        result = WSARecvFrom(Socket, &WSARecvBuffer, 1, &cbReceived, &Flags, (SOCKADDR*)&DestinationAddress, &cbDestinationAddress, &Overlapped, NULL);
+        if (result == SOCKET_ERROR)
+        {
+            result = WSAGetLastError();
+            if (result == WSA_IO_PENDING)
+            {
+                isPending = true;
+            }
+            else
+            {
+                printf("WSARecvFrom failed, 0x%x\r\n", result);
+                goto exit;
+            }
+        }
+
+        if (isPending)
+        {
+            //printf("waiting for completion event...\r\n");
+            // Wait for the receive to complete
+            ULONGLONG elapsed = (GetTickCount64() - StartTick);
+            result = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, (DWORD)(aTimeout - min(aTimeout, elapsed)), TRUE);
+            if (result == WSA_WAIT_TIMEOUT)
+            {
+                //printf("recv timeout\r\n");
+                goto exit;
+            }
+            else if (result == WSA_WAIT_FAILED)
+            {
+                printf("recv failed\r\n");
+                goto exit;
+            }
+        }
+
+        result = WSAGetOverlappedResult(Socket, &Overlapped, &cbReceived, TRUE, &Flags);
+        if (result == FALSE)
+        {
+            printf("WSAGetOverlappedResult failed, 0x%x\r\n", WSAGetLastError());
+            goto exit;
+        }
+
+        numberOfReplies++;
+    }
+
+exit:
+    
+    // Revert the comparment if necessary
+    if (RevertCompartmentOnExit)
+    {
+        (VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
+    }
+
+    free(RecvBuffer);
+    free(SendBuffer);
+
+    WSACloseEvent(Overlapped.hEvent);
+
+    if (Socket != INVALID_SOCKET) closesocket(Socket);
+
+    otLogFuncExit();
+
+    return numberOfReplies;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetRouterSelectionJitter(otNode* aNode, uint8_t aRouterJitter)
+{
+    otLogFuncEntryMsg("[%d] %d", aNode->mId, aRouterJitter);
+    printf("%d: routerselectionjitter %d\r\n", aNode->mId, aRouterJitter);
+    otThreadSetRouterSelectionJitter(aNode->mInstance, aRouterJitter);
+    otLogFuncExit();
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeCommissionerAnnounceBegin(otNode* aNode, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, const char *aAddr)
+{
+    otLogFuncEntryMsg("[%d] 0x%08x %d %d %s", aNode->mId, aChannelMask, aCount, aPeriod, aAddr);
+    printf("%d: commissioner announce 0x%08x %d %d %s\r\n", aNode->mId, aChannelMask, aCount, aPeriod, aAddr);
+
+    otIp6Address aAddress;
+    auto error = otIp6AddressFromString(aAddr, &aAddress);
+    if (error != OT_ERROR_NONE) return error;
+
+    auto result = otCommissionerAnnounceBegin(aNode->mInstance, aChannelMask, aCount, aPeriod, &aAddress);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetActiveDataset(otNode* aNode, uint64_t aTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aMasterKey)
+{
+    otLogFuncEntryMsg("[%d] 0x%llX %d %d", aNode->mId, aTimestamp, aPanId, aChannel);
+    printf("%d: dataset set active 0x%llX %d %d\r\n", aNode->mId, aTimestamp, aPanId, aChannel);
+
+    otOperationalDataset aDataset = {};
+
+    aDataset.mActiveTimestamp = aTimestamp;
+    aDataset.mIsActiveTimestampSet = true;
+
+    if (aPanId != 0)
+    {
+        aDataset.mPanId = aPanId;
+        aDataset.mIsPanIdSet = true;
+    }
+
+    if (aChannel != 0)
+    {
+        aDataset.mChannel = aChannel;
+        aDataset.mIsChannelSet = true;
+    }
+
+    if (aChannelMask != 0)
+    {
+        aDataset.mChannelMaskPage0 = aChannelMask;
+        aDataset.mIsChannelMaskPage0Set = true;
+    }
+
+    if (aMasterKey != NULL && strlen(aMasterKey) != 0)
+    {
+        int keyLength;
+        if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
+        {
+            printf("invalid length key %d\r\n", keyLength);
+            return OT_ERROR_PARSE;
+        }
+        aDataset.mIsMasterKeySet = true;
+    }
+
+    auto result = otDatasetSetActive(aNode->mInstance, &aDataset);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetPendingDataset(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint16_t aPanId, uint16_t aChannel)
+{
+    otLogFuncEntryMsg("[%d] 0x%llX 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
+    printf("%d: dataset set pending 0x%llX 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
+
+    otOperationalDataset aDataset = {};
+
+    if (aActiveTimestamp != 0)
+    {
+        aDataset.mActiveTimestamp = aActiveTimestamp;
+        aDataset.mIsActiveTimestampSet = true;
+    }
+
+    if (aPendingTimestamp != 0)
+    {
+        aDataset.mPendingTimestamp = aPendingTimestamp;
+        aDataset.mIsPendingTimestampSet = true;
+    }
+
+    if (aPanId != 0)
+    {
+        aDataset.mPanId = aPanId;
+        aDataset.mIsPanIdSet = true;
+    }
+
+    if (aChannel != 0)
+    {
+        aDataset.mChannel = aChannel;
+        aDataset.mIsChannelSet = true;
+    }
+
+    auto result = otDatasetSetPending(aNode->mInstance, &aDataset);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSendPendingSet(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint32_t aDelayTimer, uint16_t aPanId, uint16_t aChannel, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName)
+{
+    otLogFuncEntryMsg("[%d] 0x%llX 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
+    printf("%d: dataset send pending 0x%llX 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
+
+    otOperationalDataset aDataset = {};
+
+    if (aActiveTimestamp != 0)
+    {
+        aDataset.mActiveTimestamp = aActiveTimestamp;
+        aDataset.mIsActiveTimestampSet = true;
+    }
+
+    if (aPendingTimestamp != 0)
+    {
+        aDataset.mPendingTimestamp = aPendingTimestamp;
+        aDataset.mIsPendingTimestampSet = true;
+    }
+
+    if (aDelayTimer != 0)
+    {
+        aDataset.mDelay = aDelayTimer;
+        aDataset.mIsDelaySet = true;
+    }
+
+    if (aPanId != 0)
+    {
+        aDataset.mPanId = aPanId;
+        aDataset.mIsPanIdSet = true;
+    }
+
+    if (aChannel != 0)
+    {
+        aDataset.mChannel = aChannel;
+        aDataset.mIsChannelSet = true;
+    }
+
+    if (aMasterKey != NULL && strlen(aMasterKey) != 0)
+    {
+        int keyLength;
+        if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
+        {
+            printf("invalid length key %d\r\n", keyLength);
+            return OT_ERROR_PARSE;
+        }
+        aDataset.mIsMasterKeySet = true;
+    }
+
+    if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
+    {
+        otIp6Address prefix;
+        auto error = otIp6AddressFromString(aMeshLocal, &prefix);
+        if (error != OT_ERROR_NONE) return error;
+        memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8));
+        aDataset.mIsMeshLocalPrefixSet = true;
+    }
+
+    if (aNetworkName != NULL && strlen(aNetworkName) != 0)
+    {
+        strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName);
+        aDataset.mIsNetworkNameSet = true;
+    }
+
+    auto result = otDatasetSendMgmtPendingSet(aNode->mInstance, &aDataset, nullptr, 0);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSendActiveSet(otNode* aNode, uint64_t aActiveTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aExtPanId, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName, const char *aBinary)
+{
+    otLogFuncEntryMsg("[%d] 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPanId, aChannel);
+    printf("%d: dataset send active 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPanId, aChannel);
+
+    otOperationalDataset aDataset = {};
+    uint8_t tlvs[128];
+    uint8_t tlvsLength = 0;
+
+    if (aActiveTimestamp != 0)
+    {
+        aDataset.mActiveTimestamp = aActiveTimestamp;
+        aDataset.mIsActiveTimestampSet = true;
+    }
+    if (aPanId != 0)
+    {
+        aDataset.mPanId = aPanId;
+        aDataset.mIsPanIdSet = true;
+    }
+
+    if (aChannel != 0)
+    {
+        aDataset.mChannel = aChannel;
+        aDataset.mIsChannelSet = true;
+    }
+
+    if (aChannelMask != 0)
+    {
+        aDataset.mChannelMaskPage0 = aChannelMask;
+        aDataset.mIsChannelMaskPage0Set = true;
+    }
+
+    if (aExtPanId != NULL && strlen(aExtPanId) != 0)
+    {
+        int keyLength;
+        if ((keyLength = Hex2Bin(aExtPanId, aDataset.mExtendedPanId.m8, sizeof(aDataset.mExtendedPanId))) != OT_EXT_PAN_ID_SIZE)
+        {
+            printf("invalid length ext pan id %d\r\n", keyLength);
+            return OT_ERROR_PARSE;
+        }
+        aDataset.mIsExtendedPanIdSet = true;
+    }
+
+    if (aMasterKey != NULL && strlen(aMasterKey) != 0)
+    {
+        int keyLength;
+        if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
+        {
+            printf("invalid length key %d\r\n", keyLength);
+            return OT_ERROR_PARSE;
+        }
+        aDataset.mIsMasterKeySet = true;
+    }
+
+    if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
+    {
+        otIp6Address prefix;
+        auto error = otIp6AddressFromString(aMeshLocal, &prefix);
+        if (error != OT_ERROR_NONE) return error;
+        memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8));
+        aDataset.mIsMeshLocalPrefixSet = true;
+    }
+
+    if (aNetworkName != NULL && strlen(aNetworkName) != 0)
+    {
+        strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName);
+        aDataset.mIsNetworkNameSet = true;
+    }
+
+    if (aBinary != NULL && strlen(aBinary) != 0)
+    {
+        int length;
+        if ((length = Hex2Bin(aBinary,tlvs, sizeof(tlvs))) < 0)
+        {
+            printf("invalid length tlvs %d\r\n", length);
+            return OT_ERROR_PARSE;
+        }
+        tlvsLength = (uint8_t)length;
+    }
+
+    auto result = otDatasetSendMgmtActiveSet(aNode->mInstance, &aDataset, tlvsLength == 0 ? nullptr : tlvs, tlvsLength);
+    otLogFuncExit();
+    return result;
+}
+
+OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildren)
+{
+    otLogFuncEntryMsg("[%d] %d", aNode->mId, aMaxChildren);
+    printf("%d: childmax %d\r\n", aNode->mId, aMaxChildren);
+    auto result = otThreadSetMaxAllowedChildren(aNode->mInstance, aMaxChildren);
+    otLogFuncExit();
+    return result;
+}
+
+typedef struct otMacFrameEntry
+{
+    otMacFrame  Frame;
+    LIST_ENTRY  Link;
+} otMacFrameEntry;
+
+typedef struct otListener
+{
+    HANDLE              mListener;
+    CRITICAL_SECTION    mCS;
+    HANDLE              mStopEvent;
+    HANDLE              mFramesUpdatedEvent;
+    LIST_ENTRY          mFrames; // List of otMacFrameEntry
+} otListener;
+
+void
+otListenerCallback(
+    _In_opt_ PVOID aContext,
+    _In_ ULONG SourceInterfaceIndex,
+    _In_reads_bytes_(FrameLength) PUCHAR FrameBuffer,
+    _In_ UCHAR FrameLength,
+    _In_ UCHAR Channel
+)
+{
+    otListener* aListener = (otListener*)aContext;
+    assert(aListener);
+
+    if (FrameLength)
+    {
+        otMacFrameEntry* entry = new otMacFrameEntry;
+        entry->Frame.buffer[0] = Channel;
+        memcpy_s(entry->Frame.buffer + 1, sizeof(entry->Frame.buffer) - 1, FrameBuffer, FrameLength);
+        entry->Frame.length = FrameLength + 1;
+        entry->Frame.nodeid = (uint32_t)-1;
+
+        // Look up the Node ID from by interface guid
+        EnterCriticalSection(&gCS);
+        for (uint32_t i = 0; i < gNodes.size(); i++)
+        {
+            if (gNodes[i]->mInterfaceIndex == SourceInterfaceIndex)
+            {
+                entry->Frame.nodeid = gNodes[i]->mId;
+                break;
+            }
+        }
+        LeaveCriticalSection(&gCS);
+
+        // Push the frame on the list to process
+        EnterCriticalSection(&aListener->mCS);
+        InsertTailList(&aListener->mFrames, &entry->Link);
+        LeaveCriticalSection(&aListener->mCS);
+
+        // Set event indicating we have a new frame to process
+        SetEvent(aListener->mFramesUpdatedEvent);
+    }
+}
+
+OTNODEAPI otListener* OTCALL otListenerInit(uint32_t /* nodeid */)
+{
+    otLogFuncEntry();
+
+    auto ApiInstance = GetApiInstance();
+    if (ApiInstance == nullptr)
+    {
+        printf("GetApiInstance failed!\r\n");
+        otLogFuncExitMsg("GetApiInstance failed");
+        return nullptr;
+    }
+
+    otListener *listener = new otListener();
+    assert(listener);
+
+    InitializeCriticalSection(&listener->mCS);
+    listener->mStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+    listener->mFramesUpdatedEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    InitializeListHead(&listener->mFrames);
+
+    // Create the listener
+    listener->mListener = otvmpListenerCreate(&gTopologyGuid);
+    if (listener->mListener == nullptr) goto error;
+
+    // Register for callbacks
+    otvmpListenerRegister(listener->mListener, otListenerCallback, listener);
+
+    printf("S: Sniffer started\r\n");
+
+error:
+
+    // Clean up on failure
+    if (listener)
+    {
+        if (listener->mListener == nullptr)
+        {
+            otListenerFinalize(listener);
+        }
+    }
+
+    otLogFuncExit();
+
+    return listener;
+}
+
+OTNODEAPI int32_t OTCALL otListenerFinalize(otListener* aListener)
+{
+    otLogFuncEntry();
+
+    if (aListener != nullptr)
+    {
+        // Set stop event to prevent cancel any pending otListenerRead calls
+        SetEvent(aListener->mStopEvent);
+
+        if (aListener->mListener)
+        {
+            // Unregisters (and waits for callbacks to complete) and cleans up the handle
+            otvmpListenerDestroy(aListener->mListener);
+            aListener->mListener = nullptr;
+
+            // Clean up left over frames
+            PLIST_ENTRY Link = aListener->mFrames.Flink;
+            while (Link != &aListener->mFrames)
+            {
+                otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
+                Link = Link->Flink;
+                delete entry;
+            }
+
+            printf("S: Sniffer stopped\r\n");
+        }
+
+        // Clean up everything else
+        CloseHandle(aListener->mFramesUpdatedEvent);
+        aListener->mFramesUpdatedEvent = nullptr;
+        CloseHandle(aListener->mStopEvent);
+        aListener->mStopEvent = nullptr;
+        DeleteCriticalSection(&aListener->mCS);
+        delete aListener;
+
+        ReleaseApiInstance();
+    }
+
+    otLogFuncExit();
+
+    return 0;
+}
+
+OTNODEAPI int32_t OTCALL otListenerRead(otListener* aListener, otMacFrame *aFrame)
+{
+    do
+    {
+        bool exit = false;
+        
+        EnterCriticalSection(&aListener->mCS);
+
+        // If we have a pending frame, return it now
+        if (!IsListEmpty(&aListener->mFrames))
+        {
+            PLIST_ENTRY Link = RemoveHeadList(&aListener->mFrames);
+            otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
+            *aFrame = entry->Frame;
+            delete entry;
+            exit = true;
+        }
+
+        LeaveCriticalSection(&aListener->mCS);
+        
+        if (exit) break;
+        
+        // Wait for the shutdown or frames updated event
+        auto waitResult = WaitForMultipleObjects(2, &aListener->mStopEvent, FALSE, INFINITE);
+
+        if (waitResult == WAIT_OBJECT_0 + 1) // mFramesUpdatedEvent
+        {
+            continue;
+        }
+        else // mStopEvent
+        {
+            return 1;
+        }
+        
+    } while (true);
+
+    //printf("S: Sniffer read %d bytes from node %d\r\n", aFrame->length, aFrame->nodeid);
+
+    return 0;
+}
diff --git a/examples/drivers/windows/otNodeApi/precomp.h b/examples/drivers/windows/otNodeApi/precomp.h
new file mode 100644
index 0000000..671d5d9
--- /dev/null
+++ b/examples/drivers/windows/otNodeApi/precomp.h
@@ -0,0 +1,113 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <windows.h>
+#include <winnt.h>
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <IPHlpApi.h>
+#include <mstcpip.h>
+#include <rpc.h>
+#include <rpcdce.h>
+#include <assert.h>
+#include <new>
+#include <vector>
+#include <tuple>
+
+using namespace std;
+
+// Define to export necessary functions
+#define OTDLL
+#define OTNODEAPI EXTERN_C __declspec(dllexport)
+
+#include <openthread/openthread.h>
+#include <openthread/border_router.h>
+#include <openthread/dataset_ftd.h>
+#include <openthread/thread_ftd.h>
+#include <openthread/commissioner.h>
+#include <openthread/joiner.h>
+#include <openthread/platform/logging-windows.h>
+#include <otNode.h>
+
+void Unload();
+
+FORCEINLINE
+VOID
+InitializeListHead(
+    _Out_ PLIST_ENTRY ListHead
+    )
+{
+    ListHead->Flink = ListHead->Blink = ListHead;
+}
+
+FORCEINLINE
+PLIST_ENTRY
+RemoveHeadList(
+    _Inout_ PLIST_ENTRY ListHead
+    )
+
+{
+
+    PLIST_ENTRY Entry;
+    PLIST_ENTRY NextEntry;
+    Entry = ListHead->Flink;
+    NextEntry = Entry->Flink;
+    ListHead->Flink = NextEntry;
+    NextEntry->Blink = ListHead;
+    return Entry;
+}
+
+FORCEINLINE
+VOID
+InsertTailList(
+    _Inout_ PLIST_ENTRY ListHead,
+    _Out_ __drv_aliasesMem PLIST_ENTRY Entry
+    )
+{
+
+    PLIST_ENTRY PrevEntry;
+    PrevEntry = ListHead->Blink;
+    Entry->Flink = ListHead;
+    Entry->Blink = PrevEntry;
+    PrevEntry->Flink = Entry;
+    ListHead->Blink = Entry;
+}
+
+_Must_inspect_result_
+BOOLEAN
+CFORCEINLINE
+IsListEmpty(
+    _In_ const LIST_ENTRY * ListHead
+    )
+{
+    return (BOOLEAN)(ListHead->Flink == ListHead);
+}
diff --git a/examples/drivers/windows/ottmp/adapter.cpp b/examples/drivers/windows/ottmp/adapter.cpp
new file mode 100644
index 0000000..5836549
--- /dev/null
+++ b/examples/drivers/windows/ottmp/adapter.cpp
@@ -0,0 +1,547 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This module implements code to manage the NETADAPTER object for the
+ *   network adapter.
+ */
+
+#include "pch.hpp"
+#include "adapter.tmh"
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS
+AdapterInitialize(
+    _In_ NDIS_HANDLE                MiniportAdapterHandle,
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+/*++ 
+Routine Description:
+
+    AdapterInitialize function is called to initialize the Network Adapter
+    at the time of Pnp Add device. 
+ 
+    This routine initializes the context of the adapter object
+ 
+Arguments:
+ 
+    MiniportAdapterHandle - Handle to the NDIS Miniport Adapter object.
+ 
+    AdapterContext - The context associated with the adapter
+
+Return Value:
+
+    NTSTATUS - A failure here will indicate a fatal error in the driver.
+
+--*/
+{
+    NDIS_STATUS Status;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+    
+    do
+    {
+        NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistration = { 0 };
+        NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneral = { 0 };
+        NDIS_PM_CAPABILITIES PmCapabilities = { 0 };
+        
+        //
+        // First, set the registration attributes.
+        //
+        AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
+        AdapterRegistration.Header.Size = sizeof(AdapterRegistration);
+        AdapterRegistration.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2;
+
+        AdapterRegistration.MiniportAdapterContext = AdapterContext;
+        AdapterRegistration.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK | NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM | NDIS_MINIPORT_ATTRIBUTES_NO_PAUSE_ON_SUSPEND;
+        AdapterRegistration.InterfaceType = NdisInterfacePNPBus;
+
+        NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(_OTTMP_ADAPTER_CONTEXT);
+        Status = NdisMSetMiniportAttributes(
+            MiniportAdapterHandle,
+            (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterRegistration);
+
+        if (NDIS_STATUS_SUCCESS != Status)
+        {
+            LogError(DRIVER_DEFAULT, "[%p] NdisSetOptionalHandlers Status %!NDIS_STATUS!", AdapterContext, Status);
+            break;
+        }
+
+        //
+        // Next, set the general attributes.
+        //
+
+        AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
+        AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
+        AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
+
+        //
+        // Specify the medium type that the NIC can support but not
+        // necessarily the medium type that the NIC currently uses.
+        //
+        AdapterGeneral.MediaType = NIC_MEDIUM_TYPE;
+
+        //
+        // Specifiy medium type that the NIC currently uses.
+        //
+        AdapterGeneral.PhysicalMediumType = NdisPhysicalMediumNative802_15_4;
+
+        //
+        // We have to lie about the MTU, so that TCPIP will bind to us.
+        // Specifically, we have to rely that the ThreadLwf will fragment
+        // the packets appropriately.
+        //
+        AdapterGeneral.MtuSize = HW_MAX_FRAME_SIZE;
+        AdapterGeneral.MaxXmitLinkSpeed = NIC_RECV_XMIT_SPEED;
+        AdapterGeneral.XmitLinkSpeed = NIC_RECV_XMIT_SPEED;
+        AdapterGeneral.MaxRcvLinkSpeed = NIC_RECV_XMIT_SPEED;
+        AdapterGeneral.RcvLinkSpeed = NIC_RECV_XMIT_SPEED;
+        AdapterGeneral.MediaConnectState = MediaConnectStateConnected;
+        AdapterGeneral.MediaDuplexState = MediaDuplexStateFull;
+
+        //
+        // The maximum number of bytes the NIC can provide as lookahead data.
+        // If that value is different from the size of the lookahead buffer
+        // supported by bound protocols, NDIS will call MiniportOidRequest to
+        // set the size of the lookahead buffer provided by the miniport driver
+        // to the minimum of the miniport driver and protocol(s) values. If the
+        // driver always indicates up full packets with
+        // NdisMIndicateReceiveNetBufferLists, it should set this value to the
+        // maximum total frame size, which excludes the header.
+        //
+        // Upper-layer drivers examine lookahead data to determine whether a
+        // packet that is associated with the lookahead data is intended for
+        // one or more of their clients. If the underlying driver supports
+        // multipacket receive indications, bound protocols are given full net
+        // packets on every indication. Consequently, this value is identical
+        // to that returned for OID_GEN_RECEIVE_BLOCK_SIZE.
+        //
+        AdapterGeneral.LookaheadSize = HW_MAX_FRAME_SIZE;
+        AdapterGeneral.PowerManagementCapabilities = NULL;
+        AdapterGeneral.MacOptions = NIC_MAC_OPTIONS;
+        AdapterGeneral.SupportedPacketFilters = NIC_SUPPORTED_FILTERS;
+
+        //
+        // The maximum number of multicast addresses the NIC driver can manage.
+        // This list is global for all protocols bound to (or above) the NIC.
+        // Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from
+        // the NIC driver when attempting to set the multicast address list,
+        // even if the number of elements in the given list is less than the
+        // number originally returned for this query.
+        //
+        AdapterGeneral.MaxMulticastListSize = NIC_MAX_MCAST_LIST;
+        AdapterGeneral.MacAddressLength = NIC_MACADDR_SIZE;
+
+        //
+        // Return the MAC address of the NIC burnt in the hardware.
+        //
+        memcpy(AdapterGeneral.PermanentMacAddress, &AdapterContext->ExtendedAddress, sizeof(AdapterContext->ExtendedAddress));
+        memcpy(AdapterGeneral.CurrentMacAddress, &AdapterContext->ExtendedAddress, sizeof(AdapterContext->ExtendedAddress));
+
+        AdapterGeneral.RecvScaleCapabilities = NULL;
+        AdapterGeneral.AccessType = NET_IF_ACCESS_BROADCAST;
+        AdapterGeneral.DirectionType = NET_IF_DIRECTION_SENDRECEIVE;
+        AdapterGeneral.ConnectionType = NET_IF_CONNECTION_DEDICATED;
+        AdapterGeneral.IfType = IF_TYPE_IEEE802154;
+        AdapterGeneral.IfConnectorPresent = TRUE;
+        AdapterGeneral.SupportedStatistics = NIC_SUPPORTED_STATISTICS;
+        AdapterGeneral.SupportedPauseFunctions = NdisPauseFunctionsUnsupported;
+        AdapterGeneral.DataBackFillSize = 0;
+        AdapterGeneral.ContextBackFillSize = 0;
+
+        //
+        // The SupportedOidList is an array of OIDs for objects that the
+        // underlying driver or its NIC supports.  Objects include general,
+        // media-specific, and implementation-specific objects. NDIS forwards a
+        // subset of the returned list to protocols that make this query. That
+        // is, NDIS filters any supported statistics OIDs out of the list
+        // because protocols never make statistics queries.
+        //
+        AdapterGeneral.SupportedOidList = NICSupportedOids;
+        AdapterGeneral.SupportedOidListLength = SizeOfNICSupportedOids;
+        AdapterGeneral.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED;
+
+        //
+        // Set the power management capabilities. All 0 basically means we don't
+        // support Dx for anything.
+        //
+
+        PmCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+        PmCapabilities.Header.Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_2;
+        PmCapabilities.Header.Revision = NDIS_PM_CAPABILITIES_REVISION_2;
+
+        AdapterGeneral.PowerManagementCapabilitiesEx = &PmCapabilities;
+
+        Status = NdisMSetMiniportAttributes(
+            MiniportAdapterHandle,
+            (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterGeneral);
+
+        if (NDIS_STATUS_SUCCESS != Status)
+        {
+            LogError(DRIVER_DEFAULT, "[%p] NdisSetOptionalHandlers failed %!NDIS_STATUS!", AdapterContext, Status);
+            break;
+        }
+
+    } while (FALSE);
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    return Status;
+}
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+AdapterUninitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    SerialUninitialize(AdapterContext);
+
+    if (AdapterContext->Device)
+    {
+        WdfObjectDelete(AdapterContext->Device);
+        AdapterContext->Device = nullptr;
+    }
+
+    NdisFreeMemory(AdapterContext, 0, 0);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+PAGED
+_IRQL_requires_( PASSIVE_LEVEL )
+_Function_class_( MINIPORT_RESTART )
+NDIS_STATUS
+MPRestart(
+    _In_  NDIS_HANDLE                             MiniportAdapterContext,
+    _In_  PNDIS_MINIPORT_RESTART_PARAMETERS       /* RestartParameters */
+    )
+/*++
+
+Routine Description:
+
+    When a miniport receives a restart request, it enters into a Restarting
+    state.  The miniport may begin indicating received data (e.g., using
+    NdisMIndicateReceiveNetBufferLists), handling status indications, and
+    processing OID requests in the Restarting state.  However, no sends will be
+    requested while the miniport is in the Restarting state.
+
+    Once the miniport is ready to send data, it has entered the Running state.
+    The miniport informs NDIS that it is in the Running state by returning
+    NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function
+    has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete.
+
+
+    MiniportRestart runs at IRQL = PASSIVE_LEVEL.
+
+Arguments:
+
+    MiniportAdapterContext  Pointer to the Adapter
+    RestartParameters  Additional information about the restart operation
+
+Return Value:
+
+    If the miniport is able to immediately enter the Running state, it should
+    return NDIS_STATUS_SUCCESS.
+
+    If the miniport is still in the Restarting state, it should return
+    NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport
+    has entered the Running state.
+
+    Other NDIS_STATUS codes indicate errors.  If an error is encountered, the
+    miniport must return to the Paused state (i.e., stop indicating receives).
+
+--*/
+
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    // Set the running flag
+    AdapterContext->IsRunning = true;
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_IRQL_requires_( PASSIVE_LEVEL )
+_Function_class_( MINIPORT_PAUSE )
+NDIS_STATUS
+MPPause(
+    _In_  NDIS_HANDLE                       MiniportAdapterContext,
+    _In_  PNDIS_MINIPORT_PAUSE_PARAMETERS   /* MiniportPauseParameters */
+    )
+/*++
+
+Routine Description:
+
+    When a miniport receives a pause request, it enters into a Pausing state.
+    The miniport should not indicate up any more network data.  Any pending
+    send requests must be completed, and new requests must be rejected with
+    NDIS_STATUS_PAUSED.
+
+    Once all sends have been completed and all recieve NBLs have returned to
+    the miniport, the miniport enters the Paused state.
+
+    While paused, the miniport can still service interrupts from the hardware
+    (to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT
+    notifications).
+
+    The miniport must continue to be able to handle status indications and OID
+    requests.  MiniportPause is different from MiniportHalt because, in
+    general, the MiniportPause operation won't release any resources.
+    MiniportPause must not attempt to acquire any resources where allocation
+    can fail, since MiniportPause itself must not fail.
+
+
+    MiniportPause runs at IRQL = PASSIVE_LEVEL.
+
+Arguments:
+
+    MiniportAdapterContext  Pointer to the Adapter
+    MiniportPauseParameters  Additional information about the pause operation
+
+Return Value:
+
+    If the miniport is able to immediately enter the Paused state, it should
+    return NDIS_STATUS_SUCCESS.
+
+    If the miniport must wait for send completions or pending receive NBLs, it
+    should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the
+    miniport has entered the Paused state.
+
+    No other return value is permitted.  The pause operation must not fail.
+
+--*/
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+    
+    // Clear the flag to indicate we are no longer running
+    AdapterContext->IsRunning = false;
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+VOID
+MPSendNetBufferLists(
+    _In_  NDIS_HANDLE             MiniportAdapterContext,
+    _In_  PNET_BUFFER_LIST        NetBufferLists,
+    _In_  NDIS_PORT_NUMBER        /* PortNumber */,
+    _In_  ULONG                   SendFlags
+    )
+/*++
+
+Routine Description:
+
+    Send Packet Array handler. Called by NDIS whenever a protocol
+    bound to our miniport sends one or more packets.
+
+    The input packet descriptor pointers have been ordered according
+    to the order in which the packets should be sent over the network
+    by the protocol driver that set up the packet array. The NDIS
+    library preserves the protocol-determined ordering when it submits
+    each packet array to MiniportSendPackets
+
+    As a deserialized driver, we are responsible for holding incoming send
+    packets in our internal queue until they can be transmitted over the
+    network and for preserving the protocol-determined ordering of packet
+    descriptors incoming to its MiniportSendPackets function.
+    A deserialized miniport driver must complete each incoming send packet
+    with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable.
+
+    Runs at IRQL <= DISPATCH_LEVEL
+
+Arguments:
+
+    MiniportAdapterContext      Pointer to our adapter
+    NetBufferLists              Head of a list of NBLs to send
+    PortNumber                  A miniport adapter port.  Default is 0.
+    SendFlags                   Additional flags for the send operation
+
+Return Value:
+
+    None.  Write status directly into each NBL with the NET_BUFFER_LIST_STATUS
+    macro.
+
+--*/
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
+    PNET_BUFFER_LIST FailedNbls = NULL;
+
+    LogFuncEntryMsg(DRIVER_DEFAULT, "NetBufferList: %p", NetBufferLists);
+
+    PNET_BUFFER_LIST CurrNbl = NetBufferLists;
+    while (CurrNbl)
+    {
+        PNET_BUFFER_LIST NextNbl = CurrNbl->Next;
+        CurrNbl->Next = NULL;
+
+        // Only allow one NB per NBL
+        if (CurrNbl->FirstNetBuffer == NULL ||
+            CurrNbl->FirstNetBuffer->Next != NULL)
+        {
+            CurrNbl->Status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            // Try to queue up for send
+            NTSTATUS status = SerialSendData(AdapterContext, CurrNbl);
+
+            if (!NT_SUCCESS(status)) {
+                CurrNbl->Status = status;
+            }
+            else {
+                CurrNbl = NULL;
+            }
+        }
+
+        // If we still have the CurrNbl, it failed, so move it to the failure list
+        if (CurrNbl) {
+            CurrNbl->Next = FailedNbls;
+            FailedNbls = CurrNbl;
+        }
+
+        CurrNbl = NextNbl;
+    }
+    
+    // Complete any failures
+    if (FailedNbls) {
+        NdisMSendNetBufferListsComplete(AdapterContext->Adapter, FailedNbls, (SendFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL));
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+VOID
+MPCancelSend(
+    _In_  NDIS_HANDLE     /* MiniportAdapterContext */,
+    _In_  PVOID           /* CancelId */
+    )
+/*++
+
+Routine Description:
+
+    MiniportCancelSend cancels the transmission of all NET_BUFFER_LISTs that
+    are marked with a specified cancellation identifier. Miniport drivers
+    that queue send packets for more than one second should export this
+    handler. When a protocol driver or intermediate driver calls the
+    NdisCancelSendNetBufferLists function, NDIS calls the MiniportCancelSend
+    function of the appropriate lower-level driver (miniport driver or
+    intermediate driver) on the binding.
+
+    Runs at IRQL <= DISPATCH_LEVEL.
+
+Arguments:
+
+    MiniportAdapterContext      Pointer to our adapter
+    CancelId                    All the packets with this Id should be cancelled
+
+Return Value:
+
+    None.
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+VOID
+MPReturnNetBufferLists(
+    _In_  NDIS_HANDLE       /* MiniportAdapterContext */,
+    _In_  PNET_BUFFER_LIST  NetBufferLists,
+    _In_  ULONG             /* ReturnFlags */
+    )
+/*++
+
+Routine Description:
+
+    NDIS Miniport entry point called whenever protocols are done with one or
+    NBLs that we indicated up with NdisMIndicateReceiveNetBufferLists.
+
+    Note that the list of NBLs may be chained together from multiple separate
+    lists that were indicated up individually.
+
+Arguments:
+
+    MiniportAdapterContext      Pointer to our adapter
+    NetBufferLists              NBLs being returned
+    ReturnFlags                 May contain the NDIS_RETURN_FLAGS_DISPATCH_LEVEL
+                                flag, which if is set, indicates we can get a
+                                small perf win by not checking or raising the
+                                IRQL
+
+Return Value:
+
+    None.
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    // Iterate through all the NetBufferLists
+    for (PNET_BUFFER_LIST pNblNext, pNbl = NetBufferLists; pNbl; pNbl = pNblNext)
+    {
+        // Save next to temporary and clear member variable
+        pNblNext = NET_BUFFER_LIST_NEXT_NBL(pNbl);
+        NET_BUFFER_LIST_NEXT_NBL(pNbl) = NULL;
+
+        // Iterate through all the Netbuffers
+        for (PNET_BUFFER pNbNext, pNb = NET_BUFFER_LIST_FIRST_NB(pNbl); pNb; pNb = pNbNext)
+        {
+            pNbNext = NET_BUFFER_NEXT_NB(pNb);
+            NdisFreeNetBuffer(pNb);
+        }
+
+        NdisFreeNetBufferList(pNbl);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/ottmp/adapter.hpp b/examples/drivers/windows/ottmp/adapter.hpp
new file mode 100644
index 0000000..fc51171
--- /dev/null
+++ b/examples/drivers/windows/ottmp/adapter.hpp
@@ -0,0 +1,224 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   Header file for the routines related to NETADAPTER Object for the 
+ *   Network Adapter
+ */
+
+#define NIC_TAG_RECV_NBL ((ULONG)'rMVT')  // TVMr
+#define OTTMP_ADAPTER_CONTEXT_SIGNATURE 'pdAt'
+
+// The maximum size of one Spinel command / serial packet
+#define MAX_SPINEL_COMMAND_LENGTH     1300
+
+typedef struct _SERIAL_SEND_ITEM
+{
+    LIST_ENTRY          Link;
+    PNET_BUFFER_LIST    NetBufferList;
+    WDFMEMORY           WdfMemory;
+    ULONG               EncodedBufferLength;
+    _Field_size_bytes_(EncodedBufferLength)
+    UCHAR               EncodedBuffer[0];
+
+} SERIAL_SEND_ITEM, *PSERIAL_SEND_ITEM;
+
+const ULONG SERIAL_SEND_ITEM_SIZE = FIELD_OFFSET(SERIAL_SEND_ITEM, EncodedBuffer);
+
+typedef struct _OTTMP_ADAPTER_CONTEXT {
+    ULONG                           Signature;
+
+#ifdef OTTMP_LEGACY
+    NDIS_HANDLE                     Adapter;
+#else
+    //
+    // Handle to the NETADAPTER object for this context
+    //
+    NETADAPTER                      Adapter;
+#endif
+
+    //
+    // Handle to the corresponding WDFDEVICE
+    //
+    WDFDEVICE                       Device;
+
+    // Flag to indicate if the data path is enabled
+    bool                            IsConnected;
+
+    // Flag to indicate if the Adapter has been started or not
+    bool                            IsRunning;
+    
+#ifdef OTTMP_LEGACY
+    PGLOBALS                        pGlobals;
+#else
+    // Receive packet pool
+    NETBUFFERLISTCOLLECTION         ReceiveCollection;
+#endif
+    
+    ULONGLONG                       ExtendedAddress; // TODO - Cache
+
+    //
+    // Serial Device
+    //
+
+    WDFIOTARGET                     WdfIoTarget;
+
+    WDFSPINLOCK                     SendLock;
+    _Guarded_by_(SendLock)
+    LIST_ENTRY                      SendQueue;
+    bool                            SendQueueRunning;
+    WDFWORKITEM                     SendWorkItem;
+    
+    WDFWORKITEM                     RecvWorkItem;
+    WDFREQUEST                      RecvReadRequest;
+
+    UCHAR                           RecvBuffer[MAX_SPINEL_COMMAND_LENGTH * 2];
+    ULONG                           RecvBufferLength;
+
+    //
+    // NIC configuration - This information is queried by the protocol drivers.
+    // Since this sample focuses on demonstrating the NDIS WDF model, 
+    // it doesn't modify any these values during runtime.
+    // Please look at the netvmini630 sample to see how these values can be set
+    // and updated
+    // -------------------------------------------------------------------------
+    //
+    ULONG                   PacketFilter;
+    ULONG                   ulLookahead;
+    ULONG64                 ulLinkSendSpeed;
+    ULONG64                 ulLinkRecvSpeed;
+    ULONG                   ulMaxBusySends;
+    ULONG                   ulMaxBusyRecvs;
+
+    //
+    // Statistics - 
+    // Since this sample focuses on demonstrating the NDIS WDF model, 
+    // it doesn't modify any these values during runtime.
+    // Please look at the netvmini630 sample to see how these values can be set
+    // and updated
+    // -------------------------------------------------------------------------
+    //
+
+    // Packet counts
+    ULONG64                 FramesRxDirected;
+    ULONG64                 FramesRxMulticast;
+    ULONG64                 FramesRxBroadcast;
+    ULONG64                 FramesTxDirected;
+    ULONG64                 FramesTxMulticast;
+    ULONG64                 FramesTxBroadcast;
+
+    // Byte counts
+    ULONG64                 BytesRxDirected;
+    ULONG64                 BytesRxMulticast;
+    ULONG64                 BytesRxBroadcast;
+    ULONG64                 BytesTxDirected;
+    ULONG64                 BytesTxMulticast;
+    ULONG64                 BytesTxBroadcast;
+
+    // Count of transmit errors
+    ULONG                   TxAbortExcessCollisions;
+    ULONG                   TxLateCollisions;
+    ULONG                   TxDmaUnderrun;
+    ULONG                   TxLostCRS;
+    ULONG                   TxOKButDeferred;
+    ULONG                   OneRetry;
+    ULONG                   MoreThanOneRetry;
+    ULONG                   TotalRetries;
+    ULONG                   TransmitFailuresOther;
+
+    // Count of receive errors
+    ULONG                   RxCrcErrors;
+    ULONG                   RxAlignmentErrors;
+    ULONG                   RxResourceErrors;
+    ULONG                   RxDmaOverrunErrors;
+    ULONG                   RxCdtFrames;
+    ULONG                   RxRuntErrors;
+
+} OTTMP_ADAPTER_CONTEXT, *POTTMP_ADAPTER_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OTTMP_ADAPTER_CONTEXT, GetAdapterContext);
+
+typedef struct _WDF_DEVICE_INFO
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext;
+
+} WDF_DEVICE_INFO, *PWDF_DEVICE_INFO;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WDF_DEVICE_INFO, GetWdfDeviceInfo);
+
+#ifdef OTTMP_LEGACY
+
+PAGED MINIPORT_PAUSE                    MPPause;
+PAGED MINIPORT_RESTART                  MPRestart;
+
+MINIPORT_SEND_NET_BUFFER_LISTS          MPSendNetBufferLists;
+MINIPORT_RETURN_NET_BUFFER_LISTS        MPReturnNetBufferLists;
+MINIPORT_CANCEL_SEND                    MPCancelSend;
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NDIS_STATUS
+AdapterInitialize(
+    _In_ NDIS_HANDLE                MiniportAdapterHandle,
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+AdapterUninitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+#else
+
+PAGED EVT_NET_ADAPTER_SET_CAPABILITIES  EvtAdapterSetCapabilities;
+PAGED EVT_NET_ADAPTER_START             EvtAdapterStart;
+PAGED EVT_NET_ADAPTER_PAUSE             EvtAdapterPause;
+
+EVT_NET_ADAPTER_SEND_NET_BUFFER_LISTS   EvtAdapterSendNetBufferLists;
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+AdapterInitialize(
+    _In_ NETADAPTER             Adapter,
+    _In_ POTTMP_ADAPTER_CONTEXT AdapterContext,
+    _In_ POTTMP_DEVICE_CONTEXT  DeviceContext
+    );
+
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+AdapterIndicateEnergyScanComplete(
+    _In_ POTTMP_ADAPTER_CONTEXT AdapterContext,
+    _In_ NDIS_STATUS            Status
+    );
+
+EXT_CALLBACK AdapterEnergyScanTimerCallback;
diff --git a/examples/drivers/windows/ottmp/device.cpp b/examples/drivers/windows/ottmp/device.cpp
new file mode 100644
index 0000000..9b7619c
--- /dev/null
+++ b/examples/drivers/windows/ottmp/device.cpp
@@ -0,0 +1,383 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This module implements code to manage the WDFDEVICE object for the
+ *   network adapter.
+ */
+
+#include <initguid.h>
+#include "pch.hpp"
+#include "device.tmh"
+
+PAGED
+_IRQL_requires_( PASSIVE_LEVEL )
+_Function_class_( MINIPORT_INITIALIZE )
+NDIS_STATUS
+MPInitializeEx(
+    _In_  NDIS_HANDLE                       MiniportAdapterHandle,
+    _In_  NDIS_HANDLE                       MiniportDriverContext,
+    _In_  PNDIS_MINIPORT_INIT_PARAMETERS    /* MiniportInitParameters */
+    )
+/*++
+Routine Description:
+
+    The MiniportInitialize function is a required function that sets up a
+    NIC (or virtual NIC) for network I/O operations, claims all hardware
+    resources necessary to the NIC in the registry, and allocates resources
+    the driver needs to carry out network I/O operations.
+
+    MiniportInitialize runs at IRQL = PASSIVE_LEVEL.
+
+Arguments:
+
+Return Value:
+
+    NDIS_STATUS_xxx code
+
+--*/
+{
+    NDIS_STATUS             Status = NDIS_STATUS_FAILURE;
+    PGLOBALS                pGlobals = (PGLOBALS)MiniportDriverContext;
+    PDEVICE_OBJECT          pPdo = nullptr;
+    PDEVICE_OBJECT          pFdo = nullptr;
+    PDEVICE_OBJECT          pNextDeviceObject = nullptr;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+    WDFDEVICE               device = nullptr;
+    POTTMP_DEVICE_CONTEXT   deviceContext = nullptr;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    do
+    {
+        NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistration = { 0 };
+        NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneral = { 0 };
+        NDIS_PM_CAPABILITIES PmCapabilities = { 0 };
+
+        //
+        // NdisMGetDeviceProperty function enables us to get the:
+        // PDO - created by the bus driver to represent our device.
+        // FDO - created by NDIS to represent our miniport as a function
+        //              driver.
+        // NextDeviceObject - deviceobject of another driver (filter)
+        //                    attached to us at the bottom.
+        // Since our driver is talking to NDISPROT, the NextDeviceObject
+        // is not useful. But if we were to talk to a driver that we
+        // are attached to as part of the devicestack then NextDeviceObject
+        // would be our target DeviceObject for sending read/write Requests.
+        //
+
+        NdisMGetDeviceProperty(
+            MiniportAdapterHandle,
+            &pPdo,
+            &pFdo,
+            &pNextDeviceObject,
+            nullptr,
+            nullptr);
+
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, OTTMP_DEVICE_CONTEXT);
+
+        NTSTATUS ntStatus = WdfDeviceMiniportCreate(
+            pGlobals->WdfDriver,
+            &attributes,
+            pFdo,
+            pNextDeviceObject,
+            pPdo,
+            &device);
+
+        if (!NT_SUCCESS(ntStatus))
+        {
+            LogError(DRIVER_DEFAULT, "WdfDeviceMiniportCreate failed %!STATUS!", ntStatus);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        //
+        // Get WDF miniport device context.
+        //
+        deviceContext = GetDeviceContext(device);
+        deviceContext->Signature = OTTMP_DEVICE_CONTEXT_SIGNATURE;
+        deviceContext->Device = device;
+
+        //
+        // Allocate adapter context structure and initialize all the
+        // memory resources for sending and receiving packets.
+        //
+        deviceContext->AdapterContext = (POTTMP_ADAPTER_CONTEXT)NdisAllocateMemoryWithTagPriority(
+            pGlobals->hDriver, 
+            sizeof(OTTMP_ADAPTER_CONTEXT), 
+            OTTMP_ADAPTER_CONTEXT_SIGNATURE,
+            NormalPoolPriority);
+
+        if (deviceContext->AdapterContext == nullptr)
+        {
+            LogError(DRIVER_DEFAULT, "NdisAllocateMemoryWithTagPriority failed");
+            Status = NDIS_STATUS_RESOURCES;
+            break;
+        }
+        
+        RtlZeroMemory(deviceContext->AdapterContext, sizeof(OTTMP_ADAPTER_CONTEXT));
+        deviceContext->AdapterContext->Signature = OTTMP_ADAPTER_CONTEXT_SIGNATURE;
+        deviceContext->AdapterContext->Adapter = MiniportAdapterHandle;
+        deviceContext->AdapterContext->Device = deviceContext->Device;
+        deviceContext->AdapterContext->pGlobals = pGlobals;
+
+        Status = AdapterInitialize(
+            MiniportAdapterHandle,
+            deviceContext->AdapterContext);
+
+        if (NDIS_STATUS_SUCCESS != Status)
+        {
+            LogError(DRIVER_DEFAULT, "AdapterInitialize failed %!NDIS_STATUS!", Status);
+            break;
+        }
+
+        ntStatus = SerialInitialize(deviceContext->AdapterContext);
+        if (!NT_SUCCESS(ntStatus))
+        {
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        // Start the read loop
+        LogVerbose(DRIVER_DEFAULT, "Starting recv worker");
+        WdfWorkItemEnqueue(deviceContext->AdapterContext->RecvWorkItem);
+
+    } while (FALSE);
+
+    if (Status != NDIS_STATUS_SUCCESS)
+    {
+        if (deviceContext && deviceContext->AdapterContext)
+        {
+            AdapterUninitialize(deviceContext->AdapterContext);
+            deviceContext->AdapterContext = nullptr;
+        }
+    }
+
+    LogFuncExitNDIS(DRIVER_DEFAULT, Status);
+
+    return Status;
+}
+
+PAGED
+VOID
+MPHaltEx(
+    _In_  NDIS_HANDLE           MiniportAdapterContext,
+    _In_  NDIS_HALT_ACTION      HaltAction
+    )
+/*++
+
+Routine Description:
+
+    Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE,
+    IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the PNP
+    manager. Here, the driver should free all the resources acquired in
+    MiniportInitialize and stop access to the hardware. NDIS will not submit
+    any further request once this handler is invoked.
+
+    1) Free and unmap all I/O resources.
+    2) Disable interrupt and deregister interrupt handler.
+    3) Deregister shutdown handler regsitered by
+        NdisMRegisterAdapterShutdownHandler .
+    4) Cancel all queued up timer callbacks.
+    5) Finally wait indefinitely for all the outstanding receive
+        packets indicated to the protocol to return.
+
+    MiniportHalt runs at IRQL = PASSIVE_LEVEL.
+
+
+Arguments:
+
+    MiniportAdapterContext  Pointer to the Adapter
+    HaltAction  The reason for halting the adapter
+
+Return Value:
+
+    None.
+
+--*/
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    //
+    // Call Shutdown handler to disable interrupt and turn the hardware off
+    // by issuing a full reset
+    //
+    if (HaltAction != NdisHaltDeviceSurpriseRemoved)
+    {
+        MPShutdownEx(MiniportAdapterContext, NdisShutdownPowerOff);
+    }
+
+    AdapterUninitialize(AdapterContext);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+VOID
+MPShutdownEx(
+    _In_  NDIS_HANDLE           /* MiniportAdapterContext */,
+    _In_  NDIS_SHUTDOWN_ACTION  /* ShutdownAction */
+    )
+/*++
+
+Routine Description:
+
+    The MiniportShutdownEx handler restores hardware to its initial state when
+    the system is shut down, whether by the user or because an unrecoverable
+    system error occurred. This is to ensure that the NIC is in a known
+    state and ready to be reinitialized when the machine is rebooted after
+    a system shutdown occurs for any reason, including a crash dump.
+
+    Here just disable the interrupt and stop the DMA engine.  Do not free
+    memory resources or wait for any packet transfers to complete.  Do not call
+    into NDIS at this time.
+
+    This can be called at aribitrary IRQL, including in the context of a
+    bugcheck.
+
+Arguments:
+
+    MiniportAdapterContext  Pointer to our adapter
+    ShutdownAction  The reason why NDIS called the shutdown function
+
+Return Value:
+
+    None.
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+PAGED
+VOID
+MPDevicePnpEventNotify(
+    _In_  NDIS_HANDLE             /* MiniportAdapterContext */,
+    _In_  PNET_DEVICE_PNP_EVENT   NetDevicePnPEvent
+    )
+/*++
+
+Routine Description:
+
+    Runs at IRQL = PASSIVE_LEVEL in the context of system thread.
+
+Arguments:
+
+    MiniportAdapterContext      Pointer to our adapter
+    NetDevicePnPEvent           Self-explanatory
+
+Return Value:
+
+    None.
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    switch (NetDevicePnPEvent->DevicePnPEvent)
+    {
+    case NdisDevicePnPEventQueryRemoved:
+        //
+        // Called when NDIS receives IRP_MN_QUERY_REMOVE_DEVICE.
+        //
+        LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventQueryRemoved");
+        break;
+
+    case NdisDevicePnPEventRemoved:
+        //
+        // Called when NDIS receives IRP_MN_REMOVE_DEVICE.
+        // NDIS calls MiniportHalt function after this call returns.
+        //
+        LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventRemoved");
+        break;
+
+    case NdisDevicePnPEventSurpriseRemoved:
+        //
+        // Called when NDIS receives IRP_MN_SUPRISE_REMOVAL.
+        // NDIS calls MiniportHalt function after this call returns.
+        //
+        LogInfo(DRIVER_DEFAULT, "MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved");
+        break;
+
+    case NdisDevicePnPEventQueryStopped:
+        //
+        // Called when NDIS receives IRP_MN_QUERY_STOP_DEVICE. ??
+        //
+        LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventQueryStopped");
+        break;
+
+    case NdisDevicePnPEventStopped:
+        //
+        // Called when NDIS receives IRP_MN_STOP_DEVICE.
+        // NDIS calls MiniportHalt function after this call returns.
+        //
+        //
+        LogInfo(DRIVER_DEFAULT, "MPPnPEventNotify: NdisDevicePnPEventStopped");
+        break;
+
+    case NdisDevicePnPEventPowerProfileChanged:
+        //
+        // After initializing a miniport driver and after miniport driver
+        // receives an OID_PNP_SET_POWER notification that specifies
+        // a device power state of NdisDeviceStateD0 (the powered-on state),
+        // NDIS calls the miniport's MiniportPnPEventNotify function with
+        // PnPEvent set to NdisDevicePnPEventPowerProfileChanged.
+        //
+        LogInfo(DRIVER_DEFAULT, "MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged");
+
+        if (NetDevicePnPEvent->InformationBufferLength == sizeof( ULONG ))
+        {
+            const ULONG NdisPowerProfile = *((const ULONG *)NetDevicePnPEvent->InformationBuffer);
+
+            if (NdisPowerProfile == NdisPowerProfileBattery)
+            {
+                LogInfo(DRIVER_DEFAULT, "The host system is running on battery power");
+            }
+            else if (NdisPowerProfile == NdisPowerProfileAcOnLine)
+            {
+                LogInfo(DRIVER_DEFAULT, "The host system is running on AC power");
+            }
+        }
+        break;
+
+    default:
+        LogError(DRIVER_DEFAULT, "MPDevicePnpEventNotify: unknown PnP event 0x%x\n", NetDevicePnPEvent->DevicePnPEvent);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/ottmp/device.hpp b/examples/drivers/windows/ottmp/device.hpp
new file mode 100644
index 0000000..cca9ef2
--- /dev/null
+++ b/examples/drivers/windows/ottmp/device.hpp
@@ -0,0 +1,72 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   Header file for the routines related to WDFDEVICE representing the
+ *   Network Adapter
+ */
+
+#pragma once
+
+#define OTTMP_DEVICE_CONTEXT_SIGNATURE 'veDt'
+typedef struct _OTTMP_DEVICE_CONTEXT {
+    //
+    // Signature for Sanity Check.
+    //
+    ULONG                     Signature;
+
+    //
+    // Handle to the WDFDEVICE of which this is the context
+    //
+    WDFDEVICE                 Device;
+
+    //
+    // Pointer to the Context of the corresponding NETADAPTER object
+    //
+    POTTMP_ADAPTER_CONTEXT AdapterContext;
+
+} OTTMP_DEVICE_CONTEXT, *POTTMP_DEVICE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(OTTMP_DEVICE_CONTEXT, GetDeviceContext);
+
+extern "C" {
+#ifdef OTTMP_LEGACY
+PAGED MINIPORT_INITIALIZE                    MPInitializeEx;
+PAGED MINIPORT_HALT                          MPHaltEx;
+MINIPORT_SHUTDOWN                            MPShutdownEx;
+PAGED MINIPORT_DEVICE_PNP_EVENT_NOTIFY       MPDevicePnpEventNotify;
+#else
+PAGED EVT_WDF_DRIVER_DEVICE_ADD              EvtDriverDeviceAdd;
+PAGED EVT_WDF_DEVICE_D0_EXIT                 EvtDeviceD0Exit;
+PAGED EVT_WDF_DEVICE_PREPARE_HARDWARE        EvtDevicePrepareHardware;
+PAGED EVT_WDF_DEVICE_RELEASE_HARDWARE        EvtDeviceReleaseHardware;
+PAGED EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT    EvtDeviceSelfManagedIoInit;
+PAGED EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP EvtDeviceSelfManagedIoCleanup;
+EVT_WDF_DEVICE_D0_ENTRY                      EvtDeviceD0Entry;
+#endif
+}
diff --git a/examples/drivers/windows/ottmp/driver.cpp b/examples/drivers/windows/ottmp/driver.cpp
new file mode 100644
index 0000000..348df82
--- /dev/null
+++ b/examples/drivers/windows/ottmp/driver.cpp
@@ -0,0 +1,273 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This module has code to deal with loading and unloading of the driver 
+ */
+
+#include "pch.hpp"
+#include "driver.tmh"
+
+#ifdef OTTMP_LEGACY
+static GLOBALS GlobalData = { 0 };
+#endif
+
+INITCODE
+extern "C"
+NTSTATUS
+DriverEntry (
+    _In_ PDRIVER_OBJECT   DriverObject,
+    _In_ PUNICODE_STRING  RegistryPath
+    )
+/*++
+
+Routine Description:
+    DriverEntry initializes the driver and is the first routine called by the
+    system after the driver is loaded. DriverEntry specifies the other entry
+    points in the function driver, such as EvtDevice and DriverUnload.
+
+Parameters Description:
+
+    DriverObject - represents the instance of the function driver that is loaded
+    into memory. DriverEntry must initialize members of DriverObject before it
+    returns to the caller. DriverObject is allocated by the system before the
+    driver is loaded, and it is released by the system after the system unloads
+    the function driver from memory.
+
+    RegistryPath - represents the driver specific path in the Registry.
+    The function driver can use the path to store driver related data between
+    reboots. The path does not store hardware instance specific data.
+
+Return Value:
+
+    A success status as determined by NT_SUCCESS macro, if successful. 
+ 
+--*/
+{
+    NTSTATUS                             status;
+    WDF_DRIVER_CONFIG                    config;
+#ifdef OTTMP_LEGACY
+    NDIS_STATUS                          ndisStatus = NDIS_STATUS_FAILURE;
+    NDIS_MINIPORT_DRIVER_CHARACTERISTICS MPChar = { 0 };
+    NET_BUFFER_LIST_POOL_PARAMETERS      NblParams = { 0 };
+    NET_BUFFER_POOL_PARAMETERS           NbParams = { 0 };
+#endif
+
+    WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    //
+    // Create the WdfDriver Object 
+    //
+    WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
+
+#ifdef OTTMP_LEGACY
+    //
+    // Set WdfDriverInitNoDispatchOverride flag to tell the framework
+    // not to provide dispatch routines for the driver. In other words,
+    // the framework must not intercept IRPs that the I/O manager has
+    // directed to the driver. In this case, it will be handled by NDIS
+    // port driver.
+    //
+    config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
+#else
+    config.EvtDriverDeviceAdd = EvtDriverDeviceAdd;
+    config.EvtDriverUnload = EvtDriverUnload;
+#endif
+
+    status = WdfDriverCreate(DriverObject,
+                             RegistryPath,
+                             WDF_NO_OBJECT_ATTRIBUTES,
+                             &config,
+#ifdef OTTMP_LEGACY
+                             &GlobalData.WdfDriver
+#else
+                             NULL
+#endif
+                             );
+    if (!NT_SUCCESS(status))
+    {
+        LogError(DRIVER_DEFAULT, "WdfDriverCreate failed, %!STATUS!", status);
+        goto error;
+    }
+    
+#ifdef OTTMP_LEGACY
+    MPChar.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS;
+    MPChar.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;
+    MPChar.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;
+
+    // Version Suff
+    MPChar.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION;
+    MPChar.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION;
+    MPChar.MajorDriverVersion = NIC_VENDOR_DRIVER_VERSION_MAJOR;
+    MPChar.MinorDriverVersion = NIC_VENDOR_DRIVER_VERSION_MINOR;
+
+    MPChar.InitializeHandlerEx = MPInitializeEx;
+    MPChar.HaltHandlerEx = MPHaltEx;
+    MPChar.UnloadHandler = MPDriverUnload;
+    MPChar.PauseHandler = MPPause;
+    MPChar.RestartHandler = MPRestart;
+    MPChar.OidRequestHandler = MPOidRequest;
+    MPChar.SendNetBufferListsHandler = MPSendNetBufferLists;
+    MPChar.ReturnNetBufferListsHandler = MPReturnNetBufferLists;
+    MPChar.CancelSendHandler = MPCancelSend;
+    MPChar.DevicePnPEventNotifyHandler = MPDevicePnpEventNotify;
+    MPChar.ShutdownHandlerEx = MPShutdownEx;
+    MPChar.CancelOidRequestHandler = MPCancelOidRequest;
+
+    ndisStatus = NdisMRegisterMiniportDriver(DriverObject,
+                                             RegistryPath,
+                                             &GlobalData,
+                                             &MPChar,
+                                             &GlobalData.hDriver);
+    if (ndisStatus != NDIS_STATUS_SUCCESS)
+    {
+        LogError(DRIVER_DEFAULT, "NdisMRegisterMiniportDriver failed %!NDIS_STATUS!", ndisStatus);
+        status = STATUS_UNSUCCESSFUL;
+        goto error;
+    }
+
+    NblParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+    NblParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+    NblParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
+    NblParams.PoolTag = NIC_TAG_RECV_NBL;
+    GlobalData.hNblPool = NdisAllocateNetBufferListPool(GlobalData.hDriver, &NblParams);
+    if (GlobalData.hNblPool == 0)
+    {
+        LogError(DRIVER_DEFAULT, "NdisAllocateNetBufferListPool failed");
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto error;
+    }
+
+    NbParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
+    NbParams.Header.Revision = NET_BUFFER_POOL_PARAMETERS_REVISION_1;
+    NbParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1;
+    NbParams.PoolTag = NIC_TAG_RECV_NBL;
+    NbParams.DataSize = MAX_SPINEL_COMMAND_LENGTH;
+    GlobalData.hNbPool = NdisAllocateNetBufferPool(GlobalData.hDriver, &NbParams);
+    if (GlobalData.hNbPool == 0)
+    {
+        LogError(DRIVER_DEFAULT, "NdisAllocateNetBufferListPool failed");
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto error;
+    }
+#endif
+
+error:
+
+    if (!NT_SUCCESS(status)) 
+    {
+#ifdef OTTMP_LEGACY
+        MPDriverUnload(DriverObject);
+#else
+        WPP_CLEANUP(DriverObject);
+#endif
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+    return status;
+}
+
+#ifdef OTTMP_LEGACY
+
+PAGED
+_Use_decl_annotations_
+VOID
+MPDriverUnload(
+    _In_ PDRIVER_OBJECT DriverObject
+    )
+/*++
+Routine Description:
+
+    MPDriverUnload will clean up the WPP resources that was allocated
+    for this driver.
+
+Arguments:
+
+    DriverObject - Handle to a framework driver object created in DriverEntry
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    if (GlobalData.WdfDriver)
+    {
+        NT_ASSERT(GlobalData.WdfDriver == WdfGetDriver());
+        WdfDriverMiniportUnload(GlobalData.WdfDriver);
+        GlobalData.WdfDriver = nullptr;
+    }
+
+    if (GlobalData.hNblPool)
+    {
+        NdisFreeNetBufferListPool(GlobalData.hNblPool);
+        GlobalData.hNblPool = nullptr;
+    }
+
+    if (GlobalData.hDriver)
+    {
+        NdisMDeregisterMiniportDriver(GlobalData.hDriver);
+        GlobalData.hDriver = nullptr;
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+
+#pragma warning(suppress: 25024) // Dangerous cast
+    WPP_CLEANUP(DriverObject);
+}
+
+#else
+
+PAGED
+VOID
+EvtDriverUnload(
+    WDFDRIVER Driver
+    )
+/*++
+Routine Description:
+
+    EvtDriverUnload will clean up the WPP resources that was allocated
+    for this driver.
+
+Arguments:
+
+    Driver - Handle to a framework driver object created in DriverEntry
+
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    LogFuncExit(DRIVER_DEFAULT);
+    WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver));
+}
+
+#endif
diff --git a/examples/drivers/windows/ottmp/driver.hpp b/examples/drivers/windows/ottmp/driver.hpp
new file mode 100644
index 0000000..f63e6ad
--- /dev/null
+++ b/examples/drivers/windows/ottmp/driver.hpp
@@ -0,0 +1,58 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   Header file for the Driver Load / Unload routines
+ */
+
+#pragma once
+
+extern "C" {
+DRIVER_INITIALIZE DriverEntry;
+}
+
+#ifdef OTTMP_LEGACY
+PAGED MINIPORT_UNLOAD MPDriverUnload;
+
+typedef struct _GLOBALS
+{
+    WDFDRIVER        WdfDriver;
+    NDIS_HANDLE      hDriver;
+    NDIS_HANDLE      hNblPool;
+    NDIS_HANDLE      hNbPool;
+} GLOBALS, *PGLOBALS;
+#else
+PAGED EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
+#endif
+
+//
+// Own Version
+//
+#define NIC_VENDOR_DRIVER_VERSION_MAJOR  1
+#define NIC_VENDOR_DRIVER_VERSION_MINOR  0
+#define NIC_VENDOR_DRIVER_VERSION ((NIC_VENDOR_DRIVER_VERSION_MAJOR << 16) | NIC_VENDOR_DRIVER_VERSION_MINOR)
diff --git a/examples/drivers/windows/ottmp/hardware.hpp b/examples/drivers/windows/ottmp/hardware.hpp
new file mode 100644
index 0000000..bedf3d4
--- /dev/null
+++ b/examples/drivers/windows/ottmp/hardware.hpp
@@ -0,0 +1,153 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This module defines constants that describe physical characteristics and
+ *   limits of the underlying hardware.
+ */
+
+#pragma once
+
+#ifndef IF_TYPE_IEEE802154
+#define IF_TYPE_IEEE802154                  259 // IEEE 802.15.4 WPAN interface
+#define NdisPhysicalMediumNative802_15_4    (NDIS_PHYSICAL_MEDIUM)20
+#endif
+
+//
+// Link layer addressing
+// -----------------------------------------------------------------------------
+//
+
+// Number of bytes in a hardware address.  802.15.4 uses 8 byte addresses.
+#define NIC_MACADDR_SIZE                   8
+
+//
+// Frames
+// -----------------------------------------------------------------------------
+//
+
+#define HW_MAX_FRAME_SIZE                  1280
+
+//
+// Medium properties
+// -----------------------------------------------------------------------------
+//
+
+#define NIC_MEDIUM_TYPE                    NdisMediumIP
+
+// Claim to be 250kbps duplex
+#define KILOBITS_PER_SECOND                1000ULL
+#define MEGABITS_PER_SECOND                1000000ULL
+#define NIC_RECV_XMIT_SPEED                (250ULL*KILOBITS_PER_SECOND)
+
+//
+// Hardware limits
+// -----------------------------------------------------------------------------
+//
+
+// Max number of multicast addresses supported in hardware
+#define NIC_MAX_MCAST_LIST                 32
+
+// Maximum number of uncompleted sends that a single adapter will permit
+#define NIC_MAX_BUSY_SENDS                 1024
+
+// Maximum number of received packets that can be in the OS at any time
+// (Also known as the receive pool size)
+#define NIC_MAX_OUTSTANDING_RECEIVES       32
+
+
+//
+// Physical adapter properties
+// -----------------------------------------------------------------------------
+//
+
+// Change to your company name instead of using OpenThread
+#define NIC_VENDOR_DESC                    "OpenThread"
+
+// Highest byte is the NIC byte plus three vendor bytes. This is normally
+// obtained from the NIC.
+#define NIC_VENDOR_ID                      0x00FFFFFF
+
+#ifdef OTTMP_LEGACY
+#define NIC_SUPPORTED_FILTERS ( \
+                NDIS_PACKET_TYPE_DIRECTED    | \
+                NDIS_PACKET_TYPE_MULTICAST   | \
+                NDIS_PACKET_TYPE_BROADCAST   | \
+                NDIS_PACKET_TYPE_PROMISCUOUS | \
+                NDIS_PACKET_TYPE_ALL_MULTICAST)
+#else
+#define NIC_SUPPORTED_FILTERS (NET_PACKET_FILTER_TYPES_FLAGS) ( \
+                NET_PACKET_FILTER_TYPE_DIRECTED   | \
+                NET_PACKET_FILTER_TYPE_MULTICAST  | \
+                NET_PACKET_FILTER_TYPE_BROADCAST  | \
+                NET_PACKET_FILTER_TYPE_PROMISCUOUS | \
+                NET_PACKET_FILTER_TYPE_ALL_MULTICAST)
+#endif
+
+//
+// Specify a bitmask that defines optional properties of the NIC.
+// This miniport indicates receive with NdisMIndicateReceiveNetBufferLists
+// function.  Such a driver should set this NDIS_MAC_OPTION_TRANSFERS_NOT_PEND
+// flag.
+//
+// NDIS_MAC_OPTION_NO_LOOPBACK tells NDIS that NIC has no internal
+// loopback support so NDIS will manage loopbacks on behalf of
+// this driver.
+//
+// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA tells the protocol that
+// our receive buffer is not on a device-specific card. If
+// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA is not set, multi-buffer
+// indications are copied to a single flat buffer.
+//
+#define NIC_MAC_OPTIONS (\
+                NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \
+                NDIS_MAC_OPTION_TRANSFERS_NOT_PEND  | \
+                NDIS_MAC_OPTION_NO_LOOPBACK         | \
+                NDIS_MAC_OPTION_8021P_PRIORITY      | \
+                NDIS_MAC_OPTION_8021Q_VLAN)
+
+// NDIS 6.x miniports must support all counters in OID_GEN_STATISTICS.
+#define NIC_SUPPORTED_STATISTICS (\
+                NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV    | \
+                NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV   | \
+                NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV   | \
+                NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV              | \
+                NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS           | \
+                NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR              | \
+                NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT   | \
+                NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT  | \
+                NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT  | \
+                NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT             | \
+                NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR             | \
+                NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS          | \
+                NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV     | \
+                NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV    | \
+                NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV    | \
+                NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT    | \
+                NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT   | \
+                NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT)
diff --git a/examples/drivers/windows/ottmp/hdlc.cpp b/examples/drivers/windows/ottmp/hdlc.cpp
new file mode 100644
index 0000000..89dc907
--- /dev/null
+++ b/examples/drivers/windows/ottmp/hdlc.cpp
@@ -0,0 +1,343 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements an HDLC-lite encoder and decoder.
+ */
+
+#include "pch.hpp"
+#include "hdlc.tmh"
+
+/**
+ * FCS lookup table
+ */
+enum
+{
+    kInitFcs = 0xffff,  ///< Initial FCS value.
+    kGoodFcs = 0xf0b8,  ///< Good FCS value.
+};
+
+static bool
+hdlc_byte_needs_escape(UCHAR byte)
+{
+    switch (byte) 
+    {
+    case HdlcXOn:
+    case HdlcXOff:
+    case HdlcEscapeSequence:
+    case HdlcFlagSequence:
+    case HdlcSpecial:
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+/**
+ * This method updates an FCS.
+ *
+ * @param[in]  aFcs   The FCS to update.
+ * @param[in]  aByte  The input byte value.
+ *
+ * @returns The updated FCS.
+ */
+USHORT UpdateFcs(USHORT aFcs, UCHAR aByte)
+{
+    const USHORT sFcsTable[256] =
+    {
+        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+        0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+        0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+        0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+        0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+        0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+        0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+        0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+        0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+        0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+        0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+        0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+        0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+        0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+        0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+        0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+        0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+        0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+    };
+    return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff];
+}
+
+ULONG
+HdlcComputeEncodedByteLength(
+    _In_ UCHAR          aByte
+    )
+{
+    return hdlc_byte_needs_escape(aByte) ? 2 : 1;
+}
+
+ULONG
+HdlcComputeEncodedLength(
+    _In_reads_bytes_(DecodedBufferLength) 
+         PUCHAR         DecodedBuffer,
+    _In_ ULONG          DecodedBufferLength
+    )
+{
+    USHORT Fcs = kInitFcs;
+    ULONG Length = 2; // Start and end sequence
+    
+    for (ULONG i = 0; i < DecodedBufferLength; i++)
+    {
+        Length += HdlcComputeEncodedByteLength(DecodedBuffer[i]);
+        Fcs = UpdateFcs(Fcs, DecodedBuffer[i]);
+    }
+    
+    Fcs ^= 0xffff;
+    
+    Length += HdlcComputeEncodedByteLength(Fcs & 0xff);
+    Length += HdlcComputeEncodedByteLength(Fcs >> 8);
+
+    return Length;
+}
+
+bool
+HdlcEncodeByte(
+    _In_    UCHAR       aByte,
+    _Inout_ ULONG&      Offset,
+    _In_    ULONG       EncodedBufferLength,
+    _Out_writes_bytes_(EncodedBufferLength) 
+            PUCHAR      EncodedBuffer
+    )
+{
+    if (hdlc_byte_needs_escape(aByte))
+    {
+        if (Offset + 2 > EncodedBufferLength) return false;
+
+        EncodedBuffer[Offset++] = HdlcEscapeSequence;
+        EncodedBuffer[Offset++] = (aByte ^ 0x20);
+    }
+    else
+    {
+        if (Offset + 1 > EncodedBufferLength) return false;
+
+        EncodedBuffer[Offset++] = aByte;
+    }
+
+    return true;
+}
+
+_Success_(return == true)
+bool
+HdlcEncodeBuffer(
+    _In_reads_bytes_(DecodedBufferLength) 
+         PUCHAR         DecodedBuffer,
+    _In_ ULONG          DecodedBufferLength,
+    _Out_writes_bytes_(EncodedBufferLength) 
+         PUCHAR         EncodedBuffer,
+    _In_ ULONG          EncodedBufferLength
+    )
+{
+    USHORT Fcs = kInitFcs;
+    ULONG Offset = 0;
+    bool Complete = false;
+
+    if (Offset + 1 > EncodedBufferLength) goto error;
+    EncodedBuffer[Offset++] = HdlcFlagSequence;
+
+    for (ULONG i = 0; i < DecodedBufferLength; i++)
+    {
+        UCHAR aByte = DecodedBuffer[i];
+
+        if (!HdlcEncodeByte(aByte, Offset, EncodedBufferLength, EncodedBuffer)) {
+            goto error;
+        }
+
+        Fcs = UpdateFcs(Fcs, aByte);
+    }
+    
+    Fcs ^= 0xffff;
+    
+    if (!HdlcEncodeByte(Fcs & 0xff, Offset, EncodedBufferLength, EncodedBuffer) ||
+        !HdlcEncodeByte(Fcs >> 8,   Offset, EncodedBufferLength, EncodedBuffer)) {
+        goto error;
+    }
+    
+    if (Offset + 1 > EncodedBufferLength) goto error;
+    EncodedBuffer[Offset++] = HdlcFlagSequence;
+
+    NT_ASSERT(Offset == EncodedBufferLength);
+    Complete = true;
+
+error:
+
+    return Complete;
+}
+
+enum HdlcDecodeState
+{
+    kStateNoSync = 0,
+    kStateSync,
+    kStateEscaped,
+};
+
+_Success_(return == true)
+bool
+HdlcDecodeBuffer(
+    _In_reads_bytes_(*EncodedBufferLength) 
+            PUCHAR      EncodedBuffer,
+    _Inout_ PULONG      EncodedBufferLength,
+    _Inout_ PULONG      DecodedBufferLength,
+    _Out_writes_bytes_opt_(*DecodedBufferLength) 
+            PUCHAR      DecodedBuffer,
+    _Out_   bool*       IsGood
+    )
+{
+    UCHAR byte;
+    HdlcDecodeState state = kStateNoSync;
+    ULONG DecodedLength = 0;
+    USHORT Fcs = kInitFcs;
+
+    for (ULONG i = 0; i < *EncodedBufferLength; i++)
+    {
+        byte = EncodedBuffer[i];
+
+        switch (state)
+        {
+            case kStateNoSync:
+            {
+                if (byte == HdlcFlagSequence)
+                {
+                    if (i != 0)
+                    {
+                        // Set the output for junk length
+                        *EncodedBufferLength = i;
+                        
+                        // Set the output 'IsGood' flag based on FCS
+                        *IsGood = false;
+
+                        return true;
+                    }
+                    else
+                    {
+                        state = kStateSync;
+                    }
+                }
+                break;
+            }
+            case kStateSync:
+            {
+                switch (byte)
+                {
+                    case HdlcEscapeSequence:
+                    {
+                        state = kStateEscaped;
+                        break;
+                    }
+                    case HdlcFlagSequence:
+                    {
+                        // If it wasn't enough buffer to be a complete sequence, maybe we are
+                        // actually between sequences. Leave the trailing escape character.
+                        if (i < sizeof(USHORT) + 2)
+                        {
+                            // Set the output encoded buffer length
+                            *EncodedBufferLength = i;
+
+                            *IsGood = false;
+                        }
+                        else
+                        {
+                            // Set the output encoded buffer length
+                            *EncodedBufferLength = i + 1;
+
+                            // Set the decoded buffer length (subtract 2 for the FCS)
+                            *DecodedBufferLength = DecodedLength - 2;
+
+                            // Set the output 'IsGood' flag based on FCS
+                            *IsGood = (Fcs == kGoodFcs);
+                        }
+
+                        return true;
+                    }
+                    default:
+                    {
+                        Fcs = UpdateFcs(Fcs, byte);
+                        DecodedLength++;
+                        if (DecodedBuffer)
+                        {
+                            if (*DecodedBufferLength >= DecodedLength)
+                            {
+                                DecodedBuffer[DecodedLength - 1] = byte;
+                            }
+                        }
+                        break;
+                    }
+                }
+                break;
+            }
+            case kStateEscaped:
+            {
+                if (i + 1 < *EncodedBufferLength)
+                {
+                    byte ^= 0x20;
+                    Fcs = UpdateFcs(Fcs, byte);
+                    DecodedLength++;
+                    if (DecodedBuffer)
+                    {
+                        if (*DecodedBufferLength >= DecodedLength)
+                        {
+                            DecodedBuffer[DecodedLength - 1] = byte;
+                        }
+                    }
+
+                    state = kStateSync;
+                }
+                else
+                {
+                    // Not enough buffer
+                }
+                break;
+            }
+        }
+    }
+
+    return false;
+}
diff --git a/examples/drivers/windows/ottmp/hdlc.hpp b/examples/drivers/windows/ottmp/hdlc.hpp
new file mode 100644
index 0000000..cb73394
--- /dev/null
+++ b/examples/drivers/windows/ottmp/hdlc.hpp
@@ -0,0 +1,72 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for an HDLC-lite encoder and decoder.
+ */
+
+#pragma once
+
+enum
+{
+    HdlcXOn            = 0x11,
+    HdlcXOff           = 0x13,
+    HdlcFlagSequence   = 0x7e,  ///< HDLC Flag value
+    HdlcEscapeSequence = 0x7d,  ///< HDLC Escape value
+    HdlcSpecial        = 0xF8
+};
+
+ULONG
+HdlcComputeEncodedLength(
+    _In_reads_bytes_(DecodedBufferLength) 
+         PUCHAR         DecodedBuffer,
+    _In_ ULONG          DecodedBufferLength
+    );
+
+_Success_(return == true)
+bool
+HdlcEncodeBuffer(
+    _In_reads_bytes_(DecodedBufferLength) 
+         PUCHAR         DecodedBuffer,
+    _In_ ULONG          DecodedBufferLength,
+    _Out_writes_bytes_(EncodedBufferLength) 
+         PUCHAR         EncodedBuffer,
+    _In_ ULONG          EncodedBufferLength
+    );
+
+_Success_(return == true)
+bool
+HdlcDecodeBuffer(
+    _In_reads_bytes_(*EncodedBufferLength) 
+            PUCHAR      EncodedBuffer,
+    _Inout_ PULONG      EncodedBufferLength,
+    _Inout_ PULONG      DecodedBufferLength,
+    _Out_writes_bytes_opt_(*DecodedBufferLength) 
+            PUCHAR      DecodedBuffer,
+    _Out_   bool*       IsGood
+    );
diff --git a/examples/drivers/windows/ottmp/oid.cpp b/examples/drivers/windows/ottmp/oid.cpp
new file mode 100644
index 0000000..4e34c9a
--- /dev/null
+++ b/examples/drivers/windows/ottmp/oid.cpp
@@ -0,0 +1,408 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.hpp"
+#include "oid.tmh"
+
+template<typename _Datum>
+PAGED NDIS_STATUS RequestQuery( _In_ PNDIS_OID_REQUEST OidRequest, const _Datum & value );
+
+#define VERIFY_NDIS_OBJECT_HEADER(_header, _type, _revision, _size) \
+    (((_header).Type == _type) && \
+     ((_header).Revision >= _revision) && \
+     ((_header).Size >= _size))
+
+#define VERIFY_NDIS_OBJECT_HEADER_PTR(_header, _type, _revision, _size) \
+    (((_header)->Type == _type) && \
+     ((_header)->Revision >= _revision) && \
+     ((_header)->Size >= _size))
+
+#define VERIFY_NDIS_REQUEST_OBJECT_HEADER(_buffer, _type, _revision, _size) \
+    VERIFY_NDIS_OBJECT_HEADER_PTR(reinterpret_cast<PNDIS_OBJECT_HEADER>(_buffer), _type, _revision, _size)
+
+#define ASSIGN_NDIS_OBJECT_HEADER(_header, _type, _revision, _size) \
+    (_header).Type = _type; \
+    (_header).Revision = _revision; \
+    (_header).Size = _size; 
+
+#define ASSIGN_NDIS_OBJECT_HEADER_PTR(_header, _type, _revision, _size) \
+    (_header)->Type = _type; \
+    (_header)->Revision = _revision; \
+    (_header)->Size = _size; 
+
+//
+// List of Supported OIDs.
+//
+NDIS_OID NICSupportedOids[] =
+{
+    // General
+    OID_GEN_CURRENT_LOOKAHEAD,
+    OID_GEN_CURRENT_PACKET_FILTER,
+    OID_GEN_INTERRUPT_MODERATION,
+    OID_GEN_LINK_PARAMETERS,   // TODO: Mandatory Set
+    OID_GEN_MAXIMUM_TOTAL_SIZE,
+    OID_GEN_RCV_OK,
+    OID_GEN_RECEIVE_BLOCK_SIZE,
+    OID_GEN_RECEIVE_BUFFER_SPACE,
+    OID_GEN_STATISTICS,
+    OID_GEN_TRANSMIT_BLOCK_SIZE,
+    OID_GEN_TRANSMIT_BUFFER_SPACE,
+    OID_GEN_VENDOR_DRIVER_VERSION,
+    OID_GEN_VENDOR_DESCRIPTION,
+    OID_GEN_VENDOR_ID,
+    OID_GEN_XMIT_OK,
+    OID_GEN_LINK_PARAMETERS,    // TODO: Mandatory Set
+
+    // 802.3 Specific
+    OID_802_3_CURRENT_ADDRESS,
+    OID_802_3_PERMANENT_ADDRESS,
+    OID_802_3_MULTICAST_LIST,
+    OID_802_3_MAXIMUM_LIST_SIZE,
+    OID_802_3_RCV_ERROR_ALIGNMENT,
+    OID_802_3_XMIT_ONE_COLLISION,
+    OID_802_3_XMIT_MORE_COLLISIONS,
+
+    // PnP Stuff
+    OID_PNP_CAPABILITIES,                // Optional
+    OID_PNP_QUERY_POWER,                 // Optional
+    
+    // OpenThread Stuff
+    OID_OT_CAPABILITIES,
+};
+
+const ULONG SizeOfNICSupportedOids = sizeof(NICSupportedOids);
+
+template<typename _Datum>
+PAGED
+NDIS_STATUS RequestQuery( _In_ PNDIS_OID_REQUEST OidRequest, const _Datum & value )
+{
+    PAGED_CODE();
+    NT_ASSERT( (OidRequest->RequestType == NdisRequestQueryInformation) ||
+        (OidRequest->RequestType == NdisRequestQueryStatistics) );
+
+    if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof( _Datum ))
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( _Datum );
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
+        return NDIS_STATUS_INVALID_LENGTH;
+    }
+    else
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( _Datum );
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( _Datum );
+        _Datum * pData = reinterpret_cast<_Datum *>(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+        *pData = value;
+        return NDIS_STATUS_SUCCESS;
+    }
+}
+
+PAGED
+NDIS_STATUS RequestQuery32or64( _In_ PNDIS_OID_REQUEST OidRequest, const ULONG64 value )
+{
+    PAGED_CODE();
+    NT_ASSERT( (OidRequest->RequestType == NdisRequestQueryInformation) ||
+        (OidRequest->RequestType == NdisRequestQueryStatistics) );
+
+    if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof( ULONG64 ))
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG64 );
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( ULONG64 );
+        PULONG64 pData = reinterpret_cast<PULONG64>(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+        *pData = value;
+        return NDIS_STATUS_SUCCESS;
+    }
+    else if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof( ULONG ))
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG );
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( ULONG );
+        PULONG pData = reinterpret_cast<PULONG>(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+        *pData = (ULONG)value;
+        return NDIS_STATUS_SUCCESS;
+    }
+    else
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( ULONG64 );
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
+        return NDIS_STATUS_INVALID_LENGTH;
+    }
+}
+
+PAGED
+NDIS_STATUS RequestQueryThreadCapabilities(_In_ PNDIS_OID_REQUEST OidRequest)
+{
+    PAGED_CODE();
+
+    OT_CAPABILITIES caps = { 0 };
+    ASSIGN_NDIS_OBJECT_HEADER(caps.Header, NDIS_OBJECT_TYPE_DEFAULT, OT_CAPABILITIES_REVISION_1, SIZEOF_OT_CAPABILITIES_REVISION_1);
+    caps.MiniportMode = OT_MP_MODE_THREAD;    // Thread Tunnel mode
+    
+    if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < SIZEOF_OT_CAPABILITIES_REVISION_1)
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = SIZEOF_OT_CAPABILITIES_REVISION_1;
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
+        return NDIS_STATUS_INVALID_LENGTH;
+    }
+    else
+    {
+        OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = SIZEOF_OT_CAPABILITIES_REVISION_1;
+        OidRequest->DATA.QUERY_INFORMATION.BytesWritten = SIZEOF_OT_CAPABILITIES_REVISION_1;
+        memcpy(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, &caps, SIZEOF_OT_CAPABILITIES_REVISION_1);
+        return NDIS_STATUS_SUCCESS;
+    }
+}
+
+PAGED
+NDIS_STATUS RequestQueryGenStatistics(_In_ PNDIS_OID_REQUEST OidRequest, _In_ POTTMP_ADAPTER_CONTEXT AdapterContext)
+{
+    PAGED_CODE();
+
+    NDIS_STATISTICS_INFO statistics = { 0 };
+    ASSIGN_NDIS_OBJECT_HEADER(statistics.Header, NDIS_OBJECT_TYPE_DEFAULT, NDIS_SIZEOF_STATISTICS_INFO_REVISION_1, NDIS_STATISTICS_INFO_REVISION_1);
+
+    statistics.SupportedStatistics = NIC_SUPPORTED_STATISTICS;
+
+    /* Bytes in */
+    statistics.ifHCInOctets = AdapterContext->BytesRxDirected +
+                               AdapterContext->BytesRxMulticast +
+                               AdapterContext->BytesRxBroadcast;
+
+    statistics.ifHCInUcastOctets = AdapterContext->BytesRxDirected;            
+    statistics.ifHCInMulticastOctets = AdapterContext->BytesRxMulticast;            
+    statistics.ifHCInBroadcastOctets = AdapterContext->BytesRxBroadcast;
+
+    /* Packets in */
+    statistics.ifHCInUcastPkts = AdapterContext->FramesRxDirected;            
+    statistics.ifHCInMulticastPkts = AdapterContext->FramesRxMulticast;            
+    statistics.ifHCInBroadcastPkts = AdapterContext->FramesRxBroadcast;
+
+    /* Errors in */
+    statistics.ifInErrors = AdapterContext->RxCrcErrors +
+                             AdapterContext->RxAlignmentErrors +
+                             AdapterContext->RxDmaOverrunErrors +
+                             AdapterContext->RxRuntErrors;
+
+    statistics.ifInDiscards = AdapterContext->RxResourceErrors;            
+
+    /* Bytes out */
+    statistics.ifHCOutOctets = AdapterContext->BytesTxDirected +
+                                AdapterContext->BytesTxMulticast +
+                                AdapterContext->BytesTxBroadcast;
+
+    statistics.ifHCOutUcastOctets = AdapterContext->BytesTxDirected;            
+    statistics.ifHCOutMulticastOctets = AdapterContext->BytesTxMulticast;            
+    statistics.ifHCOutBroadcastOctets = AdapterContext->BytesTxBroadcast;
+
+    /* Packets out */
+    statistics.ifHCOutUcastPkts = AdapterContext->FramesTxDirected;            
+    statistics.ifHCOutMulticastPkts = AdapterContext->FramesTxMulticast;            
+    statistics.ifHCOutBroadcastPkts = AdapterContext->FramesTxBroadcast;
+
+    /* Errors out */
+    statistics.ifOutErrors = AdapterContext->TxAbortExcessCollisions +
+                              AdapterContext->TxDmaUnderrun +
+                              AdapterContext->TxLostCRS +
+                              AdapterContext->TxLateCollisions+
+                              AdapterContext->TransmitFailuresOther;
+
+    statistics.ifOutDiscards = 0ULL;
+    
+    return RequestQuery(OidRequest, statistics);
+}
+
+PAGED 
+_Use_decl_annotations_
+NDIS_STATUS
+MPOidRequest(
+    _In_  NDIS_HANDLE             MiniportAdapterContext,
+    _In_  PNDIS_OID_REQUEST       OidRequest
+    )
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)MiniportAdapterContext;
+    NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED;
+    bool fFailExpected = false;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    switch (OidRequest->RequestType)
+    {
+    case NdisRequestSetInformation:
+        switch (OidRequest->DATA.QUERY_INFORMATION.Oid)
+        {
+        case OID_802_3_MULTICAST_LIST:
+            // TODO: Check with Jeffrey how to better handle this
+            status = NDIS_STATUS_MULTICAST_FULL;
+            fFailExpected = true;
+            break;
+
+            //
+            // Fake it until we make it :)
+            // We can't bind unless we report success for these OIDs
+            //
+        case OID_GEN_CURRENT_PACKET_FILTER:
+        case OID_PM_ADD_WOL_PATTERN:
+        case OID_PM_REMOVE_WOL_PATTERN:
+        case OID_GEN_CURRENT_LOOKAHEAD:
+            // TODO: Implement this
+            status = NDIS_STATUS_SUCCESS;
+            break;
+
+            // Explicitly not supported
+        case OID_GEN_INTERRUPT_MODERATION:
+            status = NDIS_STATUS_NOT_SUPPORTED;
+            fFailExpected = true;
+            break;
+
+            // Unknown
+        default:
+            status = NDIS_STATUS_NOT_SUPPORTED;
+            break;
+        }
+        break;
+    case NdisRequestQueryInformation:
+    case NdisRequestQueryStatistics:
+        switch (OidRequest->DATA.QUERY_INFORMATION.Oid)
+        {
+        case OID_GEN_INTERRUPT_MODERATION:
+            {
+                static const NDIS_INTERRUPT_MODERATION_PARAMETERS nimp = {
+                    { NDIS_OBJECT_TYPE_DEFAULT, NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 },
+                    0,
+                    NdisInterruptModerationNotSupported };
+                status = RequestQuery( OidRequest, nimp );
+                break;
+            }
+
+        case OID_GEN_RCV_OK:
+            status = RequestQuery32or64( OidRequest, AdapterContext->FramesRxBroadcast
+                     + AdapterContext->FramesRxMulticast
+                     + AdapterContext->FramesRxDirected );
+            break;
+
+        case OID_GEN_MAXIMUM_TOTAL_SIZE:
+        case OID_GEN_TRANSMIT_BLOCK_SIZE:
+        case OID_GEN_RECEIVE_BLOCK_SIZE:
+            status = RequestQuery<ULONG>( OidRequest, HW_MAX_FRAME_SIZE );
+            break;
+
+        case OID_GEN_RECEIVE_BUFFER_SPACE:
+            status = RequestQuery<ULONG>( OidRequest, HW_MAX_FRAME_SIZE * AdapterContext->ulMaxBusyRecvs );
+            break;
+
+        case OID_GEN_STATISTICS:
+            status = RequestQueryGenStatistics(OidRequest, AdapterContext);
+            break;
+
+        case OID_GEN_TRANSMIT_BUFFER_SPACE:
+            status = RequestQuery<ULONG>( OidRequest, HW_MAX_FRAME_SIZE * AdapterContext->ulMaxBusySends );
+            break;
+
+        case OID_GEN_VENDOR_DESCRIPTION:
+        case OID_GEN_VENDOR_DRIVER_VERSION:
+        case OID_GEN_VENDOR_ID:
+            if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(NIC_VENDOR_DESC))
+            {
+                OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( NIC_VENDOR_DESC );
+                OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
+                status = NDIS_STATUS_INVALID_LENGTH;
+            }
+            else
+            {
+                OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof( NIC_VENDOR_DESC );
+                OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof( NIC_VENDOR_DESC );
+                memcpy(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, NIC_VENDOR_DESC, sizeof(NIC_VENDOR_DESC));
+                status = NDIS_STATUS_SUCCESS;
+            }
+            break;
+
+        case OID_GEN_XMIT_OK:
+            status = RequestQuery32or64( OidRequest, AdapterContext->FramesTxBroadcast
+                     + AdapterContext->FramesTxMulticast
+                     + AdapterContext->FramesTxDirected );
+            break;
+
+        case OID_802_3_CURRENT_ADDRESS:
+            status = RequestQuery( OidRequest, AdapterContext->ExtendedAddress );
+            break;
+
+        case OID_802_3_PERMANENT_ADDRESS:
+            status = RequestQuery( OidRequest, AdapterContext->ExtendedAddress );
+            break;
+
+        case OID_PNP_CAPABILITIES:
+            // We do not support low power
+            status = NDIS_STATUS_NOT_SUPPORTED;
+            fFailExpected = true;
+            break;
+
+        case OID_PNP_QUERY_POWER:
+            status = NDIS_STATUS_NOT_ACCEPTED;
+            fFailExpected = true;
+            break;
+
+        case OID_OT_CAPABILITIES:
+            status = RequestQueryThreadCapabilities( OidRequest );
+            break;
+
+        default:
+            status = NDIS_STATUS_NOT_SUPPORTED;
+            break;
+        }
+        break;
+    case NdisRequestMethod:
+        break;
+    default:
+        status = NDIS_STATUS_INVALID_OID;
+        break;
+
+    }
+    UNREFERENCED_PARAMETER( OidRequest );
+
+    if (!fFailExpected && (status != NDIS_STATUS_SUCCESS))
+    {
+        LogFuncExitMsg(DRIVER_DEFAULT, " Type:%u Oid:%u Status:%!NDIS_STATUS!", OidRequest->RequestType, OidRequest->DATA.QUERY_INFORMATION.Oid, status);
+    }
+    else
+    {
+        // By not using LogFuncExitNDIS, this won't get auto-promoted to a warning on non-0 statuses
+        LogFuncExitMsg(DRIVER_DEFAULT, " Type:%u Oid:%u Status:%!NDIS_STATUS!", OidRequest->RequestType, OidRequest->DATA.QUERY_INFORMATION.Oid, status);
+    }
+    return status;
+}
+
+_Use_decl_annotations_
+VOID
+MPCancelOidRequest(
+    _In_  NDIS_HANDLE             /* MiniportAdapterContext */,
+    _In_  PVOID                   /* pRequestId */
+)
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
diff --git a/examples/drivers/windows/ottmp/oid.hpp b/examples/drivers/windows/ottmp/oid.hpp
new file mode 100644
index 0000000..3c9b122
--- /dev/null
+++ b/examples/drivers/windows/ottmp/oid.hpp
@@ -0,0 +1,73 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   Header file for the routines related to OIDs
+ */
+
+extern NDIS_OID NICSupportedOids[];
+extern const ULONG SizeOfNICSupportedOids;
+
+#ifdef OTTMP_LEGACY
+
+PAGED MINIPORT_OID_REQUEST MPOidRequest;
+MINIPORT_CANCEL_OID_REQUEST MPCancelOidRequest;
+
+#else
+
+EVT_NET_REQUEST_QUERY_DATA EvtQueryUlong;
+EVT_NET_REQUEST_QUERY_DATA EvtQueryGenVendorDescription;
+EVT_NET_REQUEST_QUERY_DATA EvtQueryAddress;
+EVT_NET_REQUEST_QUERY_DATA EvtQueryGenStatistics;
+EVT_NET_REQUEST_QUERY_DATA EvtQueryGenInterruptModeration;
+EVT_NET_REQUEST_QUERY_DATA EvtQueryGenXmitRcvOk;
+
+EVT_NET_REQUEST_QUERY_DATA EvtQueryThreadCapabilities;
+
+EVT_NET_REQUEST_DEFAULT_SET_DATA EvtDefaultSetData;
+
+NDIS_STATUS
+AdapterSetInformation(
+    _In_  POTTMP_ADAPTER_CONTEXT    AdapterContext,
+    _In_  PNDIS_OID_REQUEST         NdisSetRequest);
+
+NDIS_STATUS
+AdapterQueryInformation(
+    _In_    POTTMP_ADAPTER_CONTEXT  AdapterContext,
+    _Inout_ PNDIS_OID_REQUEST       NdisQueryRequest
+    );
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+AddDefaultRequestSequentialQueue(
+    _In_    POTTMP_ADAPTER_CONTEXT AdapterContext
+    );
+
+
+#endif
diff --git a/examples/drivers/windows/ottmp/ottmp.inf b/examples/drivers/windows/ottmp/ottmp.inf
new file mode 100644
index 0000000..3a91afa
--- /dev/null
+++ b/examples/drivers/windows/ottmp/ottmp.inf
@@ -0,0 +1,121 @@
+;
+;  Copyright (c) 2016, The OpenThread Authors.
+;  All rights reserved.
+;
+;  Redistribution and use in source and binary forms, with or without
+;  modification, are permitted provided that the following conditions are met:
+;  1. Redistributions of source code must retain the above copyright
+;     notice, this list of conditions and the following disclaimer.
+;  2. Redistributions in binary form must reproduce the above copyright
+;     notice, this list of conditions and the following disclaimer in the
+;     documentation and/or other materials provided with the distribution.
+;  3. Neither the name of the copyright holder nor the
+;     names of its contributors may be used to endorse or promote products
+;     derived from this software without specific prior written permission.
+;
+;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+;  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+;  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+;  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+;  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+;  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+;  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+;  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;  POSSIBILITY OF SUCH DAMAGE.
+;
+
+[Version]
+Signature           = "$Windows NT$"
+Class               = Net
+ClassGUID           = {4d36e972-e325-11ce-bfc1-08002be10318}
+Provider            = %OpenThread%
+DriverVer           = 
+PnpLockDown         = 1
+CatalogFile         = ottmp.cat
+
+[Manufacturer]
+%OpenThread%        = OpenThread,NT$ARCH$
+
+[OpenThread.NT$ARCH$]
+%ottmp.DeviceDesc%  = ottmp.ndi, root\ottmp ; Root enumerated
+
+;-------------------------------------------------------------------------------
+; OpenThread Tunnel Thread Adapter
+;-------------------------------------------------------------------------------
+[ottmp.ndi]
+Characteristics     = 0x4 ; NCF_PHYSICAL
+*IfType             = 259 ; IF_TYPE_IEEE802154
+*MediaType          = 19  ; NdisMediumIP
+*PhysicalMediaType  = 20  ; NdisPhysicalMediumNative802_15_4
+*IfConnectorPresent = 0
+*AccessType         = 2   ; NET_IF_ACCESS_BROADCAST
+*ConnectionType     = 1   ; NET_IF_CONNECTION_PASSIVE
+*DirectionType      = 0   ; NET_IF_DIRECTION_SENDRECEIVE
+AddReg              = ottmp.Reg
+CopyFiles           = ottmp.CopyFiles
+
+[ottmp.ndi.Services]
+AddService          = ottmp, 2, ottmp.Service, ottmp.EventLog
+
+;-------------------------------------------------------------------------------
+; OpenThread Tunnel Miniport Common
+;-------------------------------------------------------------------------------
+[ottmp.Reg]
+HKR,    ,                         BusNumber,           0, "0" 
+HKR, Ndi,                         Service,             0, "ottmp"
+HKR, Ndi\Interfaces,              UpperRange,          0, "flpp6"
+HKR, Ndi\Interfaces,              LowerRange,          0, "802.15.4"
+
+;-------------------------------------------------------------------------------
+; WDF Section
+;-------------------------------------------------------------------------------
+[ottmp.ndi.Wdf]
+KmdfService         = ottmp, ottmp.wdfsect
+
+[ottmp.wdfsect]
+KmdfLibraryVersion  = $KMDFVERSION$
+
+;-------------------------------------------------------------------------------
+; Driver and Service Section
+;-------------------------------------------------------------------------------
+[ottmp.CopyFiles]
+ottmp.sys,,,2
+
+[ottmp.Service]
+DisplayName        = %ottmp.Service.DispName%
+ServiceType        = 1 ;%SERVICE_KERNEL_DRIVER%
+StartType          = 3 ;%SERVICE_DEMAND_START%
+ErrorControl       = 1 ;%SERVICE_ERROR_NORMAL%
+ServiceBinary      = %12%\ottmp.sys
+LoadOrderGroup     = NDIS
+AddReg             = TextModeFlags.Reg
+Description        = %ottmp.DeviceDesc%
+
+[ottmp.EventLog]
+AddReg             = ottmp.AddEventLog.Reg
+
+[ottmp.AddEventLog.Reg]
+HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll"
+HKR, , TypesSupported,   0x00010001, 7
+
+[TextModeFlags.Reg]
+HKR, , TextModeFlags,    0x00010001, 0x0001
+
+[SourceDisksNames]
+1 = %ottmp.DeviceDesc%,,,""
+
+[SourceDisksFiles]
+ottmp.sys = 1,,
+
+[DestinationDirs]
+ottmp.CopyFiles = 12
+
+;-------------------------------------------------------------------------------
+; Localizable Strings
+;-------------------------------------------------------------------------------
+[Strings]
+OpenThread              = "OpenThread"                      
+ottmp.DeviceDesc        = "OpenThread Tunnel Thread Adapter"
+ottmp.Service.DispName  = "OpenThread Tunnel Miniport"
diff --git a/examples/drivers/windows/ottmp/ottmp.rc b/examples/drivers/windows/ottmp/ottmp.rc
new file mode 100644
index 0000000..26a7c38
--- /dev/null
+++ b/examples/drivers/windows/ottmp/ottmp.rc
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+ 
+#include <windows.h>
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define    VER_FILETYPE    VFT_DRV
+/* possible values:        VFT_UNKNOWN
+                VFT_APP
+                VFT_DLL
+                VFT_DRV
+                VFT_FONT
+                VFT_VXD
+                VFT_STATIC_LIB
+*/
+#define    VER_FILESUBTYPE    VFT2_DRV_NETWORK
+/* possible values        VFT2_UNKNOWN
+                VFT2_DRV_PRINTER
+                VFT2_DRV_KEYBOARD
+                VFT2_DRV_LANGUAGE
+                VFT2_DRV_DISPLAY
+                VFT2_DRV_MOUSE
+                VFT2_DRV_NETWORK
+                VFT2_DRV_SYSTEM
+                VFT2_DRV_INSTALLABLE
+                VFT2_DRV_SOUND
+                VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR     "OpenThread Tunnel Miniport"
+#define VER_INTERNALNAME_STR        "OTTMP.SYS"
+
+
+#include "common.ver"   // NT5.0 version file.
+
diff --git a/examples/drivers/windows/ottmp/pch.hpp b/examples/drivers/windows/ottmp/pch.hpp
new file mode 100644
index 0000000..4c79167
--- /dev/null
+++ b/examples/drivers/windows/ottmp/pch.hpp
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+ 
+//
+// system headers
+//
+extern "C" {
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <ntverp.h>
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <ntintsafe.h>
+#include <ndis.h>
+#include <WppRecorder.h>
+#include <wdf.h>
+#ifndef OTTMP_LEGACY
+#include <netadaptercx.h>
+#endif
+#include <WdfMiniport.h>
+#include <wdm.h>
+#include <ntddser.h>
+}
+
+// Intellisense definition for DbgRaiseAssertionFailure because for some reason Visual Studio can't
+// find it.
+#ifdef __INTELLISENSE__
+#define DbgRaiseAssertionFailure() ((void) 0)
+#endif
+
+#include "otOID.h"
+ 
+#define CODE_SEG(seg) __declspec(code_seg(seg))
+#define INITCODE CODE_SEG("INIT")  
+#define PAGED  CODE_SEG("PAGE")
+
+typedef struct _OTTMP_ADAPTER_CONTEXT *POTTMP_ADAPTER_CONTEXT;
+typedef struct _OTTMP_DEVICE_CONTEXT *POTTMP_DEVICE_CONTEXT;
+
+#include "hardware.hpp"
+#include "hdlc.hpp"
+#include "driver.hpp"
+#include "adapter.hpp"
+#include "device.hpp"
+#include "serial.hpp"
+#include "oid.hpp"
+#include "openthread/platform/logging-windows.h"
diff --git a/examples/drivers/windows/ottmp/serial.cpp b/examples/drivers/windows/ottmp/serial.cpp
new file mode 100644
index 0000000..ff85d7e
--- /dev/null
+++ b/examples/drivers/windows/ottmp/serial.cpp
@@ -0,0 +1,1308 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pch.hpp"
+#include "serial.tmh"
+
+PAGED
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialInitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+/*++
+Routine Description:
+
+    SerialInitialize attempts to find and open the first COM port available, with
+    the assumption that it should be for the Thread device.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+
+Return Value:
+
+    NTSTATUS    - A failure here will indicate the serial COM port was not able
+                  to be opened.
+--*/
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+    PWSTR SymbolicLinkList = NULL;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    PAGED_CODE();
+    
+    do
+    {
+        WDF_OBJECT_ATTRIBUTES attr = { 0 };
+        WDF_WORKITEM_CONFIG config = { 0 };
+
+        //
+        // Send Queue Variables
+        //
+
+        InitializeListHead(&AdapterContext->SendQueue);
+        AdapterContext->SendQueueRunning = false;
+
+        WDF_OBJECT_ATTRIBUTES_INIT(&attr);
+        attr.ParentObject = AdapterContext->Device;
+        status = WdfSpinLockCreate(&attr, &AdapterContext->SendLock);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfSpinLockCreate(lockSend) failed %!STATUS!", status);
+            break;
+        }
+
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO);
+        attr.ParentObject = AdapterContext->Device;
+        WDF_WORKITEM_CONFIG_INIT(&config, SerialSendLoop);
+
+        status = WdfWorkItemCreate(&config, &attr, &AdapterContext->SendWorkItem);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialSendLoop) failed %!STATUS!", status);
+            break;
+        }
+        GetWdfDeviceInfo(AdapterContext->SendWorkItem)->AdapterContext = AdapterContext;
+
+        //
+        // Receive Variables
+        //
+
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO);
+        attr.ParentObject = AdapterContext->Device;
+        WDF_WORKITEM_CONFIG_INIT(&config, SerialRecvLoop);
+
+        status = WdfWorkItemCreate(&config, &attr, &AdapterContext->RecvWorkItem);
+        
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfWorkItemCreate(SerialRecvLoop) failed %!STATUS!", status);
+            break;
+        }
+
+        GetWdfDeviceInfo(AdapterContext->RecvWorkItem)->AdapterContext = AdapterContext;
+
+        // Query the system for device with SERIAL interface
+        status =
+            IoGetDeviceInterfaces(
+                &GUID_DEVINTERFACE_COMPORT,
+                NULL,
+                0,
+                &SymbolicLinkList   // List of symbolic names; separate by NULL, EOL with NULL+NULL.
+            );
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IoGetDeviceInterfaces failed %!STATUS!", status);
+            break;
+        }
+
+        // Make sure there is a COM port found
+        NT_ASSERT(SymbolicLinkList);
+        if (*SymbolicLinkList == NULL) {
+            status = STATUS_DEVICE_NOT_CONNECTED;
+            LogError(DRIVER_DEFAULT, "No COM ports found!");
+            break;
+        }
+
+#if DBG
+        for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1)
+        {
+            LogVerbose(DRIVER_DEFAULT, "Symbolic Name found: %ws", sym);
+        }
+#endif
+
+        // Try to open each serial port until we get that one works or we exhaust them all
+        for (PCWSTR sym = SymbolicLinkList; *sym != NULL; sym += wcslen(sym) + 1)
+        {
+            // Initialize the target
+            status = SerialInitializeTarget(AdapterContext, sym);
+
+            // Break on success
+            if (NT_SUCCESS(status)) {
+                break;
+            }
+        }
+
+    } while (false);
+
+    // Clean up on failure
+    if (!NT_SUCCESS(status)) {
+        SerialUninitialize(AdapterContext);
+    }
+
+    if (SymbolicLinkList) {
+        ExFreePool(SymbolicLinkList);
+        SymbolicLinkList = NULL;
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialUninitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+/*++
+Routine Description:
+
+    SerialUninitialize cleans up any cached Wdf IoTarget created from SerialInitialize.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    PAGED_CODE();
+
+    // TODO - Clean up work item
+
+    // TODO - Clean up send queue
+
+    SerialUninitializeTarget(AdapterContext);
+
+    if (AdapterContext->RecvWorkItem) {
+        WdfWorkItemFlush(AdapterContext->RecvWorkItem);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+PAGED
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialInitializeTarget(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ PCWSTR                     TargetName
+)
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+    WDFIOTARGET tempTarget = WDF_NO_HANDLE;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    do
+    {
+        DECLARE_UNICODE_STRING_SIZE(PortName, 64); // Maximum name length of the device path to a serial port
+        WDF_IO_TARGET_OPEN_PARAMS openParams = { 0 };
+        WDF_OBJECT_ATTRIBUTES attr = { 0 };
+
+        // Create the Wdf IoTarget
+        status = WdfIoTargetCreate(AdapterContext->Device, WDF_NO_OBJECT_ATTRIBUTES, &tempTarget);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfIoTargetCreate failed %!STATUS!", status);
+            break;
+        }
+
+        // Try the COM port
+        LogInfo(DRIVER_DEFAULT, "Opening device: %ws", TargetName);
+        RtlInitUnicodeString(&PortName, TargetName);
+        WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
+            &openParams,
+            &PortName,
+            GENERIC_READ | GENERIC_WRITE);
+
+        // Open the port on the target
+        status = WdfIoTargetOpen(tempTarget, &openParams);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfIoTargetOpen(%wZ) failed %!STATUS!", &PortName, status);
+            break;
+        }
+
+        AdapterContext->WdfIoTarget = tempTarget;
+        tempTarget = WDF_NO_HANDLE;
+
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, WDF_DEVICE_INFO);
+        attr.ParentObject = AdapterContext->Device;
+
+        status = WdfRequestCreate(&attr, AdapterContext->WdfIoTarget, &AdapterContext->RecvReadRequest);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "WdfRequestCreate failed %!STATUS!", status);
+            break;
+        }
+
+        // Try to configure the target
+        status = SerialConfigure(AdapterContext);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "SerialConfigure failed %!STATUS!", status);
+            break;
+        }
+
+    } while (false);
+
+    // Clean up on failure
+    if (!NT_SUCCESS(status)) {
+        SerialUninitializeTarget(AdapterContext);
+    }
+
+    if (tempTarget) {
+        WdfIoTargetClose(tempTarget);
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialUninitializeTarget(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+)
+/*++
+Routine Description:
+
+    SerialUninitializeTarget cleans up any cached Wdf IoTarget created from SerialInitializeTarget.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+--*/
+{
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    if (AdapterContext->WdfIoTarget) {
+        //
+        // WdfIoTargetStop will cancel all the outstanding I/O and wait
+        // for them to complete before returning. WdfIoTargetStop  with the
+        // action type WdfIoTargetCancelSentIo can be called at IRQL PASSIVE_LEVEL only.
+        //
+        WdfIoTargetStop(AdapterContext->WdfIoTarget, WdfIoTargetCancelSentIo);
+        WdfIoTargetClose(AdapterContext->WdfIoTarget);
+        AdapterContext->WdfIoTarget = NULL;
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_Must_inspect_result_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+FORCEINLINE
+SerialSendIoctl(
+    _In_     POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_     ULONG                      IoctlCode,
+    _In_opt_ PWDF_REQUEST_SEND_OPTIONS  RequestOptions = NULL,
+    _In_opt_ PWDF_MEMORY_DESCRIPTOR     InputBuffer = WDF_NO_HANDLE,
+    _In_opt_ PWDF_MEMORY_DESCRIPTOR     OutputBuffer = WDF_NO_HANDLE,
+    _Out_opt_ PULONG_PTR                BytesReturned = NULL
+    )
+/*++
+Routine Description:
+
+    Helper/Wrapper function for WdfIoTargetSendIoctlSynchronously.
+
+--*/
+{
+    return WdfIoTargetSendIoctlSynchronously(
+            AdapterContext->WdfIoTarget, 
+            WDF_NO_HANDLE, 
+            IoctlCode, 
+            InputBuffer, 
+            OutputBuffer, 
+            RequestOptions, 
+            BytesReturned);
+}
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialConfigure(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+/*++
+Routine Description:
+
+    SerialInitialize attempts to find and open the first COM port available, with
+    the assumption that it should be for the Thread device.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+
+Return Value:
+
+    NTSTATUS    - A failure here will indicate the serial COM port was not able
+                  to be configured as desired.
+--*/
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+    WDF_MEMORY_DESCRIPTOR inputDesc;
+    WDF_REQUEST_SEND_OPTIONS wrso = {
+        sizeof(WDF_REQUEST_SEND_OPTIONS),
+        WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+        WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete
+    };
+
+    LogFuncEntry(DRIVER_DEFAULT);
+    
+    PAGED_CODE();
+    
+    do
+    {
+        // Initial reset of the device
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_RESET_DEVICE, &wrso);
+        
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_RESET_DEVICE failed %!STATUS!", status);
+            break;
+        }
+
+        // 8 bits, no parity, 1 stop bit
+        {
+            const SERIAL_LINE_CONTROL slc = { STOP_BIT_1, NO_PARITY, 8 };
+            WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&slc, sizeof(slc));
+
+            status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_LINE_CONTROL, &wrso, &inputDesc);
+            
+            if (!NT_SUCCESS(status)) {
+                LogError(DRIVER_DEFAULT,  "IOCTL_SERIAL_SET_LINE_CONTROL failed %!STATUS!", status );
+                break;
+            }
+        }
+
+        // Xon and Xoff characters
+        {
+            const SERIAL_CHARS sc = { 0, 0, 0, 0, 0x11, 0x13 };
+            WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sc, sizeof(sc));
+
+            status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_CHARS, &wrso, &inputDesc);
+            
+            if (!NT_SUCCESS(status)) {
+                LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_CHARS failed %!STATUS!", status);
+                break;
+            }
+        }
+
+        // Baud rate
+        {
+            const SERIAL_BAUD_RATE sbr = { 115200 };
+            WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sbr, sizeof(sbr));
+
+            status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_BAUD_RATE, &wrso, &inputDesc);
+            
+            if (!NT_SUCCESS(status)) {
+                LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_BAUD_RATE failed %!STATUS!", status);
+                break;
+            }
+        }
+        
+        /*{
+            // Only send if CTS is set, Set RTS before sending
+            const SERIAL_HANDFLOW shf = 
+            {
+                SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL,
+                MAX_SPINEL_COMMAND_LENGTH, MAX_SPINEL_COMMAND_LENGTH
+            };
+            WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&shf, sizeof(shf));
+            
+            status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_HANDFLOW, &wrso, &inputDesc);
+            
+            if (!NT_SUCCESS(status)) {
+                LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_HANDFLOW failed %!STATUS!", status);
+                // break; Ignore for now
+            }
+        }
+
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_XON, &wrso);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_XON failed %!STATUS!", status);
+            break;
+        }
+
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_RTS, &wrso);
+        
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_RTS failed %!STATUS!", status);
+            break;
+        }
+
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_DTR, &wrso);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_DTR failed %!STATUS!", status);
+            break;
+        }*/
+
+        {
+            const SERIAL_TIMEOUTS sto = {
+                1, 0, 0,      // On read, only timeout if more than 1ms *between* bytes (wait forever for first byte)
+                1, 10         // Write times out after (1ms * n-bytes) + (10ms)
+            };
+            WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&sto, sizeof(sto));
+
+            status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_SET_TIMEOUTS, &wrso, &inputDesc);
+        
+            if (!NT_SUCCESS(status)) {
+                LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_SET_TIMEOUTS failed %!STATUS!", status);
+                break;
+            }
+        }
+
+        status = SerialFlushAndCheckStatus(AdapterContext);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "SerialFlushAndCheckStatus failed %!STATUS!", status);
+            break;
+        }
+
+    } while (false);
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SerialCheckStatus(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ bool                       DataExpected
+    )
+/*++
+Routine Description:
+
+    SerialCheckStatus validates the current status of the serial COM port.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+
+Return Value:
+
+    NTSTATUS    - A failure here will indicate the serial COM port is not in an
+                  expected state.
+--*/
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+    WDF_MEMORY_DESCRIPTOR outputDesc;
+    WDF_REQUEST_SEND_OPTIONS wrso = {
+        sizeof(WDF_REQUEST_SEND_OPTIONS),
+        WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+        WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete
+    };
+    ULONG_PTR bytesReturned = 0;
+    SERIAL_STATUS ss = { 0 };
+    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDesc, (PVOID)&ss, sizeof(ss));
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    // Check to ensure we are ready to send
+    status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_GET_COMMSTATUS, &wrso, WDF_NO_HANDLE, &outputDesc, &bytesReturned);
+
+    if (!NT_SUCCESS(status)) 
+    {
+        LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_GET_COMMSTATUS failed %!STATUS!", status);
+    }
+    else if (bytesReturned >= sizeof(ss)) 
+    {
+        if (ss.HoldReasons)
+        {
+            if (ss.HoldReasons != SERIAL_TX_WAITING_FOR_CTS)
+            {
+                LogError(DRIVER_DEFAULT, "HoldReasons is wrong (should only be CTS, but is %x)", ss.HoldReasons );
+                status = STATUS_INVALID_DEVICE_STATE;
+            }
+            else if (!DataExpected)
+            {
+                LogError(DRIVER_DEFAULT, "Adapter already has data on init!?!?!");
+                status = STATUS_INVALID_STATE_TRANSITION;
+            }
+        }
+        if (ss.Errors)
+        {
+            LogWarning(DRIVER_DEFAULT, "Unexpected Error %x", ss.Errors);
+            status = STATUS_UNSUCCESSFUL;
+        }
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SerialFlushAndCheckStatus(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    )
+/*++
+Routine Description:
+
+    SerialFlushAndCheckStatus flushed and validates the current status of the serial COM port.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+
+Return Value:
+
+    NTSTATUS    - A failure here will indicate the serial COM port is not in an
+                  expected state.
+--*/
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    do
+    {
+        WDF_MEMORY_DESCRIPTOR inputDesc;
+        WDF_REQUEST_SEND_OPTIONS wrso = {
+            sizeof(WDF_REQUEST_SEND_OPTIONS),
+            WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+            WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete
+        };
+        const ULONG flags = SERIAL_PURGE_RXABORT | SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXABORT | SERIAL_PURGE_TXCLEAR;
+
+        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDesc, (PVOID)&flags, sizeof(flags));
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_PURGE, &wrso, &inputDesc);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_PURGE failed %!STATUS!", status);
+            break;
+        }
+
+        status = SerialSendIoctl(AdapterContext, IOCTL_SERIAL_CLEAR_STATS, &wrso);
+
+        if (!NT_SUCCESS(status)) {
+            LogError(DRIVER_DEFAULT, "IOCTL_SERIAL_CLEAR_STATS failed %!STATUS!", status);
+            break;
+        }
+
+        status = SerialCheckStatus(AdapterContext, false);
+        for (int i = 0; !NT_SUCCESS(status) && i < 20; i++)
+        {
+            NdisMSleep(1); // just sleep enough to give up our quantum
+            status = SerialCheckStatus(AdapterContext, false);
+        }
+
+    } while (false);
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+bool
+SerialPushSend(
+    _In_ POTTMP_ADAPTER_CONTEXT AdapterContext,
+    _In_ PSERIAL_SEND_ITEM      SendItem
+    )
+{
+    WdfSpinLockAcquire(AdapterContext->SendLock);
+    
+    // Start the work item up if it's not already running
+    if (!AdapterContext->SendQueueRunning)
+    {
+        LogVerbose(DRIVER_DEFAULT, "Starting Send Work Item");
+        AdapterContext->SendQueueRunning = true;
+        WdfWorkItemEnqueue(AdapterContext->SendWorkItem);
+    }
+
+    // Insert the new item at the end of the list
+    InsertTailList(&AdapterContext->SendQueue, &SendItem->Link);
+
+    WdfSpinLockRelease(AdapterContext->SendLock);
+
+    return true;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+PSERIAL_SEND_ITEM
+SerialPopSend(
+    _In_ POTTMP_ADAPTER_CONTEXT AdapterContext
+    )
+{
+    PLIST_ENTRY current = NULL;
+
+    // Grab the head of the list
+    // Careful, this might have gotten aborted, leaving the list empty
+    WdfSpinLockAcquire(AdapterContext->SendLock);
+
+    if (!IsListEmpty(&AdapterContext->SendQueue))
+    {
+        current = RemoveHeadList(&AdapterContext->SendQueue);
+    }
+    if (current == NULL)
+    {
+        // Do this under the lock, but the state is consumed outside the lock
+        AdapterContext->SendQueueRunning = false;
+        LogVerbose(DRIVER_DEFAULT, "Send Work Item Complete");
+    }
+
+    WdfSpinLockRelease(AdapterContext->SendLock);
+
+    return current ? CONTAINING_RECORD(current, SERIAL_SEND_ITEM, Link) : NULL;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+SerialSendData(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ PNET_BUFFER_LIST           NetBufferList
+    )
+/*++
+Routine Description:
+
+    SerialSendData encodes and queues up the data to be sent over the serial COM port.
+
+Arguments:
+
+    AdapterContext - handle to a OTTMP Adapter
+
+    NetBufferLists - a single NET_BUFFER_LIST object, containing a signle NET_BUFFER for 
+                     Spinel tunnel commands.
+
+    DispatchLevel  - flag indicating if we are running at dispatch or not
+
+Return Value:
+
+    NTSTATUS    - A failure here will indicate we either failed to encode or queue the data.
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    WDF_OBJECT_ATTRIBUTES attributes;
+    WDFMEMORY WdfMemBuffer = NULL;
+    PSERIAL_SEND_ITEM SendItem = NULL;
+    PUCHAR DecodedBuffer = NULL;
+    ULONG DecodedBufferLength = NetBufferList->FirstNetBuffer->DataLength;
+    ULONG EncodedBufferLength;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    do
+    {
+        // Get the decoded buffer from the NBL/NB. We required
+        // the use of contiguous buffers.
+        DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBufferList->FirstNetBuffer, DecodedBufferLength, NULL, 1, 0);
+        if (DecodedBuffer == NULL) {
+            status = STATUS_INVALID_PARAMETER;
+            break;
+        }
+        
+        LogVerbose(DRIVER_DEFAULT, "Sending %u decoded bytes", DecodedBufferLength);
+        DumpBuffer(DecodedBuffer, DecodedBufferLength);
+
+        // Calculate the buffer size required
+        EncodedBufferLength = HdlcComputeEncodedLength(DecodedBuffer, DecodedBufferLength);
+        
+        // Allocate the memory
+        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+        attributes.ParentObject = AdapterContext->Device;
+#pragma warning(push)
+#pragma warning(suppress: 28160) // Param 3 could be 0
+        status = WdfMemoryCreate(
+                    &attributes,
+                    NonPagedPoolNx,
+                    0,
+                    SERIAL_SEND_ITEM_SIZE + EncodedBufferLength,
+                    &WdfMemBuffer,
+                    (PVOID*)&SendItem
+                    );
+#pragma warning(pop)
+
+        if (!NT_SUCCESS(status)) {
+            LogWarning(DRIVER_DEFAULT, "WdfMemoryCreate (%u bytes) failed %!STATUS!", (SERIAL_SEND_ITEM_SIZE + EncodedBufferLength), status);
+            break;
+        }
+        
+        SendItem->NetBufferList = NetBufferList;
+        SendItem->WdfMemory = WdfMemBuffer;
+        SendItem->EncodedBufferLength = EncodedBufferLength;
+
+        // Encode data
+        if (!HdlcEncodeBuffer(DecodedBuffer, DecodedBufferLength, SendItem->EncodedBuffer, EncodedBufferLength)) {
+            NT_ASSERT(FALSE); // Should never fail, unless we have a bug in the length computation
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+
+        // Queue data to be sent out
+        if (!SerialPushSend(AdapterContext, SendItem)) {
+            status = STATUS_DEVICE_NOT_READY;
+            break;
+        }
+
+    } while (false);
+
+    if (!NT_SUCCESS(status)) {
+        if (WdfMemBuffer) {
+            WdfObjectDelete(WdfMemBuffer);
+        }
+    }
+
+    LogFuncExitNT(DRIVER_DEFAULT, status);
+
+    return status;
+}
+
+PAGED
+_Function_class_(EVT_WDF_WORKITEM)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialSendLoop(
+    _In_ WDFWORKITEM WorkItem
+    )
+/*++
+Routine Description:
+
+    SerialSendLoop handles the actual sending of data over the serial COM port.
+
+Arguments:
+
+    WorkItem - handle to a Wdf Device Info object for the Adapter context
+
+--*/
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext;
+    WDF_REQUEST_SEND_OPTIONS wrso = {
+        sizeof(WDF_REQUEST_SEND_OPTIONS),
+        WDF_REQUEST_SEND_OPTION_TIMEOUT | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+        WDF_REL_TIMEOUT_IN_SEC(1) // Nothing should take more than a second to complete
+    };
+    WDFMEMORY_OFFSET offset = { 0 };
+    PSERIAL_SEND_ITEM SendItem = NULL;
+    WDF_MEMORY_DESCRIPTOR wmd;
+    NTSTATUS status;
+
+    WDF_OBJECT_ATTRIBUTES Attributes;
+    WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
+    Attributes.ParentObject = AdapterContext->Device;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+#pragma warning(push)
+#pragma warning(suppress: 6387) // Param 2 is NULL
+    WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&wmd, NULL, &offset);
+#pragma warning(pop)
+
+    while (NULL != (SendItem = SerialPopSend(AdapterContext)))
+    {
+        LogVerbose(DRIVER_DEFAULT, "Sending %u encoded bytes", SendItem->EncodedBufferLength);
+        DumpBuffer(SendItem->EncodedBuffer, SendItem->EncodedBufferLength);
+
+        if (SendItem->EncodedBufferLength > 0)
+        {
+            offset.BufferLength = SendItem->EncodedBufferLength;
+            status = 
+                WdfMemoryCreatePreallocated(
+                    &Attributes, 
+                    SendItem->EncodedBuffer, 
+                    SendItem->EncodedBufferLength, 
+                    &wmd.u.HandleType.Memory);
+
+            if (!NT_SUCCESS( status )) {
+                LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status);
+            }
+            else
+            {
+                // Send the buffer out
+                status = WdfIoTargetSendWriteSynchronously(AdapterContext->WdfIoTarget, NULL, &wmd, NULL, &wrso, NULL);
+
+                if (!NT_SUCCESS( status )) {
+                    LogError(DRIVER_DEFAULT, "WdfIoTargetSendWriteSynchronously (%u bytes) failed %!STATUS!", SendItem->EncodedBufferLength, status);
+                }
+            
+                WdfObjectDelete(wmd.u.HandleType.Memory);
+            }
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+
+        // Complete the NetBufferList
+        SendItem->NetBufferList->Status = status;
+#ifdef OTTMP_LEGACY
+        NdisMSendNetBufferListsComplete(AdapterContext->Adapter, SendItem->NetBufferList, 0);
+#else
+        NetBufferListsCompleteSend(SendItem->NetBufferList);
+#endif
+
+        // Hack to sleep 1 ms per 5 bytes sent
+        NdisMSleep(1000 * (1 + SendItem->EncodedBufferLength / 5));
+
+        // Cleanup
+        WdfObjectDelete(SendItem->WdfMemory);
+    };
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+PAGED
+_Function_class_(EVT_WDF_WORKITEM)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialRecvLoop(
+    _In_ WDFWORKITEM WorkItem
+    )
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = GetWdfDeviceInfo(WorkItem)->AdapterContext;
+    WDFMEMORY mem = { 0 };
+    NTSTATUS status;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    PAGED_CODE();
+
+    do
+    {
+        WDFREQUEST & request = AdapterContext->RecvReadRequest;
+
+        WDF_OBJECT_ATTRIBUTES attributes;
+        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+        attributes.ParentObject = AdapterContext->Device;
+        status =
+            WdfMemoryCreatePreallocated(
+                &attributes, 
+                AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, 
+                MAX_SPINEL_COMMAND_LENGTH, 
+                &mem);
+
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "WdfMemoryCreateFromLookaside failed %!STATUS!", status);
+            break;
+        }
+
+        status = WdfIoTargetFormatRequestForRead(AdapterContext->WdfIoTarget, request, mem, NULL, NULL);
+
+        if (!NT_SUCCESS(status))
+        {
+            LogError(DRIVER_DEFAULT, "WdfIoTargetFormatRequestForRead failed %!STATUS!", status);
+            break;
+        }
+        else
+        {
+            WdfRequestSetCompletionRoutine(request, SerialRecvComplete, AdapterContext);
+            if (WdfRequestSend(request, AdapterContext->WdfIoTarget, WDF_NO_SEND_OPTIONS))
+            {
+                // Send succeeded, no cleanup
+                mem = NULL;
+                break;
+            }
+
+            status = WdfRequestGetStatus(request);
+            if (!NT_SUCCESS(status))
+            {
+                LogError(DRIVER_DEFAULT, "WdfRequestSend failed %!STATUS!", status);
+            }
+
+            WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 };
+            WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+
+            // refresh the request so it is ready to reuse
+            status = WdfRequestReuse(request, &reuseParams);
+            if (!NT_SUCCESS(status))
+            {
+                NT_ASSERT(NT_SUCCESS(status));
+                LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status);
+            }
+        }
+
+    } while (false);
+
+    if (mem) {
+        WdfObjectDelete(mem);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_When_(return==0,_At_(*pNetBufferList, __drv_allocatesMem(mem)))
+_When_(return==0,_At_((*pNetBufferList)->FirstNetBuffer, __drv_allocatesMem(mem)))
+NTSTATUS
+SerialAllocateNetBufferList(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ ULONG                      BufferLength,
+    _Out_ PNET_BUFFER_LIST         *pNetBufferList
+    )
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNET_BUFFER_LIST NetBufferList = NULL;
+    PNET_BUFFER NetBuffer = NULL;
+
+    do
+    {
+
+#ifdef OTTMP_LEGACY
+        // Allocate the NetBufferList
+        NetBufferList = NdisAllocateNetBufferList(AdapterContext->pGlobals->hNblPool, 0, 0);
+
+        if (NetBufferList == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+
+        // Allocate the NetBuffer
+        NetBufferList->FirstNetBuffer = NdisAllocateNetBufferMdlAndData(AdapterContext->pGlobals->hNbPool);
+
+        if (NetBufferList->FirstNetBuffer == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+#else
+        // Grab a NetBufferList from the collection
+        status = NetBufferListCollectionRetrieveNbls(AdapterContext->ReceiveCollection, 1, &NetBufferList);
+
+        if (!NT_SUCCESS(status))
+        {
+            break;
+        }
+#endif
+
+        NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
+        NetBuffer = NetBufferList->FirstNetBuffer;
+
+        // If there is no buffer allocated yet, go ahead and allocate the max
+        if (NET_BUFFER_DATA_LENGTH(NetBuffer) == 0)
+        {
+            // Allocate the max buffer size
+            ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, MAX_SPINEL_COMMAND_LENGTH, 0, NULL);
+            if (ndisStatus != NDIS_STATUS_SUCCESS)
+            {
+                LogError(DRIVER_DEFAULT, "NdisRetreatNetBufferDataStart failed %!NDIS_STATUS!", ndisStatus);
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                break;
+            }
+        }
+
+        // By this point, we should have a NetBuffer with a contiguous memory block of MAX_SPINEL_COMMAND_LENGTH bytes,
+        // though it's offset could be anywhere in the buffer, from when it was previously used.
+
+        // Adjust buffer length to fit the requested length
+        if (NET_BUFFER_DATA_LENGTH(NetBuffer) > BufferLength)
+        {
+            NdisAdvanceNetBufferDataStart(NetBuffer, NET_BUFFER_DATA_LENGTH(NetBuffer) - BufferLength, FALSE, NULL);
+        }
+        else if (NET_BUFFER_DATA_LENGTH(NetBuffer) < BufferLength)
+        {
+            ndisStatus = NdisRetreatNetBufferDataStart(NetBuffer, BufferLength - NET_BUFFER_DATA_LENGTH(NetBuffer), 0, NULL);
+            NT_ASSERT(ndisStatus == NDIS_STATUS_SUCCESS);
+            if (ndisStatus != NDIS_STATUS_SUCCESS)
+            {
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                break;
+            }
+        }
+
+        // Set the output
+        *pNetBufferList = NetBufferList;
+
+    } while (FALSE);
+
+    if (!NT_SUCCESS(status))
+    {
+#ifdef OTTMP_LEGACY
+        if (NetBuffer) NdisFreeNetBuffer(NetBuffer);
+        if (NetBufferList) NdisFreeNetBufferList(NetBufferList);
+#else
+        if (NetBufferList) NetBufferListsDiscardReceive(NetBufferList);
+#endif
+    }
+
+    return status;
+}
+
+_Function_class_(EVT_WDF_REQUEST_COMPLETION_ROUTINE)
+_IRQL_requires_same_
+VOID
+SerialRecvComplete(
+    _In_ WDFREQUEST Request,
+    _In_ WDFIOTARGET Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+    _In_ WDFCONTEXT Context
+    )
+{
+    POTTMP_ADAPTER_CONTEXT AdapterContext = (POTTMP_ADAPTER_CONTEXT)Context;
+    NTSTATUS status;
+
+    LogFuncEntry(DRIVER_DEFAULT);
+
+    UNREFERENCED_PARAMETER(Target); // Except for an assert
+    NT_ASSERT((Target == AdapterContext->WdfIoTarget) || (WDF_NO_HANDLE == AdapterContext->WdfIoTarget));
+    NT_ASSERT(Request == AdapterContext->RecvReadRequest);
+
+    WDFMEMORY mem = Params->Parameters.Read.Buffer;
+    NT_ASSERT(mem);
+
+    status = WdfRequestGetStatus(Request);
+    if (NT_SUCCESS(status))
+    {
+        NT_ASSERT(Params->Type == WdfRequestTypeRead);
+        NT_ASSERT(Params->Parameters.Read.Offset == 0);
+
+        size_t MemoryLength = 0;
+        NT_ASSERT(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength == (PUCHAR)WdfMemoryGetBuffer(mem, &MemoryLength));
+        UNREFERENCED_PARAMETER(MemoryLength);
+
+        LogVerbose(DRIVER_DEFAULT, "Received %u encoded bytes", (ULONG)Params->IoStatus.Information);
+        DumpBuffer(AdapterContext->RecvBuffer + AdapterContext->RecvBufferLength, (ULONG)Params->IoStatus.Information);
+
+        AdapterContext->RecvBufferLength += (ULONG)Params->IoStatus.Information;
+        
+        // Decode and receive
+        ULONG ReadOffset = 0;
+        while (AdapterContext->RecvBufferLength > ReadOffset)
+        {
+            // Parse, validate and compute the decoded buffer size requirements
+            ULONG UsedEncodedBufferLength = AdapterContext->RecvBufferLength - ReadOffset;
+            ULONG DecodedBufferLength = 0;
+            bool HasGoodBuffer = false;
+            bool HasCompleteBuffer = 
+                HdlcDecodeBuffer(
+                    AdapterContext->RecvBuffer + ReadOffset,
+                    &UsedEncodedBufferLength,
+                    &DecodedBufferLength,
+                    NULL,
+                    &HasGoodBuffer);
+
+            // We should never have used more buffer than available
+            NT_ASSERT(UsedEncodedBufferLength <= AdapterContext->RecvBufferLength - ReadOffset);
+
+            // Did we have a complete (start and end sequence chars) buffer?
+            if (!HasCompleteBuffer)
+            {
+                AdapterContext->RecvBufferLength -= ReadOffset;
+
+                LogWarning(DRIVER_DEFAULT, "Buffering %u incomplete bytes", AdapterContext->RecvBufferLength);
+                NT_ASSERT(AdapterContext->RecvBufferLength < MAX_SPINEL_COMMAND_LENGTH);
+
+                memmove_s(AdapterContext->RecvBuffer, sizeof(AdapterContext->RecvBuffer), 
+                          AdapterContext->RecvBuffer + ReadOffset, AdapterContext->RecvBufferLength);
+                break;
+            }
+            else
+            {
+                // Was the buffer too short or did it's FCS not match?
+                if (HasGoodBuffer)
+                {
+                    NT_ASSERT(UsedEncodedBufferLength <= MAX_SPINEL_COMMAND_LENGTH);
+
+                    // Allocate the NetBufferList & NetBuffer to decode the data to
+                    PNET_BUFFER_LIST NetBufferList = NULL;
+                    status = SerialAllocateNetBufferList(AdapterContext, DecodedBufferLength, &NetBufferList);
+
+                    if (NT_SUCCESS(status))
+                    {
+                        PNET_BUFFER NetBuffer = NetBufferList->FirstNetBuffer;
+                        NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer));
+
+                        // Get pointer to contiguous buffer
+                        PUCHAR DecodedBuffer = (PUCHAR)NdisGetDataBuffer(NetBuffer, DecodedBufferLength, NULL, 1, 0);
+                        NT_ASSERT(DecodedBuffer);
+                        if (DecodedBuffer)
+                        {
+                            HasCompleteBuffer = 
+                                HdlcDecodeBuffer(
+                                    AdapterContext->RecvBuffer + ReadOffset,
+                                    &UsedEncodedBufferLength,
+                                    &DecodedBufferLength,
+                                    DecodedBuffer,
+                                    &HasGoodBuffer);
+
+                            NT_ASSERT(HasCompleteBuffer);
+                            NT_ASSERT(HasGoodBuffer);
+                            NT_ASSERT(DecodedBufferLength == NET_BUFFER_DATA_LENGTH(NetBuffer));
+                                
+                            LogVerbose(DRIVER_DEFAULT, "Received %u decoded bytes", DecodedBufferLength);
+                            DumpBuffer(DecodedBuffer, DecodedBufferLength);
+                        }
+                        else
+                        {
+                            status = STATUS_INVALID_PARAMETER;
+                        }
+
+                        if (NT_SUCCESS(status))
+                        {
+                            // Indicate up the new NBL we just created
+#ifdef OTTMP_LEGACY
+                            NdisMIndicateReceiveNetBufferLists(
+                                AdapterContext->Adapter,
+                                NetBufferList,
+                                NDIS_DEFAULT_PORT_NUMBER,
+                                1,
+                                0
+                                );
+#else
+                            NetBufferListsCompleteReceive(
+                                NetBufferList,
+                                NDIS_DEFAULT_PORT_NUMBER,
+                                0
+                                );
+#endif
+                        }
+                        else
+                        {
+#ifdef OTTMP_LEGACY
+                            NdisFreeNetBuffer(NetBuffer);
+                            NdisFreeNetBufferList(NetBufferList);
+#else
+                            NetBufferListsDiscardReceive(NetBufferList);
+#endif
+                        }
+                    }
+                }
+                else
+                {
+                    LogWarning(DRIVER_DEFAULT, "Dropping %u bad bytes", UsedEncodedBufferLength);
+                    DumpBuffer(AdapterContext->RecvBuffer + ReadOffset, UsedEncodedBufferLength);
+                }
+
+                // Skip over used data
+                ReadOffset += UsedEncodedBufferLength;
+            }
+        }
+
+        // We read all the buffer, so reset the length
+        if (AdapterContext->RecvBufferLength == ReadOffset)
+        {
+            AdapterContext->RecvBufferLength = 0;
+        }
+    }
+    else
+    {
+        LogError(DRIVER_DEFAULT, "Read request failed %!STATUS!", status);
+    }
+
+    WdfObjectDelete(mem);
+
+    if (status != STATUS_DELETE_PENDING)
+    {
+        WDF_REQUEST_REUSE_PARAMS reuseParams = { 0 };
+        WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+        status = WdfRequestReuse(Request, &reuseParams);
+        if (!NT_SUCCESS(status))
+        {
+            NT_ASSERT(NT_SUCCESS(status));
+            LogError(DRIVER_DEFAULT, "WdfRequestReuse failed %!STATUS!", status);
+        }
+        
+        LogVerbose(DRIVER_DEFAULT, "Starting recv worker");
+        WdfWorkItemEnqueue(AdapterContext->RecvWorkItem);
+    }
+
+    LogFuncExit(DRIVER_DEFAULT);
+}
+
+VOID 
+DumpLine(
+    _In_reads_bytes_(aLength) PCUCHAR aBuf, 
+    _In_ size_t aLength
+    )
+{
+    char buf[80] = {0};
+    char *cur = buf;
+
+    sprintf_s(cur, sizeof(buf) - (cur - buf), "|");
+    cur += 1;
+
+    for (size_t i = 0; i < 16; i++)
+    {
+        if (i < aLength)
+        {
+            sprintf_s(cur, sizeof(buf) - (cur - buf), " %02X", aBuf[i]);
+            cur += 3;
+        }
+        else
+        {
+            sprintf_s(cur, sizeof(buf) - (cur - buf), " ..");
+            cur += 3;
+        }
+
+        if (!((i + 1) % 8))
+        {
+            sprintf_s(cur, sizeof(buf) - (cur - buf), " |");
+            cur += 2;
+        }
+    }
+
+    sprintf_s(cur, sizeof(buf) - (cur - buf), " ");
+    cur += 1;
+
+    for (size_t i = 0; i < 16; i++)
+    {
+        if (i < aLength && isprint(0x7f & aBuf[i]))
+        {
+            char c = 0x7f & aBuf[i];
+            sprintf_s(cur, sizeof(buf) - (cur - buf), "%c", c);
+            cur += 1;
+        }
+        else
+        {
+            sprintf_s(cur, sizeof(buf) - (cur - buf), ".");
+            cur += 1;
+        }
+    }
+
+    LogVerbose(DRIVER_DEFAULT, "%s", buf);
+}
+
+VOID 
+DumpBuffer(
+    _In_reads_bytes_(aLength) PCUCHAR aBuf, 
+    _In_ size_t aLength
+    )
+{
+    for (size_t i = 0; i < aLength; i += 16)
+    {
+        DumpLine(aBuf + i, (aLength - i) < 16 ? (aLength - i) : 16);
+    }
+}
diff --git a/examples/drivers/windows/ottmp/serial.hpp b/examples/drivers/windows/ottmp/serial.hpp
new file mode 100644
index 0000000..7d58192
--- /dev/null
+++ b/examples/drivers/windows/ottmp/serial.hpp
@@ -0,0 +1,122 @@
+/*
+ *    Copyright (c) 2016, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+PAGED
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialInitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialUninitialize(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+PAGED
+_No_competing_thread_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialInitializeTarget(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ PCWSTR                     TargetName
+);
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialUninitializeTarget(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+);
+
+PAGED
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+SerialConfigure(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SerialCheckStatus(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ bool                       DataExpected
+    );
+
+PAGED
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SerialFlushAndCheckStatus(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+SerialSendData(
+    _In_ POTTMP_ADAPTER_CONTEXT     AdapterContext,
+    _In_ PNET_BUFFER_LIST           NetBufferLists
+    );
+
+PAGED
+_Function_class_(EVT_WDF_WORKITEM)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialSendLoop(
+    _In_ WDFWORKITEM                WorkItem
+    );
+
+PAGED
+_Function_class_(EVT_WDF_WORKITEM)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+SerialRecvLoop(
+    _In_ WDFWORKITEM                WorkItem
+    );
+
+_Function_class_(EVT_WDF_REQUEST_COMPLETION_ROUTINE)
+_IRQL_requires_same_
+VOID
+SerialRecvComplete(
+    _In_ WDFREQUEST                 Request,
+    _In_ WDFIOTARGET                Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+    _In_ WDFCONTEXT                 Context
+    );
+
+VOID 
+DumpBuffer(
+    _In_reads_bytes_(aLength) PCUCHAR aBuf, 
+    _In_ size_t                     aLength
+    );
diff --git a/examples/platforms/Makefile.am b/examples/platforms/Makefile.am
new file mode 100644
index 0000000..fd2b214
--- /dev/null
+++ b/examples/platforms/Makefile.am
@@ -0,0 +1,97 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+# Always package (e.g. for 'make dist') these subdirectories.
+
+DIST_SUBDIRS                            = \
+    cc2538                                \
+    cc2650                                \
+    da15000                               \
+    efr32                                 \
+    emsk                                  \
+    kw41z                                 \
+    nrf52840                              \
+    posix                                 \
+    utils                                 \
+    $(NULL)
+
+# Always build (e.g. for 'make all') these subdirectories.
+
+SUBDIRS                                 = \
+    utils                                 \
+    $(NULL)
+
+if OPENTHREAD_EXAMPLES_CC2538
+SUBDIRS                                += cc2538
+endif
+
+if OPENTHREAD_EXAMPLES_CC2650
+SUBDIRS                                += cc2650
+endif
+
+if OPENTHREAD_EXAMPLES_DA15000
+SUBDIRS                                += da15000
+endif
+
+if OPENTHREAD_EXAMPLES_EFR32
+SUBDIRS                                += efr32
+endif
+
+if OPENTHREAD_EXAMPLES_EMSK
+SUBDIRS                                += emsk
+endif
+
+if OPENTHREAD_EXAMPLES_KW41Z
+SUBDIRS                                += kw41z
+endif
+
+if OPENTHREAD_EXAMPLES_NRF52840
+SUBDIRS                                += nrf52840
+endif
+
+if OPENTHREAD_EXAMPLES_POSIX
+SUBDIRS                                += posix
+endif
+
+# Always pretty (e.g. for 'make pretty') these subdirectories.
+
+PRETTY_SUBDIRS                          = \
+    cc2538                                \
+    cc2650                                \
+    da15000                               \
+    efr32                                 \
+    emsk                                  \
+    kw41z                                 \
+    nrf52840                              \
+    posix                                 \
+    utils                                 \
+    $(NULL)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/platforms/cc2538/Makefile.am b/examples/platforms/cc2538/Makefile.am
new file mode 100644
index 0000000..3ec32f0
--- /dev/null
+++ b/examples/platforms/cc2538/Makefile.am
@@ -0,0 +1,66 @@
+#
+#  Copyright (c) 2016, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+lib_LIBRARIES                             = libopenthread-cc2538.a
+
+libopenthread_cc2538_a_CPPFLAGS           = \
+    -I$(top_srcdir)/include                 \
+    -I$(top_srcdir)/examples/platforms      \
+    -I$(top_srcdir)/src/core                \
+    $(NULL)
+
+libopenthread_cc2538_a_SOURCES            = \
+    alarm.c                                 \
+    flash.c                                 \
+    misc.c                                  \
+    platform.c                              \
+    radio.c                                 \
+    random.c                                \
+    startup-gcc.c                           \
+    uart.c                                  \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_DIAG
+libopenthread_cc2538_a_SOURCES           += \
+    diag.c                                  \
+    $(NULL)
+endif
+
+noinst_HEADERS                            = \
+    cc2538-reg.h                            \
+    platform-cc2538.h                       \
+    rom-utility.h                           \
+    $(NULL)
+
+Dash                                      = -
+libopenthread_cc2538_a_LIBADD             = \
+    $(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o")
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/platforms/cc2538/README.md b/examples/platforms/cc2538/README.md
new file mode 100644
index 0000000..54e0c58
--- /dev/null
+++ b/examples/platforms/cc2538/README.md
@@ -0,0 +1,93 @@
+# OpenThread on CC2538 Example
+
+This directory contains example platform drivers for the [Texas
+Instruments CC2538][cc2538].
+
+[cc2538]: http://www.ti.com/product/CC2538
+
+The example platform drivers are intended to present the minimal code
+necessary to support OpenThread.  As a result, the example platform
+drivers do not necessarily highlight the platform's full capabilities.
+
+## Toolchain
+
+Download and install the [GNU toolchain for ARM
+Cortex-M][gnu-toolchain].
+
+[gnu-toolchain]: https://launchpad.net/gcc-arm-embedded
+
+## Building
+
+In a Bash terminal, follow these instructions to build the cc2538 examples.
+
+```bash
+$ cd <path-to-openthread>
+$ ./bootstrap
+$ make -f examples/Makefile-cc2538
+```
+
+## Flash Binaries
+
+If the build completed successfully, the `elf` files may be found in
+`<path-to-openthread>/output/cc2538/bin`.
+
+To flash the images with [Flash Programmer 2][ti-flash-programmer-2],
+the files must have the `*.elf` extension.
+
+```bash
+$ cd <path-to-openthread>/output/cc2538/bin
+$ cp ot-cli ot-cli.elf
+```
+
+To load the images with the [serial bootloader][ti-cc2538-bootloader],
+the images must be converted to `bin`. This is done using
+`arm-none-eabi-objcopy`
+
+```bash
+$ cd <path-to-openthread>/output/cc2538/bin
+$ arm-none-eabi-objcopy -O binary ot-cli ot-cli.bin
+```
+
+The [cc2538-bsl.py script][cc2538-bsl-tool] provides a convenient
+method for flashing a CC2538 via the UART. To enter the bootloader
+backdoor for flashing, hold down SELECT for CC2538DK (corresponds to
+logic '0') while you press the Reset button.
+
+[ti-flash-programmer-2]: http://www.ti.com/tool/flash-programmer
+[ti-cc2538-bootloader]: http://www.ti.com/lit/an/swra466a/swra466a.pdf
+[cc2538-bsl-tool]: https://github.com/JelmerT/cc2538-bsl
+
+## Interact
+
+1. Open terminal to `/dev/ttyUSB1` (serial port settings: 115200 8-N-1).
+2. Type `help` for list of commands.
+
+```bash
+> help
+help
+channel
+childtimeout
+contextreusedelay
+extaddr
+extpanid
+ipaddr
+keysequence
+leaderweight
+masterkey
+mode
+netdataregister
+networkidtimeout
+networkname
+panid
+ping
+prefix
+releaserouterid
+rloc16
+route
+routerupgradethreshold
+scan
+start
+state
+stop
+whitelist
+```
diff --git a/examples/platforms/cc2538/alarm.c b/examples/platforms/cc2538/alarm.c
new file mode 100644
index 0000000..252f3ab
--- /dev/null
+++ b/examples/platforms/cc2538/alarm.c
@@ -0,0 +1,129 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the OpenThread platform abstraction for the alarm.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <openthread/config.h>
+#include <openthread/openthread.h>
+#include <openthread/platform/alarm.h>
+#include <openthread/platform/diag.h>
+#include <openthread/platform/platform.h>
+
+#include "platform-cc2538.h"
+
+enum
+{
+    kSystemClock = 32000000,  ///< MHz
+    kTicksPerSec = 1000,      ///< Ticks per second
+};
+
+static uint32_t sCounter = 0;
+static uint32_t sAlarmT0 = 0;
+static uint32_t sAlarmDt = 0;
+static bool sIsRunning = false;
+
+void cc2538AlarmInit(void)
+{
+    HWREG(NVIC_ST_RELOAD) = kSystemClock / kTicksPerSec;
+    HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | NVIC_ST_CTRL_ENABLE;
+}
+
+uint32_t otPlatAlarmGetNow(void)
+{
+    return sCounter;
+}
+
+void otPlatAlarmStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt)
+{
+    (void)aInstance;
+    sAlarmT0 = t0;
+    sAlarmDt = dt;
+    sIsRunning = true;
+}
+
+void otPlatAlarmStop(otInstance *aInstance)
+{
+    (void)aInstance;
+    sIsRunning = false;
+}
+
+void cc2538AlarmProcess(otInstance *aInstance)
+{
+    uint32_t expires;
+    bool fire = false;
+
+    if (sIsRunning)
+    {
+        expires = sAlarmT0 + sAlarmDt;
+
+        if (sAlarmT0 <= sCounter)
+        {
+            if (expires >= sAlarmT0 && expires <= sCounter)
+            {
+                fire = true;
+            }
+        }
+        else
+        {
+            if (expires >= sAlarmT0 || expires <= sCounter)
+            {
+                fire = true;
+            }
+        }
+
+        if (fire)
+        {
+            sIsRunning = false;
+
+#if OPENTHREAD_ENABLE_DIAG
+
+            if (otPlatDiagModeGet())
+            {
+                otPlatDiagAlarmFired(aInstance);
+            }
+            else
+#endif
+            {
+                otPlatAlarmFired(aInstance);
+            }
+        }
+    }
+
+}
+
+void SysTick_Handler()
+{
+    sCounter++;
+}
diff --git a/examples/platforms/cc2538/cc2538-reg.h b/examples/platforms/cc2538/cc2538-reg.h
new file mode 100644
index 0000000..089c825
--- /dev/null
+++ b/examples/platforms/cc2538/cc2538-reg.h
@@ -0,0 +1,209 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes CC2538 register definitions.
+ *
+ */
+
+#ifndef CC2538_REG_H_
+#define CC2538_REG_H_
+
+#include <stdint.h>
+
+#define HWREG(x)                                (*((volatile uint32_t *)(x)))
+
+#define NVIC_ST_CTRL                            0xE000E010  // SysTick Control and Status
+#define NVIC_ST_RELOAD                          0xE000E014  // SysTick Reload Value Register
+#define NVIC_EN0                                0xE000E100  // Interrupt 0-31 Set Enable
+
+#define NVIC_ST_CTRL_COUNT                      0x00010000  // Count Flag
+#define NVIC_ST_CTRL_CLK_SRC                    0x00000004  // Clock Source
+#define NVIC_ST_CTRL_INTEN                      0x00000002  // Interrupt Enable
+#define NVIC_ST_CTRL_ENABLE                     0x00000001  // Enable
+
+#define RFCORE_XREG_SRCMATCH_EN                 0x00000001  // SRCMATCH.SRC_MATCH_EN(1)
+#define RFCORE_XREG_SRCMATCH_AUTOPEND           0x00000002  // SRCMATCH.AUTOPEND(1)
+#define RFCORE_XREG_SRCMATCH_PEND_DATAREQ_ONLY  0x00000004  // SRCMATCH.PEND_DATAREQ_ONLY(1)
+
+#define RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE 3           // Num of register for source match enable status
+#define RFCORE_XREG_SRCMATCH_SHORT_ENTRIES      24          // 24 short address entries in maximum
+#define RFCORE_XREG_SRCMATCH_EXT_ENTRIES        12          // 12 extended address entries in maximum
+#define RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET 4           // address offset for one short address entry
+#define RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET   8           // address offset for one extended address entry
+
+#define INT_UART0                               21          // UART0 Rx and Tx
+
+#define IEEE_EUI64                              0x00280028  // Address of IEEE EUI-64 address
+
+#define RFCORE_FFSM_SRCADDRESS_TABLE            0x40088400  // Source Address Table
+
+#define RFCORE_FFSM_SRCEXTPENDEN0               0x40088590  // Enable/Disable automatic pending per extended address
+#define RFCORE_FFSM_SRCSHORTPENDEN0             0x4008859C  // Enable/Disable automatic pending per short address
+#define RFCORE_FFSM_EXT_ADDR0                   0x400885A8  // Local address information
+#define RFCORE_FFSM_PAN_ID0                     0x400885C8  // Local address information
+#define RFCORE_FFSM_PAN_ID1                     0x400885CC  // Local address information
+#define RFCORE_FFSM_SHORT_ADDR0                 0x400885D0  // Local address information
+#define RFCORE_FFSM_SHORT_ADDR1                 0x400885D4  // Local address information
+#define RFCORE_XREG_FRMFILT0                    0x40088600  // The frame filtering function
+#define RFCORE_XREG_SRCMATCH                    0x40088608  // Source address matching and pending bits
+#define RFCORE_XREG_SRCSHORTEN0                 0x4008860C  // Short address matching
+#define RFCORE_XREG_SRCEXTEN0                   0x40088618  // Extended address matching
+
+#define RFCORE_XREG_FRMCTRL0                    0x40088624  // Frame handling
+#define RFCORE_XREG_FRMCTRL1                    0x40088628  // Frame handling
+#define RFCORE_XREG_RXENABLE                    0x4008862C  // RX enabling
+#define RFCORE_XREG_FREQCTRL                    0x4008863C  // Controls the RF frequency
+#define RFCORE_XREG_TXPOWER                     0x40088640  // Controls the output power
+#define RFCORE_XREG_FSMSTAT1                    0x4008864C  // Radio status register
+#define RFCORE_XREG_FIFOPCTRL                   0x40088650  // FIFOP threshold
+#define RFCORE_XREG_CCACTRL0                    0x40088658  // CCA threshold
+#define RFCORE_XREG_RSSISTAT                    0x40088664  // RSSI valid status register
+#define RFCORE_XREG_AGCCTRL1                    0x400886C8  // AGC reference level
+#define RFCORE_XREG_TXFILTCFG                   0x400887E8  // TX filter configuration
+#define RFCORE_XREG_RFRND                       0x4008869C  // Random data
+#define RFCORE_SFR_RFDATA                       0x40088828  // The TX FIFO and RX FIFO
+#define RFCORE_SFR_RFERRF                       0x4008882C  // RF error interrupt flags
+#define RFCORE_SFR_RFIRQF0                      0x40088834  // RF interrupt flags
+#define RFCORE_SFR_RFST                         0x40088838  // RF CSMA-CA/strobe processor
+
+#define RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN    0x00000001  // Enables frame filtering
+
+#define RFCORE_XREG_FRMCTRL0_AUTOACK            0x00000020
+#define RFCORE_XREG_FRMCTRL0_AUTOCRC            0x00000040
+#define RFCORE_XREG_FRMCTRL0_INFINITY_RX        0x00000008
+
+#define RFCORE_XREG_FRMCTRL1_PENDING_OR         0x00000004
+
+#define RFCORE_XREG_RFRND_IRND                  0x00000001
+
+#define RFCORE_XREG_FSMSTAT1_TX_ACTIVE          0x00000002
+#define RFCORE_XREG_FSMSTAT1_CCA                0x00000010  // Clear channel assessment
+#define RFCORE_XREG_FSMSTAT1_SFD                0x00000020
+#define RFCORE_XREG_FSMSTAT1_FIFOP              0x00000040
+#define RFCORE_XREG_FSMSTAT1_FIFO               0x00000080
+
+#define RFCORE_XREG_RSSISTAT_RSSI_VALID         0x00000001  // RSSI value is valid.
+
+#define RFCORE_SFR_RFERRF_RXOVERF               0x00000004  // RX FIFO overflowed.
+
+#define RFCORE_SFR_RFST_INSTR_RXON              0xE3        // Instruction set RX on
+#define RFCORE_SFR_RFST_INSTR_TXON              0xE9        // Instruction set TX on
+#define RFCORE_SFR_RFST_INSTR_RFOFF             0xEF        // Instruction set RF off
+#define RFCORE_SFR_RFST_INSTR_FLUSHRX           0xED        // Instruction set flush rx buffer
+#define RFCORE_SFR_RFST_INSTR_FLUSHTX           0xEE        // Instruction set flush tx buffer
+
+#define ANA_REGS_BASE                           0x400D6000  // ANA_REGS
+#define ANA_REGS_O_IVCTRL                       0x00000004  // Analog control register
+
+#define SYS_CTRL_CLOCK_CTRL                     0x400D2000  // The clock control register
+#define SYS_CTRL_SYSDIV_32MHZ                   0x00000000  // Sys_div for sysclk 32MHz
+#define SYS_CTRL_CLOCK_CTRL_AMP_DET             0x00200000
+
+#define SYS_CTRL_PWRDBG                         0x400D2074
+#define SYS_CTRL_PWRDBG_FORCE_WARM_RESET        0x00000008
+
+#define SYS_CTRL_RCGCUART                       0x400D2028
+#define SYS_CTRL_SCGCUART                       0x400D202C
+#define SYS_CTRL_DCGCUART                       0x400D2030
+#define SYS_CTRL_I_MAP                          0x400D2098
+#define SYS_CTRL_RCGCRFC                        0x400D20A8
+#define SYS_CTRL_SCGCRFC                        0x400D20AC
+#define SYS_CTRL_DCGCRFC                        0x400D20B0
+#define SYS_CTRL_EMUOVR                         0x400D20B4
+
+#define SYS_CTRL_RCGCRFC_RFC0                   0x00000001
+#define SYS_CTRL_SCGCRFC_RFC0                   0x00000001
+#define SYS_CTRL_DCGCRFC_RFC0                   0x00000001
+
+#define SYS_CTRL_I_MAP_ALTMAP                   0x00000001
+
+#define SYS_CTRL_RCGCUART_UART0                 0x00000001
+#define SYS_CTRL_SCGCUART_UART0                 0x00000001
+#define SYS_CTRL_DCGCUART_UART0                 0x00000001
+
+#define IOC_PA0_SEL                             0x400D4000  // Peripheral select control
+#define IOC_PA1_SEL                             0x400D4004  // Peripheral select control
+#define IOC_UARTRXD_UART0                       0x400D4100
+
+#define IOC_PA0_OVER                            0x400D4080
+#define IOC_PA1_OVER                            0x400D4084
+
+#define IOC_MUX_OUT_SEL_UART0_TXD               0x00000000
+
+#define IOC_OVERRIDE_OE                         0x00000008  // PAD Config Override Output Enable
+#define IOC_OVERRIDE_DIS                        0x00000000  // PAD Config Override Disabled
+
+#define UART0_BASE                              0x4000C000
+#define GPIO_A_BASE                             0x400D9000  // GPIO
+
+#define GPIO_O_DIR                              0x00000400
+#define GPIO_O_AFSEL                            0x00000420
+
+#define GPIO_PIN_0                              0x00000001  // GPIO pin 0
+#define GPIO_PIN_1                              0x00000002  // GPIO pin 1
+
+#define UART_O_DR                               0x00000000  // UART data
+#define UART_O_FR                               0x00000018  // UART flag
+#define UART_O_IBRD                             0x00000024
+#define UART_O_FBRD                             0x00000028
+#define UART_O_LCRH                             0x0000002C
+#define UART_O_CTL                              0x00000030  // UART control
+#define UART_O_IM                               0x00000038  // UART interrupt mask
+#define UART_O_MIS                              0x00000040  // UART masked interrupt status
+#define UART_O_ICR                              0x00000044  // UART interrupt clear
+#define UART_O_CC                               0x00000FC8  // UART clock configuration
+
+#define UART_FR_RXFE                            0x00000010  // UART receive FIFO empty
+#define UART_FR_TXFF                            0x00000020  // UART transmit FIFO full
+#define UART_FR_RXFF                            0x00000040  // UART receive FIFO full
+
+#define UART_CONFIG_WLEN_8                      0x00000060  // 8 bit data
+#define UART_CONFIG_STOP_ONE                    0x00000000  // One stop bit
+#define UART_CONFIG_PAR_NONE                    0x00000000  // No parity
+
+#define UART_CTL_UARTEN                         0x00000001  // UART enable
+#define UART_CTL_TXE                            0x00000100  // UART transmit enable
+#define UART_CTL_RXE                            0x00000200  // UART receive enable
+
+#define UART_IM_RXIM                            0x00000010  // UART receive interrupt mask
+#define UART_IM_RTIM                            0x00000040  // UART receive time-out interrupt
+
+#define SOC_ADC_ADCCON1                         0x400D7000  // ADC Control
+#define SOC_ADC_RNDL                            0x400D7014  // RNG low data
+#define SOC_ADC_RNDH                            0x400D7018  // RNG high data
+
+#define SOC_ADC_ADCCON1_RCTRL0                  0x00000004  // ADCCON1 RCTRL bit 0
+#define SOC_ADC_ADCCON1_RCTRL1                  0x00000008  // ADCCON1 RCTRL bit 1
+
+#define FLASH_BASE                              0x00200000  // Flash base address
+#define FLASH_CTRL_FCTL                         0x400D3008  // Flash control
+#define FLASH_CTRL_DIECFG0                      0x400D3014  // Flash information
+
+#endif
diff --git a/examples/platforms/cc2538/cc2538.ld b/examples/platforms/cc2538/cc2538.ld
new file mode 100644
index 0000000..e4ec168
--- /dev/null
+++ b/examples/platforms/cc2538/cc2538.ld
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   GCC linker script for CC2538.
+ */
+
+MEMORY
+{
+  FLASH (rx) :           ORIGIN = 0x00200000,            LENGTH = 0x0007FFD4
+  FLASH_CCA (rx) :       ORIGIN = 0x0027FFD4,            LENGTH = 0x2C
+  SRAM (rwx) :           ORIGIN = 0x20000000,            LENGTH = 32K
+}
+
+ENTRY(flash_cca_lock_page)
+SECTIONS
+{
+    .text : ALIGN(4)
+    {
+        _text = .;
+        *(.vectors)
+        *(.text*)
+        *(.rodata*)
+        KEEP(*(.init))
+        KEEP(*(.fini))
+        _etext = .;
+    } > FLASH= 0
+
+    .init_array :
+    {
+        _init_array = .;
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array*))
+        _einit_array = .;
+    } > FLASH
+
+    .ARM.exidx : ALIGN(4)
+    {
+        *(.ARM.exidx*)
+    } > FLASH
+
+    .data : ALIGN(4)
+    {
+        _data = .;
+        *(.data*)
+        _edata = .;
+    } > SRAM AT > FLASH
+    _ldata = LOADADDR(.data);
+
+    .bss : ALIGN(4)
+    {
+        _bss = .;
+        *(.bss*)
+        *(COMMON)
+        _ebss = .;
+    } > SRAM
+
+    _heap = .;
+    end = .;
+
+    .stack : ALIGN(4)
+    {
+        *(.stack)
+    } > SRAM
+
+    .flashcca :
+    {
+        KEEP(*(.flash_cca))
+    } > FLASH_CCA
+}
diff --git a/examples/platforms/cc2538/diag.c b/examples/platforms/cc2538/diag.c
new file mode 100644
index 0000000..68f234b
--- /dev/null
+++ b/examples/platforms/cc2538/diag.c
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <openthread/config.h>
+#include <openthread/openthread.h>
+#include <openthread/platform/alarm.h>
+#include <openthread/platform/radio.h>
+
+#include "platform-cc2538.h"
+
+/**
+ * Diagnostics mode variables.
+ *
+ */
+static bool sDiagMode = false;
+
+void otPlatDiagProcess(otInstance *aInstance, int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
+{
+    // Add more plarform specific diagnostics features here.
+    snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", argv[0]);
+    (void) argc;
+    (void) aInstance;
+}
+
+void otPlatDiagModeSet(bool aMode)
+{
+    sDiagMode = aMode;
+}
+
+bool otPlatDiagModeGet()
+{
+    return sDiagMode;
+}
+
+void otPlatDiagChannelSet(uint8_t aChannel)
+{
+    (void) aChannel;
+}
+
+void otPlatDiagTxPowerSet(int8_t aTxPower)
+{
+    (void) aTxPower;
+}
+
+void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
+{
+    (void) aInstance;
+    (void) aFrame;
+    (void) aError;
+}
+
+void otPlatDiagAlarmCallback(otInstance *aInstance)
+{
+    (void) aInstance;
+}
diff --git a/examples/platforms/cc2538/flash.c b/examples/platforms/cc2538/flash.c
new file mode 100644
index 0000000..dcf7941
--- /dev/null
+++ b/examples/platforms/cc2538/flash.c
@@ -0,0 +1,178 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <openthread/config.h>
+#include <openthread/platform/alarm.h>
+
+#include "platform-cc2538.h"
+#include "rom-utility.h"
+#include "utils/code_utils.h"
+#include "utils/flash.h"
+
+#define FLASH_CTRL_FCTL_BUSY   0x00000080
+
+enum
+{
+    FLASH_PAGE_SIZE = 0x800,
+};
+
+static otError romStatusToThread(int32_t aStatus)
+{
+    otError error = OT_ERROR_NONE;
+
+    switch (aStatus)
+    {
+    case 0:
+        error = OT_ERROR_NONE;
+        break;
+
+    case -1:
+        error = OT_ERROR_FAILED;
+        break;
+
+    case -2:
+        error = OT_ERROR_INVALID_ARGS;
+        break;
+
+    default:
+        error = OT_ERROR_ABORT;
+    }
+
+    return error;
+}
+
+otError utilsFlashInit(void)
+{
+    return OT_ERROR_NONE;
+}
+
+uint32_t utilsFlashGetSize(void)
+{
+    uint32_t reg = (HWREG(FLASH_CTRL_DIECFG0) & 0x00000070) >> 4;
+
+    return reg ? (0x20000 * reg) : 0x10000;
+}
+
+otError utilsFlashErasePage(uint32_t aAddress)
+{
+    otError error = OT_ERROR_NONE;
+    int32_t status;
+    uint32_t address;
+
+    otEXPECT_ACTION(aAddress < utilsFlashGetSize(), error = OT_ERROR_INVALID_ARGS);
+
+    address = FLASH_BASE + aAddress - (aAddress & (FLASH_PAGE_SIZE - 1));
+    status = ROM_PageErase(address, FLASH_PAGE_SIZE);
+    error = romStatusToThread(status);
+
+exit:
+    return error;
+}
+
+otError utilsFlashStatusWait(uint32_t aTimeout)
+{
+    otError error = OT_ERROR_NONE;
+    uint32_t start = otPlatAlarmGetNow();
+    uint32_t busy = 1;
+
+    while (busy && ((otPlatAlarmGetNow() - start) < aTimeout))
+    {
+        busy = HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY;
+    }
+
+    otEXPECT_ACTION(!busy, error = OT_ERROR_BUSY);
+
+exit:
+    return error;
+}
+
+uint32_t utilsFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+{
+    int32_t status;
+    uint32_t busy = 1;
+    uint32_t *data;
+    uint32_t size = 0;
+
+    otEXPECT_ACTION(((aAddress + aSize) < utilsFlashGetSize()) &&
+                    (!(aAddress & 3)) && (!(aSize & 3)), aSize = 0);
+
+    data = (uint32_t *)(aData);
+
+    while (size < aSize)
+    {
+        status = ROM_ProgramFlash(data, aAddress + FLASH_BASE, 4);
+
+        while (busy)
+        {
+            busy = HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY;
+        }
+
+        otEXPECT(romStatusToThread(status) == OT_ERROR_NONE);
+        size += 4;
+        data++;
+        aAddress += 4;
+    }
+
+exit:
+    return size;
+}
+
+uint32_t utilsFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+{
+    uint32_t size = 0;
+
+    otEXPECT((aAddress + aSize) < utilsFlashGetSize());
+
+    while (size < aSize)
+    {
+        uint32_t reg = HWREG(aAddress + FLASH_BASE);
+        uint8_t *byte = (uint8_t *)&reg;
+        uint8_t maxIndex = 4;
+
+        if (size == (aSize - aSize % 4))
+        {
+            maxIndex = aSize % 4;
+        }
+
+        for (uint8_t index = 0; index < maxIndex; index++, byte++, aData++)
+        {
+            *aData = *byte;
+        }
+
+        size += maxIndex;
+        aAddress += maxIndex;
+    }
+
+exit:
+    return size;
+}
diff --git a/examples/platforms/cc2538/misc.c b/examples/platforms/cc2538/misc.c
new file mode 100644
index 0000000..167cc38
--- /dev/null
+++ b/examples/platforms/cc2538/misc.c
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/types.h>
+#include <openthread/platform/misc.h>
+
+#include "platform-cc2538.h"
+
+void otPlatReset(otInstance *aInstance)
+{
+    (void)aInstance;
+    HWREG(SYS_CTRL_PWRDBG) = SYS_CTRL_PWRDBG_FORCE_WARM_RESET;
+}
+
+otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
+{
+    (void)aInstance;
+    // TODO: Write me!
+    return OT_PLAT_RESET_REASON_POWER_ON;
+}
+
+void otPlatWakeHost(void)
+{
+    // TODO: implement an operation to wake the host from sleep state.
+}
diff --git a/examples/platforms/cc2538/openthread-core-cc2538-config.h b/examples/platforms/cc2538/openthread-core-cc2538-config.h
new file mode 100644
index 0000000..3cc0768
--- /dev/null
+++ b/examples/platforms/cc2538/openthread-core-cc2538-config.h
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes cc2538 compile-time configuration constants for OpenThread.
+ */
+
+#ifndef OPENTHREAD_CORE_CC2538_CONFIG_H_
+#define OPENTHREAD_CORE_CC2538_CONFIG_H_
+
+/**
+ * @def OPENTHREAD_CONFIG_ENABLE_DEFAULT_LOG_OUTPUT
+ *
+ * Define to 1 to enable default log output.
+ *
+ */
+#define OPENTHREAD_CONFIG_ENABLE_DEFAULT_LOG_OUTPUT            1
+
+ /**
+  * @def OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT
+  *
+  * Define to 1 if you want to enable software ACK timeout logic.
+  *
+  */
+#define OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT          1
+
+ /**
+  * @def OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT
+  *
+  * Define to 1 if you want to enable software retransmission logic.
+  *
+  */
+#define OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT           1
+
+ /**
+  * @def OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN
+  *
+  * Define to 1 if you want to enable software energy scanning logic.
+  *
+  */
+#define OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN          1
+
+#endif  // OPENTHREAD_CORE_CC2538_CONFIG_H_
diff --git a/examples/platforms/cc2538/platform-cc2538.h b/examples/platforms/cc2538/platform-cc2538.h
new file mode 100644
index 0000000..f23ad1a
--- /dev/null
+++ b/examples/platforms/cc2538/platform-cc2538.h
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes the platform-specific initializers.
+ *
+ */
+
+#ifndef PLATFORM_CC2538_H_
+#define PLATFORM_CC2538_H_
+
+#include <stdint.h>
+
+#include <openthread/types.h>
+
+#include "cc2538-reg.h"
+
+// Global OpenThread instance structure
+extern otInstance *sInstance;
+
+/**
+ * This function initializes the alarm service used by OpenThread.
+ *
+ */
+void cc2538AlarmInit(void);
+
+/**
+ * This function performs alarm driver processing.
+ *
+ * @param[in]  aInstance  The OpenThread instance structure.
+ *
+ */
+void cc2538AlarmProcess(otInstance *aInstance);
+
+/**
+ * This function initializes the radio service used by OpenThread.
+ *
+ */
+void cc2538RadioInit(void);
+
+/**
+ * This function performs radio driver processing.
+ *
+ * @param[in]  aInstance  The OpenThread instance structure.
+ *
+ */
+void cc2538RadioProcess(otInstance *aInstance);
+
+/**
+ * This function initializes the random number service used by OpenThread.
+ *
+ */
+void cc2538RandomInit(void);
+
+/**
+ * This function performs UART driver processing.
+ *
+ */
+void cc2538UartProcess(void);
+
+#endif  // PLATFORM_CC2538_H_
diff --git a/examples/platforms/cc2538/platform.c b/examples/platforms/cc2538/platform.c
new file mode 100644
index 0000000..8df97ca
--- /dev/null
+++ b/examples/platforms/cc2538/platform.c
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file includes the platform-specific initializers.
+ */
+
+#include "platform-cc2538.h"
+
+otInstance *sInstance;
+
+void PlatformInit(int argc, char *argv[])
+{
+    cc2538AlarmInit();
+    cc2538RandomInit();
+    cc2538RadioInit();
+
+    (void)argc;
+    (void)argv;
+}
+
+void PlatformProcessDrivers(otInstance *aInstance)
+{
+    sInstance = aInstance;
+
+    // should sleep and wait for interrupts here
+
+    cc2538UartProcess();
+    cc2538RadioProcess(aInstance);
+    cc2538AlarmProcess(aInstance);
+}
diff --git a/examples/platforms/cc2538/radio.c b/examples/platforms/cc2538/radio.c
new file mode 100644
index 0000000..d203f16
--- /dev/null
+++ b/examples/platforms/cc2538/radio.c
@@ -0,0 +1,845 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the OpenThread platform abstraction for radio communication.
+ *
+ */
+
+#include <openthread/config.h>
+#include <openthread/openthread.h>
+#include <openthread/platform/diag.h>
+#include <openthread/platform/platform.h>
+#include <openthread/platform/radio.h>
+
+#include "platform-cc2538.h"
+#include "common/logging.hpp"
+#include "utils/code_utils.h"
+
+enum
+{
+    IEEE802154_MIN_LENGTH = 5,
+    IEEE802154_MAX_LENGTH = 127,
+    IEEE802154_ACK_LENGTH = 5,
+    IEEE802154_FRAME_TYPE_MASK = 0x7,
+    IEEE802154_FRAME_TYPE_ACK = 0x2,
+    IEEE802154_FRAME_PENDING = 1 << 4,
+    IEEE802154_ACK_REQUEST = 1 << 5,
+    IEEE802154_DSN_OFFSET = 2,
+};
+
+enum
+{
+    CC2538_RSSI_OFFSET = 73,
+    CC2538_CRC_BIT_MASK = 0x80,
+    CC2538_LQI_BIT_MASK = 0x7f,
+};
+
+enum
+{
+    CC2538_RECEIVE_SENSITIVITY = -100, // dBm
+};
+
+typedef struct TxPowerTable
+{
+    int8_t  mTxPowerVal;
+    uint8_t mTxPowerReg;
+} TxPowerTable;
+
+// The transmit power table, the values are from SmartRF Studio 2.4.0
+static const TxPowerTable sTxPowerTable[] =
+{
+    {  7, 0xFF },
+    {  5, 0xED },
+    {  3, 0xD5 },
+    {  1, 0xC5 },
+    {  0, 0xB6 },
+    { -1, 0xB0 },
+    { -3, 0xA1 },
+    { -5, 0x91 },
+    { -7, 0x88 },
+    { -9, 0x72 },
+    { -11, 0x62 },
+    { -13, 0x58 },
+    { -15, 0x42 },
+    { -24, 0x00 },
+};
+
+static otRadioFrame sTransmitFrame;
+static otRadioFrame sReceiveFrame;
+static otError sTransmitError;
+static otError sReceiveError;
+
+static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH];
+static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH];
+static uint8_t sChannel = 0;
+static int8_t sTxPower = 0;
+
+static otRadioState sState = OT_RADIO_STATE_DISABLED;
+static bool sIsReceiverEnabled = false;
+
+void enableReceiver(void)
+{
+    if (!sIsReceiverEnabled)
+    {
+        otLogInfoPlat(sInstance, "Enabling receiver", NULL);
+
+        // flush rxfifo
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+
+        // enable receiver
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON;
+        sIsReceiverEnabled = true;
+    }
+}
+
+void disableReceiver(void)
+{
+    if (sIsReceiverEnabled)
+    {
+        otLogInfoPlat(sInstance, "Disabling receiver", NULL);
+
+        while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE);
+
+        // flush rxfifo
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+
+        if (HWREG(RFCORE_XREG_RXENABLE) != 0)
+        {
+            // disable receiver
+            HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF;
+        }
+
+        sIsReceiverEnabled = false;
+    }
+}
+
+void setChannel(uint8_t aChannel)
+{
+    if (sChannel != aChannel)
+    {
+        bool enabled = false;
+
+        if (sIsReceiverEnabled)
+        {
+            disableReceiver();
+            enabled = true;
+        }
+
+        otLogInfoPlat(sInstance, "Channel=%d", aChannel);
+
+        HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5;
+        sChannel = aChannel;
+
+        if (enabled)
+        {
+            enableReceiver();
+        }
+    }
+}
+
+void setTxPower(int8_t aTxPower)
+{
+    uint8_t i = 0;
+
+    if (sTxPower != aTxPower)
+    {
+        otLogInfoPlat(sInstance, "TxPower=%d", aTxPower);
+
+        for (i = sizeof(sTxPowerTable) / sizeof(TxPowerTable) - 1; i > 0; i--)
+        {
+            if (aTxPower < sTxPowerTable[i].mTxPowerVal)
+            {
+                break;
+            }
+        }
+
+        HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[i].mTxPowerReg;
+        sTxPower = aTxPower;
+    }
+}
+
+void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
+{
+    uint8_t *eui64 = (uint8_t *)IEEE_EUI64;
+    (void)aInstance;
+
+    for (uint8_t i = 0; i < OT_EXT_ADDRESS_SIZE; i++)
+    {
+        aIeeeEui64[i] = eui64[7 - i];
+    }
+}
+
+void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid)
+{
+    (void)aInstance;
+
+    otLogInfoPlat(sInstance, "PANID=%X", aPanid);
+
+    HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF;
+    HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8;
+}
+
+void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aAddress)
+{
+    (void)aInstance;
+
+    otLogInfoPlat(sInstance, "ExtAddr=%X%X%X%X%X%X%X%X",
+                  aAddress[7], aAddress[6], aAddress[5], aAddress[4], aAddress[3],
+                  aAddress[2], aAddress[1], aAddress[0]);
+
+    for (int i = 0; i < 8; i++)
+    {
+        ((volatile uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = aAddress[i];
+    }
+}
+
+void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
+{
+    (void)aInstance;
+
+    otLogInfoPlat(sInstance, "ShortAddr=%X", aAddress);
+
+    HWREG(RFCORE_FFSM_SHORT_ADDR0) = aAddress & 0xFF;
+    HWREG(RFCORE_FFSM_SHORT_ADDR1) = aAddress >> 8;
+}
+
+void cc2538RadioInit(void)
+{
+    sTransmitFrame.mLength = 0;
+    sTransmitFrame.mPsdu = sTransmitPsdu;
+    sReceiveFrame.mLength = 0;
+    sReceiveFrame.mPsdu = sReceivePsdu;
+
+    // enable clock
+    HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0;
+    HWREG(SYS_CTRL_SCGCRFC) = SYS_CTRL_SCGCRFC_RFC0;
+    HWREG(SYS_CTRL_DCGCRFC) = SYS_CTRL_DCGCRFC_RFC0;
+
+    // Table 23-7.
+    HWREG(RFCORE_XREG_AGCCTRL1) = 0x15;
+    HWREG(RFCORE_XREG_TXFILTCFG) = 0x09;
+    HWREG(ANA_REGS_BASE + ANA_REGS_O_IVCTRL) = 0x0b;
+
+    HWREG(RFCORE_XREG_CCACTRL0) = 0xf8;
+    HWREG(RFCORE_XREG_FIFOPCTRL) = IEEE802154_MAX_LENGTH;
+
+    HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_AUTOCRC | RFCORE_XREG_FRMCTRL0_AUTOACK;
+
+    // default: SRCMATCH.SRC_MATCH_EN(1), SRCMATCH.AUTOPEND(1),
+    // SRCMATCH.PEND_DATAREQ_ONLY(1), RFCORE_XREG_FRMCTRL1_PENDING_OR(0)
+
+    HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[0].mTxPowerReg;
+    sTxPower = sTxPowerTable[0].mTxPowerVal;
+
+    otLogInfoPlat(sInstance, "Initialized", NULL);
+}
+
+bool otPlatRadioIsEnabled(otInstance *aInstance)
+{
+    (void)aInstance;
+    return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
+}
+
+otError otPlatRadioEnable(otInstance *aInstance)
+{
+    if (!otPlatRadioIsEnabled(aInstance))
+    {
+        otLogDebgPlat(sInstance, "State=OT_RADIO_STATE_SLEEP", NULL);
+        sState = OT_RADIO_STATE_SLEEP;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+otError otPlatRadioDisable(otInstance *aInstance)
+{
+    if (otPlatRadioIsEnabled(aInstance))
+    {
+        otLogDebgPlat(sInstance, "State=OT_RADIO_STATE_DISABLED", NULL);
+        sState = OT_RADIO_STATE_DISABLED;
+    }
+
+    return OT_ERROR_NONE;
+}
+
+otError otPlatRadioSleep(otInstance *aInstance)
+{
+    otError error = OT_ERROR_INVALID_STATE;
+    (void)aInstance;
+
+    if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE)
+    {
+        otLogDebgPlat(sInstance, "State=OT_RADIO_STATE_SLEEP", NULL);
+        error = OT_ERROR_NONE;
+        sState = OT_RADIO_STATE_SLEEP;
+        disableReceiver();
+    }
+
+    return error;
+}
+
+otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
+{
+    otError error = OT_ERROR_INVALID_STATE;
+    (void)aInstance;
+
+    if (sState != OT_RADIO_STATE_DISABLED)
+    {
+        otLogDebgPlat(sInstance, "State=OT_RADIO_STATE_RECEIVE", NULL);
+
+        error = OT_ERROR_NONE;
+        sState = OT_RADIO_STATE_RECEIVE;
+        setChannel(aChannel);
+        sReceiveFrame.mChannel = aChannel;
+        enableReceiver();
+    }
+
+    return error;
+}
+
+otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
+{
+    otError error = OT_ERROR_INVALID_STATE;
+    (void)aInstance;
+
+    if (sState == OT_RADIO_STATE_RECEIVE)
+    {
+        int i;
+
+        error = OT_ERROR_NONE;
+        sState = OT_RADIO_STATE_TRANSMIT;
+        sTransmitError = OT_ERROR_NONE;
+
+        while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE);
+
+        // flush txfifo
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX;
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX;
+
+        // frame length
+        HWREG(RFCORE_SFR_RFDATA) = aFrame->mLength;
+
+        // frame data
+        for (i = 0; i < aFrame->mLength; i++)
+        {
+            HWREG(RFCORE_SFR_RFDATA) = aFrame->mPsdu[i];
+        }
+
+        setChannel(aFrame->mChannel);
+        setTxPower(aFrame->mPower);
+
+        while ((HWREG(RFCORE_XREG_FSMSTAT1) & 1) == 0);
+
+        // wait for valid rssi
+        while ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0);
+
+        otEXPECT_ACTION(((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_CCA) &&
+                         !((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_SFD))),
+                        sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE);
+
+        // begin transmit
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_TXON;
+
+        while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE);
+
+        otLogDebgPlat(sInstance, "Transmitted %d bytes", aFrame->mLength);
+    }
+
+exit:
+    return error;
+}
+
+otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
+{
+    (void)aInstance;
+    return &sTransmitFrame;
+}
+
+int8_t otPlatRadioGetRssi(otInstance *aInstance)
+{
+    (void)aInstance;
+    return 0;
+}
+
+otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
+{
+    (void)aInstance;
+    return OT_RADIO_CAPS_NONE;
+}
+
+bool otPlatRadioGetPromiscuous(otInstance *aInstance)
+{
+    (void)aInstance;
+    return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0;
+}
+
+void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
+{
+    (void)aInstance;
+
+    otLogInfoPlat(sInstance, "PromiscuousMode=%d", aEnable ? 1 : 0);
+
+    if (aEnable)
+    {
+        HWREG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN;
+    }
+    else
+    {
+        HWREG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN;
+    }
+}
+
+void readFrame(void)
+{
+    uint8_t length;
+    uint8_t crcCorr;
+    int i;
+
+    otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT);
+    otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0);
+
+    // read length
+    length = HWREG(RFCORE_SFR_RFDATA);
+    otEXPECT(IEEE802154_MIN_LENGTH <= length && length <= IEEE802154_MAX_LENGTH);
+
+    // read psdu
+    for (i = 0; i < length - 2; i++)
+    {
+        sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA);
+    }
+
+    sReceiveFrame.mPower = (int8_t)HWREG(RFCORE_SFR_RFDATA) - CC2538_RSSI_OFFSET;
+    crcCorr = HWREG(RFCORE_SFR_RFDATA);
+
+    if (crcCorr & CC2538_CRC_BIT_MASK)
+    {
+        sReceiveFrame.mLength = length;
+        sReceiveFrame.mLqi = crcCorr & CC2538_LQI_BIT_MASK;
+    }
+    else
+    {
+        // resets rxfifo
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+
+        otLogDebgPlat(sInstance, "Dropping %d received bytes (Invalid CRC)", length);
+    }
+
+    // check for rxfifo overflow
+    if ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0 &&
+        (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) == 0)
+    {
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+        HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
+    }
+
+exit:
+    return;
+}
+
+void cc2538RadioProcess(otInstance *aInstance)
+{
+    readFrame();
+
+    if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) ||
+        (sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH))
+    {
+#if OPENTHREAD_ENABLE_DIAG
+
+        if (otPlatDiagModeGet())
+        {
+            otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError);
+        }
+        else
+#endif
+        {
+            // signal MAC layer for each received frame if promiscous is enabled
+            // otherwise only signal MAC layer for non-ACK frame
+            if (((HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0) ||
+                (sReceiveFrame.mLength > IEEE802154_ACK_LENGTH))
+            {
+                otLogDebgPlat(sInstance, "Received %d bytes", sReceiveFrame.mLength);
+                otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError);
+            }
+        }
+    }
+
+    if (sState == OT_RADIO_STATE_TRANSMIT)
+    {
+        if (sTransmitError != OT_ERROR_NONE || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0)
+        {
+            if (sTransmitError != OT_ERROR_NONE)
+            {
+                otLogDebgPlat(sInstance, "Transmit failed ErrorCode=%d", sTransmitError);
+            }
+
+            sState = OT_RADIO_STATE_RECEIVE;
+
+#if OPENTHREAD_ENABLE_DIAG
+
+            if (otPlatDiagModeGet())
+            {
+                otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError);
+            }
+            else
+#endif
+            {
+                otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError);
+            }
+        }
+        else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH &&
+                 (sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK &&
+                 (sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET]))
+        {
+            sState = OT_RADIO_STATE_RECEIVE;
+
+            otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError);
+        }
+    }
+
+    sReceiveFrame.mLength = 0;
+}
+
+void RFCoreRxTxIntHandler(void)
+{
+    HWREG(RFCORE_SFR_RFIRQF0) = 0;
+}
+
+void RFCoreErrIntHandler(void)
+{
+    HWREG(RFCORE_SFR_RFERRF) = 0;
+}
+
+uint32_t getSrcMatchEntriesEnableStatus(bool aShort)
+{
+    uint32_t status = 0;
+    uint32_t *addr = aShort ? (uint32_t *) RFCORE_XREG_SRCSHORTEN0 : (uint32_t *) RFCORE_XREG_SRCEXTEN0;
+
+    for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++)
+    {
+        status |= HWREG(addr++) << (i * 8);
+    }
+
+    return status;
+}
+
+int8_t findSrcMatchShortEntry(const uint16_t aShortAddress)
+{
+    int8_t entry = -1;
+    uint16_t shortAddr;
+    uint32_t bitMask;
+    uint32_t *addr = NULL;
+    uint32_t status = getSrcMatchEntriesEnableStatus(true);
+
+    for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++)
+    {
+        bitMask = 0x00000001 << i;
+
+        if ((status & bitMask) == 0)
+        {
+            continue;
+        }
+
+        addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET);
+
+        shortAddr = HWREG(addr + 2);
+        shortAddr |= HWREG(addr + 3) << 8;
+
+        if ((shortAddr == aShortAddress))
+        {
+            entry = i;
+            break;
+        }
+    }
+
+    return entry;
+}
+
+int8_t findSrcMatchExtEntry(const uint8_t *aExtAddress)
+{
+    int8_t entry = -1;
+    uint32_t bitMask;
+    uint32_t *addr = NULL;
+    uint32_t status = getSrcMatchEntriesEnableStatus(false);
+
+    for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++)
+    {
+        uint8_t j = 0;
+        bitMask = 0x00000001 << 2 * i;
+
+        if ((status & bitMask) == 0)
+        {
+            continue;
+        }
+
+        addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET);
+
+        for (j = 0; j < sizeof(otExtAddress); j++)
+        {
+            if (HWREG(addr + j) != aExtAddress[j])
+            {
+                break;
+            }
+        }
+
+        if (j == sizeof(otExtAddress))
+        {
+            entry = i;
+            break;
+        }
+    }
+
+    return entry;
+}
+
+void setSrcMatchEntryEnableStatus(bool aShort, uint8_t aEntry, bool aEnable)
+{
+    uint8_t entry = aShort ? aEntry : (2 * aEntry);
+    uint8_t index = entry / 8;
+    uint32_t *addrEn = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0;
+    uint32_t *addrAutoPendEn = aShort ? (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0 : (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0;
+    uint32_t bitMask = 0x00000001;
+
+    if (aEnable)
+    {
+        HWREG(addrEn + index) |= (bitMask) << (entry % 8);
+        HWREG(addrAutoPendEn + index) |= (bitMask) << (entry % 8);
+    }
+    else
+    {
+        HWREG(addrEn + index) &= ~((bitMask) << (entry % 8));
+        HWREG(addrAutoPendEn + index) &= ~((bitMask) << (entry % 8));
+    }
+}
+
+int8_t findSrcMatchAvailEntry(bool aShort)
+{
+    int8_t entry = -1;
+    uint32_t bitMask;
+    uint32_t shortEnableStatus = getSrcMatchEntriesEnableStatus(true);
+    uint32_t extEnableStatus = getSrcMatchEntriesEnableStatus(false);
+
+    otLogDebgPlat(sInstance, "Short enable status: 0x%x", shortEnableStatus);
+    otLogDebgPlat(sInstance, "Ext enable status: 0x%x", extEnableStatus);
+
+    if (aShort)
+    {
+        bitMask = 0x00000001;
+
+        for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++)
+        {
+            if ((extEnableStatus & bitMask) == 0)
+            {
+                if ((shortEnableStatus & bitMask) == 0)
+                {
+                    entry = i;
+                    break;
+                }
+            }
+
+            if (i % 2 == 1)
+            {
+                extEnableStatus = extEnableStatus >> 2;
+            }
+
+            shortEnableStatus = shortEnableStatus >> 1;
+        }
+    }
+    else
+    {
+        bitMask = 0x00000003;
+
+        for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++)
+        {
+            if (((extEnableStatus | shortEnableStatus) & bitMask) == 0)
+            {
+                entry = i;
+                break;
+            }
+
+            extEnableStatus = extEnableStatus >> 2;
+            shortEnableStatus = shortEnableStatus >> 2;
+        }
+    }
+
+    return entry;
+}
+
+void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
+{
+    (void)aInstance;
+
+    otLogInfoPlat(sInstance, "EnableSrcMatch=%d", aEnable ? 1 : 0);
+
+    if (aEnable)
+    {
+        // only set FramePending when ack for data poll if there are queued messages
+        // for entries in the source match table.
+        HWREG(RFCORE_XREG_FRMCTRL1) &= ~RFCORE_XREG_FRMCTRL1_PENDING_OR;
+    }
+    else
+    {
+        // set FramePending for all ack.
+        HWREG(RFCORE_XREG_FRMCTRL1) |= RFCORE_XREG_FRMCTRL1_PENDING_OR;
+    }
+}
+
+otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
+{
+    otError error = OT_ERROR_NONE;
+    int8_t entry = findSrcMatchAvailEntry(true);
+    uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE;
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Add ShortAddr entry: %d", entry);
+
+    otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS);
+
+    addr += (entry * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET);
+
+    HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID0);
+    HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID1);
+    HWREG(addr++) = aShortAddress & 0xFF;
+    HWREG(addr++) = aShortAddress >> 8;
+
+    setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), true);
+
+exit:
+    return error;
+}
+
+otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress)
+{
+    otError error = OT_ERROR_NONE;
+    int8_t entry = findSrcMatchAvailEntry(false);
+    uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE;
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Add ExtAddr entry: %d", entry);
+
+    otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS);
+
+    addr += (entry * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET);
+
+    for (uint8_t i = 0; i < sizeof(otExtAddress); i++)
+    {
+        HWREG(addr++) = aExtAddress[i];
+    }
+
+    setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), true);
+
+exit:
+    return error;
+}
+
+otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
+{
+    otError error = OT_ERROR_NONE;
+    int8_t entry = findSrcMatchShortEntry(aShortAddress);
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Clear ShortAddr entry: %d", entry);
+
+    otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS);
+
+    setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), false);
+
+exit:
+    return error;
+}
+
+otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress)
+{
+    otError error = OT_ERROR_NONE;
+    int8_t entry = findSrcMatchExtEntry(aExtAddress);
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Clear ExtAddr entry: %d", entry);
+
+    otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS);
+
+    setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false);
+
+exit:
+    return error;
+}
+
+void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
+{
+    uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0;
+    uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0;
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Clear ShortAddr entries", NULL);
+
+    for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++)
+    {
+        HWREG(addrEn++) = 0;
+        HWREG(addrAutoPendEn++) = 0;
+    }
+}
+
+void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
+{
+    uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0;
+    uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0;
+    (void)aInstance;
+
+    otLogDebgPlat(sInstance, "Clear ExtAddr entries", NULL);
+
+    for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++)
+    {
+        HWREG(addrEn++) = 0;
+        HWREG(addrAutoPendEn++) = 0;
+    }
+}
+
+otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
+{
+    (void)aInstance;
+    (void)aScanChannel;
+    (void)aScanDuration;
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower)
+{
+    // TODO: Create a proper implementation for this driver.
+    (void)aInstance;
+    (void)aPower;
+}
+
+int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
+{
+    (void)aInstance;
+    return CC2538_RECEIVE_SENSITIVITY;
+}
diff --git a/examples/platforms/cc2538/random.c b/examples/platforms/cc2538/random.c
new file mode 100644
index 0000000..0f5c92f
--- /dev/null
+++ b/examples/platforms/cc2538/random.c
@@ -0,0 +1,119 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements a random number generator.
+ *
+ */
+
+#include <openthread/types.h>
+#include <openthread/platform/radio.h>
+#include <openthread/platform/random.h>
+
+#include "platform-cc2538.h"
+#include "utils/code_utils.h"
+
+static void generateRandom(uint8_t *aOutput, uint16_t aOutputLength)
+{
+    HWREG(SOC_ADC_ADCCON1) &= ~(SOC_ADC_ADCCON1_RCTRL1 | SOC_ADC_ADCCON1_RCTRL0);
+    HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0;
+
+    while (HWREG(SYS_CTRL_RCGCRFC) != SYS_CTRL_RCGCRFC_RFC0);
+
+    HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_INFINITY_RX;
+    HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON;
+
+    while (!HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID);
+
+    for (uint16_t index = 0; index < aOutputLength; index++)
+    {
+        aOutput[index] = 0;
+
+        for (uint8_t offset = 0; offset < 8 * sizeof(uint8_t); offset++)
+        {
+            aOutput[index] <<= 1;
+            aOutput[index] |= (HWREG(RFCORE_XREG_RFRND) & RFCORE_XREG_RFRND_IRND);
+        }
+    }
+
+    HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF;
+}
+
+void cc2538RandomInit(void)
+{
+    uint16_t seed = 0;
+
+    while (seed == 0x0000 || seed == 0x8003)
+    {
+        generateRandom((uint8_t *)&seed, sizeof(seed));
+    }
+
+    HWREG(SOC_ADC_RNDL) = (seed >> 8) & 0xff;
+    HWREG(SOC_ADC_RNDL) = seed & 0xff;
+}
+
+uint32_t otPlatRandomGet(void)
+{
+    uint32_t random = 0;
+
+    HWREG(SOC_ADC_ADCCON1) |= SOC_ADC_ADCCON1_RCTRL0;
+    random = HWREG(SOC_ADC_RNDL) | (HWREG(SOC_ADC_RNDH) << 8);
+
+    HWREG(SOC_ADC_ADCCON1) |= SOC_ADC_ADCCON1_RCTRL0;
+    random |= ((HWREG(SOC_ADC_RNDL) | (HWREG(SOC_ADC_RNDH) << 8)) << 16);
+
+    return random;
+}
+
+otError otPlatRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength)
+{
+    otError error = OT_ERROR_NONE;
+    uint8_t channel = 0;
+
+    otEXPECT_ACTION(aOutput, error = OT_ERROR_INVALID_ARGS);
+
+    if (otPlatRadioIsEnabled(sInstance))
+    {
+        channel = 11 + (HWREG(RFCORE_XREG_FREQCTRL) - 11) / 5;
+        otPlatRadioSleep(sInstance);
+        otPlatRadioDisable(sInstance);
+    }
+
+    generateRandom(aOutput, aOutputLength);
+
+    if (channel)
+    {
+        cc2538RadioInit();
+        otPlatRadioEnable(sInstance);
+        otPlatRadioReceive(sInstance, channel);
+    }
+
+exit:
+    return error;
+}
diff --git a/examples/platforms/cc2538/rom-utility.h b/examples/platforms/cc2538/rom-utility.h
new file mode 100644
index 0000000..0598236
--- /dev/null
+++ b/examples/platforms/cc2538/rom-utility.h
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ROM_UTILITY_H_
+#define ROM_UTILITY_H_
+
+#define ROM_API_TABLE_ADDR 0x00000048
+
+typedef uint32_t (* volatile FPTR_CRC32_T)(uint8_t * /*pData*/, uint32_t /*byteCount*/);
+typedef uint32_t (* volatile FPTR_GETFLSIZE_T)(void);
+typedef uint32_t (* volatile FPTR_GETCHIPID_T)(void);
+typedef int32_t (* volatile FPTR_PAGEERASE_T)(uint32_t /*FlashAddr*/, uint32_t /*Size*/);
+typedef int32_t (* volatile FPTR_PROGFLASH_T)(uint32_t * /*pRamData*/, uint32_t /*FlashAdr*/, uint32_t /*ByteCount*/);
+typedef void (* volatile FPTR_RESETDEV_T)(void);
+typedef void *(* volatile FPTR_MEMSET_T)(void * /*s*/, int32_t /*c*/, uint32_t /*n*/);
+typedef void *(* volatile FPTR_MEMCPY_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/);
+typedef int32_t (* volatile FPTR_MEMCMP_T)(const void * /*s1*/, const void * /*s2*/, uint32_t /*n*/);
+typedef void *(* volatile FPTR_MEMMOVE_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/);
+
+typedef struct
+{
+    FPTR_CRC32_T        Crc32;
+    FPTR_GETFLSIZE_T    GetFlashSize;
+    FPTR_GETCHIPID_T    GetChipId;
+    FPTR_PAGEERASE_T    PageErase;
+    FPTR_PROGFLASH_T    ProgramFlash;
+    FPTR_RESETDEV_T     ResetDevice;
+    FPTR_MEMSET_T       memset;
+    FPTR_MEMCPY_T       memcpy;
+    FPTR_MEMCMP_T       memcmp;
+    FPTR_MEMMOVE_T      memmove;
+} ROM_API_T;
+
+#define P_ROM_API              ((ROM_API_T*) ROM_API_TABLE_ADDR)
+
+#define ROM_Crc32(a,b)          P_ROM_API->Crc32(a,b)
+#define ROM_GetFlashSize()      P_ROM_API->GetFlashSize()
+#define ROM_GetChipId()         P_ROM_API->GetChipId()
+#define ROM_PageErase(a,b)      P_ROM_API->PageErase(a,b)
+#define ROM_ProgramFlash(a,b,c) P_ROM_API->ProgramFlash(a,b,c)
+#define ROM_ResetDevice()       P_ROM_API->ResetDevice()
+#define ROM_Memset(a,b,c)       P_ROM_API->memset(a,b,c)
+#define ROM_Memcpy(a,b,c)       P_ROM_API->memcpy(a,b,c)
+#define ROM_Memcmp(a,b,c)       P_ROM_API->memcmp(a,b,c)
+#define ROM_Memmove(a,b,c)      P_ROM_API->memmove(a,b,c)
+
+#endif  // ROM_UTILITY_H_
diff --git a/examples/platforms/cc2538/startup-gcc.c b/examples/platforms/cc2538/startup-gcc.c
new file mode 100644
index 0000000..67f2805
--- /dev/null
+++ b/examples/platforms/cc2538/startup-gcc.c
@@ -0,0 +1,201 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements gcc-specific startup code for the cc2538.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "cc2538-reg.h"
+
+extern uint8_t _ldata;
+extern uint8_t _data;
+extern uint8_t _edata;
+extern uint8_t _bss;
+extern uint8_t _ebss;
+extern uint8_t _init_array;
+extern uint8_t _einit_array;
+
+__extension__ typedef int __guard __attribute__((mode(__DI__)));
+int __cxa_guard_acquire(__guard *g) { return !*(char *)(g); }
+void __cxa_guard_release(__guard *g) { *(char *)g = 1; }
+void __cxa_guard_abort(__guard *g) { (void)g; }
+void __cxa_pure_virtual(void) { while (1); }
+
+void IntDefaultHandler(void);
+void ResetHandler(void);
+
+extern void SysTick_Handler(void);
+extern void UART0IntHandler(void);
+extern void RFCoreRxTxIntHandler(void);
+extern void RFCoreErrIntHandler(void);
+extern void main(void);
+
+static uint64_t stack[512] __attribute__((section(".stack")));
+
+__attribute__((section(".vectors"), used))
+void (*const vectors[])(void) =
+{
+    (void (*)(void))((unsigned long)stack + sizeof(stack)),   // Initial Stack Pointer
+    ResetHandler,                           // 1 The reset handler
+    ResetHandler,                       // 2 The NMI handler
+    IntDefaultHandler,                     // 3 The hard fault handler
+    IntDefaultHandler,                      // 4 The MPU fault handler
+    IntDefaultHandler,                      // 5 The bus fault handler
+    IntDefaultHandler,                      // 6 The usage fault handler
+    0,                                      // 7 Reserved
+    0,                                      // 8 Reserved
+    0,                                      // 9 Reserved
+    0,                                      // 10 Reserved
+    IntDefaultHandler,                      // 11 SVCall handler
+    IntDefaultHandler,                      // 12 Debug monitor handler
+    0,                                      // 13 Reserved
+    IntDefaultHandler,                      // 14 The PendSV handler
+    SysTick_Handler,                        // 15 The SysTick handler
+    IntDefaultHandler,                      // 16 GPIO Port A
+    IntDefaultHandler,                      // 17 GPIO Port B
+    IntDefaultHandler,                      // 18 GPIO Port C
+    IntDefaultHandler,                      // 19 GPIO Port D
+    0,                                      // 20 none
+    UART0IntHandler,                        // 21 UART0 Rx and Tx
+    IntDefaultHandler,                      // 22 UART1 Rx and Tx
+    IntDefaultHandler,                      // 23 SSI0 Rx and Tx
+    IntDefaultHandler,                      // 24 I2C Master and Slave
+    0,                                      // 25 Reserved
+    0,                                      // 26 Reserved
+    0,                                      // 27 Reserved
+    0,                                      // 28 Reserved
+    0,                                      // 29 Reserved
+    IntDefaultHandler,                      // 30 ADC Sequence 0
+    0,                                      // 31 Reserved
+    0,                                      // 32 Reserved
+    0,                                      // 33 Reserved
+    IntDefaultHandler,                      // 34 Watchdog timer, timer 0
+    IntDefaultHandler,                      // 35 Timer 0 subtimer A
+    IntDefaultHandler,                      // 36 Timer 0 subtimer B
+    IntDefaultHandler,                      // 37 Timer 1 subtimer A
+    IntDefaultHandler,                      // 38 Timer 1 subtimer B
+    IntDefaultHandler,                      // 39 Timer 2 subtimer A
+    IntDefaultHandler,                      // 40 Timer 2 subtimer B
+    IntDefaultHandler,                      // 41 Analog Comparator 0
+    RFCoreRxTxIntHandler,                   // 42 RFCore Rx/Tx
+    RFCoreErrIntHandler,                    // 43 RFCore Error
+    IntDefaultHandler,                      // 44 IcePick
+    IntDefaultHandler,                      // 45 FLASH Control
+    IntDefaultHandler,                      // 46 AES
+    IntDefaultHandler,                      // 47 PKA
+    IntDefaultHandler,                      // 48 Sleep Timer
+    IntDefaultHandler,                      // 49 MacTimer
+    IntDefaultHandler,                      // 50 SSI1 Rx and Tx
+    IntDefaultHandler,                      // 51 Timer 3 subtimer A
+    IntDefaultHandler,                      // 52 Timer 3 subtimer B
+    0,                                      // 53 Reserved
+    0,                                      // 54 Reserved
+    0,                                      // 55 Reserved
+    0,                                      // 56 Reserved
+    0,                                      // 57 Reserved
+    0,                                      // 58 Reserved
+    0,                                      // 59 Reserved
+    IntDefaultHandler,                      // 60 USB 2538
+    0,                                      // 61 Reserved
+    IntDefaultHandler,                      // 62 uDMA
+    IntDefaultHandler,                      // 63 uDMA Error
+};
+
+void IntDefaultHandler(void)
+{
+    while (1);
+}
+
+#define FLASH_CCA_BOOTLDR_CFG_DISABLE           0xEFFFFFFF ///< Disable backdoor function
+#define FLASH_CCA_BOOTLDR_CFG_ENABLE            0xF0FFFFFF ///< Enable backdoor function
+#define FLASH_CCA_BOOTLDR_CFG_ACTIVE_HIGH       0x08000000 ///< Selected pin on pad A active high
+#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_M      0x07000000 ///< Selected pin on pad A mask
+#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S      24         ///< Selected pin on pad A shift
+#define FLASH_CCA_IMAGE_VALID                   0x00000000 ///< Indicates valid image in flash
+
+#define FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN  3      ///< Select Button on SmartRF06 Eval Board
+
+typedef struct
+{
+    uint32_t ui32BootldrCfg;
+    uint32_t ui32ImageValid;
+    uint32_t ui32ImageVectorAddr;
+    uint8_t  ui8lock[32];
+} flash_cca_lock_page_t;
+
+__attribute__((__section__(".flashcca"), used))
+const flash_cca_lock_page_t flash_cca_lock_page =
+{
+    FLASH_CCA_BOOTLDR_CFG_ENABLE | (FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN << FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S),
+    FLASH_CCA_IMAGE_VALID,
+    (uint32_t) &vectors,
+    {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+    }
+};
+
+typedef void (*init_fn_t)(void);
+
+void ResetHandler(void)
+{
+    HWREG(SYS_CTRL_EMUOVR) = 0xFF;
+
+    // configure clocks
+    HWREG(SYS_CTRL_CLOCK_CTRL) |= SYS_CTRL_CLOCK_CTRL_AMP_DET;
+    HWREG(SYS_CTRL_CLOCK_CTRL) = SYS_CTRL_SYSDIV_32MHZ;
+
+    // alternate map
+    HWREG(SYS_CTRL_I_MAP) |= SYS_CTRL_I_MAP_ALTMAP;
+
+    // copy the data segment initializers from flash to SRAM
+    memcpy(&_data, &_ldata, &_edata - &_data);
+
+    // zero-fill the bss segment
+    memset(&_bss, 0, &_ebss - &_bss);
+
+    // C++ runtime initialization (BSS, Data, relocation, etc.)
+    init_fn_t *fp;
+
+    for (fp = (init_fn_t *)&_init_array; fp < (init_fn_t *)&_einit_array; fp++)
+    {
+        (*fp)();
+    }
+
+    // call the application's entry point
+    main();
+
+    // end here if main() returns
+    while (1);
+}
diff --git a/examples/platforms/cc2538/uart.c b/examples/platforms/cc2538/uart.c
new file mode 100644
index 0000000..ba1319c
--- /dev/null
+++ b/examples/platforms/cc2538/uart.c
@@ -0,0 +1,202 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the OpenThread platform abstraction for UART communication.
+ *
+ */
+
+#include <stddef.h>
+
+#include <openthread/types.h>
+#include <openthread/platform/uart.h>
+
+#include "platform-cc2538.h"
+#include "utils/code_utils.h"
+
+enum
+{
+    kPlatformClock = 32000000,
+    kBaudRate = 115200,
+    kReceiveBufferSize = 128,
+};
+
+extern void UART0IntHandler(void);
+
+static void processReceive(void);
+static void processTransmit(void);
+
+static const uint8_t *sTransmitBuffer = NULL;
+static uint16_t sTransmitLength = 0;
+
+typedef struct RecvBuffer
+{
+    // The data buffer
+    uint8_t mBuffer[kReceiveBufferSize];
+    // The offset of the first item written to the list.
+    uint16_t mHead;
+    // The offset of the next item to be written to the list.
+    uint16_t mTail;
+} RecvBuffer;
+
+static RecvBuffer sReceive;
+
+otError otPlatUartEnable(void)
+{
+    uint32_t div;
+
+    sReceive.mHead = 0;
+    sReceive.mTail = 0;
+
+    // clock
+    HWREG(SYS_CTRL_RCGCUART) = SYS_CTRL_RCGCUART_UART0;
+    HWREG(SYS_CTRL_SCGCUART) = SYS_CTRL_SCGCUART_UART0;
+    HWREG(SYS_CTRL_DCGCUART) = SYS_CTRL_DCGCUART_UART0;
+
+    HWREG(UART0_BASE + UART_O_CC) = 0;
+
+    // tx pin
+    HWREG(IOC_PA1_SEL) = IOC_MUX_OUT_SEL_UART0_TXD;
+    HWREG(IOC_PA1_OVER) = IOC_OVERRIDE_OE;
+    HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_1;
+
+    // rx pin
+    HWREG(IOC_PA0_SEL) = IOC_UARTRXD_UART0;
+    HWREG(IOC_PA0_OVER) = IOC_OVERRIDE_DIS;
+    HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_0;
+
+    HWREG(UART0_BASE + UART_O_CTL) = 0;
+
+    // baud rate
+    div = (((kPlatformClock * 8) / kBaudRate) + 1) / 2;
+    HWREG(UART0_BASE + UART_O_IBRD) = div / 64;
+    HWREG(UART0_BASE + UART_O_FBRD) = div % 64;
+    HWREG(UART0_BASE + UART_O_LCRH) = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE;
+
+    // configure interrupts
+    HWREG(UART0_BASE + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM;
+
+    // enable
+    HWREG(UART0_BASE + UART_O_CTL) = UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE;
+
+    // enable interrupts
+    HWREG(NVIC_EN0) = 1 << ((INT_UART0 - 16) & 31);
+
+    return OT_ERROR_NONE;
+}
+
+otError otPlatUartDisable(void)
+{
+    return OT_ERROR_NONE;
+}
+
+otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
+{
+    otError error = OT_ERROR_NONE;
+
+    otEXPECT_ACTION(sTransmitBuffer == NULL, error = OT_ERROR_BUSY);
+
+    sTransmitBuffer = aBuf;
+    sTransmitLength = aBufLength;
+
+exit:
+    return error;
+}
+
+void processReceive(void)
+{
+    // Copy tail to prevent multiple reads
+    uint16_t tail = sReceive.mTail;
+
+    // If the data wraps around, process the first part
+    if (sReceive.mHead > tail)
+    {
+        otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, kReceiveBufferSize - sReceive.mHead);
+
+        // Reset the buffer mHead back to zero.
+        sReceive.mHead = 0;
+    }
+
+    // For any data remaining, process it
+    if (sReceive.mHead != tail)
+    {
+        otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, tail - sReceive.mHead);
+
+        // Set mHead to the local tail we have cached
+        sReceive.mHead = tail;
+    }
+}
+
+void processTransmit(void)
+{
+    otEXPECT(sTransmitBuffer != NULL);
+
+    for (; sTransmitLength > 0; sTransmitLength--)
+    {
+        while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF);
+
+        HWREG(UART0_BASE + UART_O_DR) = *sTransmitBuffer++;
+    }
+
+    sTransmitBuffer = NULL;
+    otPlatUartSendDone();
+
+exit:
+    return;
+}
+
+void cc2538UartProcess(void)
+{
+    processReceive();
+    processTransmit();
+}
+
+void UART0IntHandler(void)
+{
+    uint32_t mis;
+    uint8_t byte;
+
+    mis = HWREG(UART0_BASE + UART_O_MIS);
+    HWREG(UART0_BASE + UART_O_ICR) = mis;
+
+    if (mis & (UART_IM_RXIM | UART_IM_RTIM))
+    {
+        while (!(HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE))
+        {
+            byte = HWREG(UART0_BASE + UART_O_DR);
+
+            // We can only write if incrementing mTail doesn't equal mHead
+            if (sReceive.mHead != (sReceive.mTail + 1) % kReceiveBufferSize)
+            {
+                sReceive.mBuffer[sReceive.mTail] = byte;
+                sReceive.mTail = (sReceive.mTail + 1) % kReceiveBufferSize;
+            }
+        }
+    }
+}
diff --git a/examples/platforms/cc2650/Makefile.am b/examples/platforms/cc2650/Makefile.am
new file mode 100644
index 0000000..c16246a
--- /dev/null
+++ b/examples/platforms/cc2650/Makefile.am
@@ -0,0 +1,76 @@
+#
+#  Copyright (c) 2017, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+lib_LIBRARIES                       = libopenthread-cc2650.a
+
+libopenthread_cc2650_a_CPPFLAGS                       = \
+    -I$(top_srcdir)/include                             \
+    -I$(top_srcdir)/src                                 \
+    -I$(top_srcdir)/src/core                            \
+    -I$(top_srcdir)/examples/platforms                  \
+    -I$(top_srcdir)/examples/platforms/cc2650           \
+    -I$(top_srcdir)/third_party/ti/cc26xxware           \
+    -I$(top_srcdir)/third_party/mbedtls/repo/include    \
+    $(MBEDTLS_CPPFLAGS)                                 \
+    $(NULL)
+
+libopenthread_cc2650_a_SOURCES                        = \
+    alarm.c                                             \
+    flash.c                                             \
+    misc.c                                              \
+    platform.c                                          \
+    radio.c                                             \
+    random.c                                            \
+    uart.c                                              \
+    crypto/sha256_alt.c                                 \
+    crypto/aes_alt.c                                    \
+    cc2650_ccfg.c                                       \
+    cc2650_startup.c                                    \
+    $(NULL)
+
+libopenthread_cc2650_a_DEPENDENCIES =                                     \
+    $(top_srcdir)/third_party/ti/cc26xxware/driverlib/bin/gcc/driverlib.a \
+    $(NULL)
+
+if OPENTHREAD_ENABLE_DIAG
+libopenthread_cc2650_a_SOURCES                       += \
+    diag.c                                              \
+    $(NULL)
+endif
+
+noinst_HEADERS                                        = \
+    platform-cc2650.h                                   \
+    $(NULL)
+
+Dash                                                  = -
+libopenthread_cc2650_a_LIBADD                         = \
+    $(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o")
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/examples/platforms/cc2650/README.md b/examples/platforms/cc2650/README.md
new file mode 100644
index 0000000..903d1bc
--- /dev/null
+++ b/examples/platforms/cc2650/README.md
@@ -0,0 +1,139 @@
+# OpenThread on CC2650 Example
+
+This directory contains example platform drivers for the [Texas
+Instruments CC2650][cc2650].
+
+The example platform drivers are intended to present the minimal code necessary
+to support OpenThread. As a result, the example platform drivers do not
+necessarily highlight the platform's full capabilities. The platform
+abstraction layer was build for the [CC2650 LAUNCHXL][cc2650-launchxl], usage
+on other boards with a CC2650 will require changes to the peripheral drivers.
+
+Due to flash size limitations, some features of OpenThread are not supported on
+the [Texas Instruments CC2650][cc2650]. This platform is intended for
+exprimentation and exploration of OpenThread, not a production ready
+environment. Texas Instruments recommends future TI SoCs for production.
+
+Building with gcc 5.4 is recommended due to generated code size concerns.
+
+All three configurations were tested with `arm-none-eabi-gcc 5.4.1 20160609
+(release)` on [this commit][tested-commit]. The automatic integration builds have since
+been limited to only the `cli-mtd` configuration to limit the impact on pull
+requests.
+
+[cc2650]: http://www.ti.com/product/CC2650
+[cc2650-launchxl]: http://www.ti.com/tool/Launchxl-cc2650
+[tested-commit]: https://github.com/openthread/openthread/commit/e8611291d65e8ad28d77a7645695c5352504c3dd
+
+## Build Environment
+
+Building the examples for the cc2650 requires [GNU AutoConf][gnu-autoconf],
+[GNU AutoMake][gnu-automake], [Python][python], and the
+[ARM gcc toolchain][arm-toolchain].
+
+With the exception of the arm toolchain, most of these tools are installed by
+default on modern Posix systems. Windows does not have these tools installed by
+default, and the bootstrap script requires a Posix or MSYS environment to run.
+It is possible to setup an MSYS environment inside of Windows using tools such
+as [Cygwin][cygwin] or [MinGW][mingw] but it is recommended to setup a Linux VM
+for building on a Windows system. For help setting up VirtualBox with Ubuntu,
+consult this [community help wiki article][ubuntu-wiki-virtualbox].
+
+[gnu-autoconf]: https://www.gnu.org/software/autoconf
+[gnu-automake]: https://www.gnu.org/software/automake
+[python]: https://www.python.org
+[arm-toolchain]: https://launchpad.net/gcc-arm-embedded
+[cygwin]: https://www.cygwin.com
+[mingw]: http://www.mingw.org
+[ubuntu-wiki-virtualbox]: https://help.ubuntu.com/community/VirtualBox
+
+
+## Building
+
+In a Bash terminal, follow these instructions to build the cc2650 examples.
+
+```bash
+$ cd <path-to-openthread>
+$ ./bootstrap
+$ make -f examples/Makefile-cc2650
+```
+
+## Flash Binaries
+
+If the build completed successfully, the `elf` files may be found in
+`<path-to-openthread>/output/cc2650/bin`.
+
+To flash the images with [Flash Programmer 2][ti-flash-programmer-2], the files
+must have the `*.elf` extension.
+```bash
+$ cd <path-to-openthread>/output/cc2650/bin
+$ cp ot-cli ot-cli.elf
+```
+
+To load the images with the [serial bootloader][ti-cc2650-bootloader], the
+images must be converted to `bin`. This is done using `arm-none-eabi-objcopy`
+```bash
+$ cd <path-to-openthread>/output/cc2650/bin
+$ arm-none-eabi-objcopy -O binary ot-cli ot-cli.bin
+```
+The [cc2538-bsl.py script][cc2538-bsl-tool] provides a convenient method
+for flashing a CC2650 via the UART. To enter the bootloader backdoor for flashing,
+hold down BTN-1 on CC2650 LauchPad or SELECT for CC2650DK (corresponds to logic '0')
+while you press the Reset button.
+
+[ti-flash-programmer-2]: http://www.ti.com/tool/flash-programmer
+[ti-cc2650-bootloader]: http://www.ti.com/lit/an/swra466a/swra466a.pdf
+[cc2538-bsl-tool]: https://github.com/JelmerT/cc2538-bsl
+
+## Interact
+
+### CLI example
+
+1. With a terminal client (putty, minicom, etc.) open the com port associated
+   with the cc2650 UART. The serial port settings are:
+    * 115200 baud
+    * 8 data bits
+    * no parity bit
+    * 1 stop bit
+2. Type `help` for a list of commands
+3. follow the instructions in the [CLI README][cli-readme] for instructions on
+   setting up a network
+
+[cli-readme]: ../../../src/cli/README.md
+
+```bash
+> help
+help
+channel
+childtimeout
+contextreusedelay
+extaddr
+extpanid
+ipaddr
+keysequence
+leaderweight
+masterkey
+mode
+netdataregister
+networkidtimeout
+networkname
+panid
+ping
+prefix
+releaserouterid
+rloc16
+route
+routerupgradethreshold
+scan
+start
+state
+stop
+whitelist
+```
+
+### NCP example
+
+Refer to the documentation in the [wpantund][wpantund] project for build
+instructions and usage information.
+
+[wpantund]: https://github.com/openthread/wpantund
diff --git a/examples/platforms/cc2650/alarm.c b/examples/platforms/cc2650/alarm.c
new file mode 100644
index 0000000..adffe9c
--- /dev/null
+++ b/examples/platforms/cc2650/alarm.c
@@ -0,0 +1,121 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <openthread/types.h>
+#include <driverlib/aon_rtc.h>
+
+#include <openthread/platform/alarm.h>
+#include <openthread/platform/diag.h>
+
+/**
+ * /NOTE: we could use systick, but that would sacrifice atleast a few ops
+ * every ms, and not run when the processor is sleeping.
+ */
+
+static uint32_t sTime0     = 0;
+static uint32_t sAlarmTime = 0;
+static bool     sIsRunning = false;
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void cc2650AlarmInit(void)
+{
+    /*
+     * NOTE: this will not enable the individual rtc alarm channels
+     */
+    AONRTCEnable();
+    sIsRunning = true;
+}
+
+/**
+ * Function documented in platform/alarm.h
+ */
+uint32_t otPlatAlarmGetNow(void)
+{
+    /*
+     * This is current value of RTC as it appears in the register.
+     * With seconds as the upper 32 bytes and fractions of a second as the
+     * lower 32 bytes <32.32>.
+     */
+    uint64_t rtcVal = AONRTCCurrent64BitValueGet();
+    return ((rtcVal * 1000) >> 32);
+}
+
+/**
+ * Function documented in platform/alarm.h
+ */
+void otPlatAlarmStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
+{
+    (void)aInstance;
+    sTime0 = aT0;
+    sAlarmTime = aDt;
+    sIsRunning = true;
+}
+
+/**
+ * Function documented in platform/alarm.h
+ */
+void otPlatAlarmStop(otInstance *aInstance)
+{
+    (void)aInstance;
+    sIsRunning = false;
+}
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void cc2650AlarmProcess(otInstance *aInstance)
+{
+    uint32_t offsetTime;
+
+    if (sIsRunning)
+    {
+        /* unsinged subtraction will result in the absolute offset */
+        offsetTime = otPlatAlarmGetNow() - sTime0;
+
+        if (sAlarmTime <= offsetTime)
+        {
+            sIsRunning = false;
+#if OPENTHREAD_ENABLE_DIAG
+
+            if (otPlatDiagModeGet())
+            {
+                otPlatDiagAlarmFired(aInstance);
+            }
+            else
+#endif /* OPENTHREAD_ENABLE_DIAG */
+            {
+                otPlatAlarmFired(aInstance);
+            }
+        }
+    }
+}
+
diff --git a/examples/platforms/cc2650/cc2650_ccfg.c b/examples/platforms/cc2650/cc2650_ccfg.c
new file mode 100644
index 0000000..e81fa47
--- /dev/null
+++ b/examples/platforms/cc2650/cc2650_ccfg.c
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Configure the Customer Configuration Area.
+ */
+
+// enable bootloader backdoor
+#define SET_CCFG_BL_CONFIG_BOOTLOADER_ENABLE            0xC5       // Enable ROM boot loader
+
+#define SET_CCFG_BL_CONFIG_BL_LEVEL                     0x0        // Active low to open boot loader backdoor
+
+#define SET_CCFG_BL_CONFIG_BL_PIN_NUMBER                0x0D       // DIO13 (BTN-1 button) on CC2650 LaunchPad Board for boot loader backdoor
+// #define SET_CCFG_BL_CONFIG_BL_PIN_NUMBER             0x0B       // DIO11 (SELECT button) on CC2650DK (QFN48/7*7) for boot loader backdoor
+
+#define SET_CCFG_BL_CONFIG_BL_ENABLE                    0xC5       // Enabled boot loader backdoor
+
+#define SET_CCFG_IMAGE_VALID_CONF_IMAGE_VALID           0x00000000 // Flash image is valid
+
+/*
+ * Include the default ccfg struct and configuration code.
+ */
+#include <startup_files/ccfg.c>
diff --git a/examples/platforms/cc2650/cc2650_radio.h b/examples/platforms/cc2650/cc2650_radio.h
new file mode 100644
index 0000000..6c28af7
--- /dev/null
+++ b/examples/platforms/cc2650/cc2650_radio.h
@@ -0,0 +1,198 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CC2650_RADIO_H_
+#define CC2650_RADIO_H_
+
+#include <driverlib/rf_ieee_cmd.h>
+
+enum
+{
+    IEEE802154_FRAME_TYPE_MASK        = 0x7,     ///< (IEEE 802.15.4-2006) PSDU.FCF.frameType
+    IEEE802154_FRAME_TYPE_ACK         = 0x2,     ///< (IEEE 802.15.4-2006) frame type: ACK
+    IEEE802154_FRAME_PENDING          = (1<<4),  ///< (IEEE 802.15.4-2006) PSDU.FCF.bFramePending
+    IEEE802154_ACK_REQUEST            = (1<<5),  ///< (IEEE 802.15.4-2006) PSDU.FCF.bAR
+    IEEE802154_DSN_OFFSET             = 2,       ///< (IEEE 802.15.4-2006) PSDU.sequenceNumber
+    IEEE802154_MAC_MIN_BE             = 1,       ///< (IEEE 802.15.4-2006) macMinBE
+    IEEE802154_MAC_MAX_BE             = 5,       ///< (IEEE 802.15.4-2006) macMaxBE
+    IEEE802154_MAC_MAX_CSMA_BACKOFFS  = 4,       ///< (IEEE 802.15.4-2006) macMaxCSMABackoffs
+    IEEE802154_MAC_MAX_FRAMES_RETRIES = 3,       ///< (IEEE 802.15.4-2006) macMaxFrameRetries
+    IEEE802154_A_UINT_BACKOFF_PERIOD  = 20,      ///< (IEEE 802.15.4-2006 7.4.1) MAC constants
+    IEEE802154_A_TURNAROUND_TIME      = 12,      ///< (IEEE 802.15.4-2006 6.4.1) PHY constants
+    IEEE802154_PHY_SHR_DURATION       = 10,
+    ///< (IEEE 802.15.4-2006 6.4.2) PHY PIB attribute, specifically the O-QPSK PHY
+    IEEE802154_PHY_SYMBOLS_PER_OCTET  = 2,
+    ///< (IEEE 802.15.4-2006 6.4.2) PHY PIB attribute, specifically the O-QPSK PHY
+    IEEE802154_MAC_ACK_WAIT_DURATION  = (IEEE802154_A_UINT_BACKOFF_PERIOD +
+                                         IEEE802154_A_TURNAROUND_TIME     +
+                                         IEEE802154_PHY_SHR_DURATION      +
+                                         ( 6 * IEEE802154_PHY_SYMBOLS_PER_OCTET)),
+    ///< (IEEE 802.15.4-2006 7.4.2) macAckWaitDuration PIB attribute
+    IEEE802154_SYMBOLS_PER_SEC        = 62500    ///< (IEEE 802.15.4-2006 6.5.3.2) O-QPSK symbol rate
+};
+
+enum
+{
+    CC2650_RAT_TICKS_PER_SEC          = 4000000, ///< 4MHz clock
+    CC2650_INVALID_RSSI               = 127,
+    CC2650_UNKNOWN_EUI64              = 0xFF,
+    ///< If the EUI64 read from the ccfg is all ones then the customer did not set the address
+};
+
+/**
+ * TX Power dBm lookup table - values from SmartRF Studio
+ */
+typedef struct output_config
+{
+    int      dbm;
+    uint16_t value;
+} output_config_t;
+
+static const output_config_t rgOutputPower[] =
+{
+    {   5, 0x9330},
+    {   4, 0x9324},
+    {   3, 0x5a1c},
+    {   2, 0x4e18},
+    {   1, 0x4214},
+    {   0, 0x3161},
+    {  -3, 0x2558},
+    {  -6, 0x1d52},
+    {  -9, 0x194e},
+    { -12, 0x144b},
+    { -15, 0x0ccb},
+    { -18, 0x0cc9},
+    { -21, 0x0cc7},
+};
+
+#define OUTPUT_CONFIG_COUNT (sizeof(rgOutputPower) / sizeof(rgOutputPower[0]))
+
+/* Max and Min Output Power in dBm */
+#define OUTPUT_POWER_MIN     (rgOutputPower[OUTPUT_CONFIG_COUNT - 1].dbm)
+#define OUTPUT_POWER_MAX     (rgOutputPower[0].dbm)
+#define OUTPUT_POWER_UNKNOWN 0xFFFF
+
+/**
+ * return value used when searching the source match array
+ */
+#define CC2650_SRC_MATCH_NONE 0xFF
+
+/**
+ * number of extended addresses used for source matching
+ */
+#define CC2650_EXTADD_SRC_MATCH_NUM 10
+
+/**
+ * structure for source matching extended addresses
+ */
+typedef struct __attribute__((aligned(4))) ext_src_match_data
+{
+    uint32_t srcMatchEn[((CC2650_EXTADD_SRC_MATCH_NUM + 31) / 32)];
+    uint32_t srcPendEn[((CC2650_EXTADD_SRC_MATCH_NUM + 31) / 32)];
+    uint64_t extAddrEnt[CC2650_EXTADD_SRC_MATCH_NUM];
+} ext_src_match_data_t;
+
+/**
+ * number of short addresses used for source matching
+ */
+#define CC2650_SHORTADD_SRC_MATCH_NUM 10
+
+/**
+ * structure for source matching short addresses
+ */
+typedef struct __attribute__((aligned(4))) short_src_match_data
+{
+    uint32_t srcMatchEn[((CC2650_SHORTADD_SRC_MATCH_NUM + 31) / 32)];
+    uint32_t srcPendEn[((CC2650_SHORTADD_SRC_MATCH_NUM + 31) / 32)];
+    rfc_shortAddrEntry_t extAddrEnt[CC2650_SHORTADD_SRC_MATCH_NUM];
+} short_src_match_data_t;
+
+/**
+ * size of length field in receive struct
+ *
+ * defined in Table 23-10 of the cc26xx TRM
+ */
+#define DATA_ENTRY_LENSZ_BYTE 1
+
+/**
+ * address type for @ref rfCoreModifySourceMatchEntry()
+ */
+typedef enum cc2650_address
+{
+    SHORT_ADDRESS = 1,
+    EXT_ADDRESS   = 0,
+} cc2650_address_t;
+
+/**
+ * This enum represents the state of a radio.
+ * Initially, a radio is in the Disabled state.
+ *
+ * The following are valid radio state transitions for the cc2650:
+ *
+ *                                    (Radio ON)
+ *  +----------+  Enable()  +-------+  Receive()   +---------+   Transmit()  +----------+
+ *  |          |----------->|       |------------->|         |-------------->|          |
+ *  | Disabled |            | Sleep |              | Receive |               | Transmit |
+ *  |          |<-----------|       |<-------------|         |<--------------|          |
+ *  +----------+  Disable() |       |   Sleep()    |         |<--  Receive() +----------+
+ *                          |       | (Radio OFF)  +---------+   \           | transmit
+ *                          |       |                             \-----\    | complete
+ *                          |       | EnergyScan() +--------+            |   V
+ *                          |       |------------->|        |      +------------------+
+ *                          |       |              | EdScan |      |                  |
+ *                          |       |<-------------|        |      | TransmitComplete |
+ *                          |       |  signal ED   |        |      |                  |
+ *                          +-------+  scan done   +--------+      +------------------+
+ *
+ * These states slightly differ from the states in \ref include/platform/radio.h.
+ * The additional states the phy can be in are due to the asynchronous nature
+ * of the CM0 radio core.
+ *
+ * | state            | description                                        |
+ * |------------------|----------------------------------------------------|
+ * | Disabled         | The rfcore powerdomain is off and the RFCPE is off |
+ * | Sleep            | The RFCORE PD is on, and the RFCPE is in IEEE mode |
+ * | Receive          | The RFCPE is running a CMD_IEEE_RX                 |
+ * | Transmit         | The RFCPE is running a transmit command string     |
+ * | TransmitComplete | The transmit command string has completed          |
+ * | EdScan           | The RFCPE is running a CMD_IEEE_ED_SCAN            |
+ *
+ * \note The RAT start and Radio Setup commands may be moved to the Receive()
+ *       and EnergyScan() transitions in the future.
+ */
+typedef enum cc2650_PhyState
+{
+    cc2650_stateDisabled = 0,
+    cc2650_stateSleep,
+    cc2650_stateReceive,
+    cc2650_stateEdScan,
+    cc2650_stateTransmit,
+    cc2650_stateTransmitComplete,
+} cc2650_PhyState;
+
+#endif /* CC2650_RADIO_H_ */
diff --git a/examples/platforms/cc2650/cc2650_startup.c b/examples/platforms/cc2650/cc2650_startup.c
new file mode 100644
index 0000000..6e0b565
--- /dev/null
+++ b/examples/platforms/cc2650/cc2650_startup.c
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Include the standard startup files for the gcc toolchain
+ */
+#include <startup_files/startup_gcc.c>
diff --git a/examples/platforms/cc2650/crypto/aes_alt.c b/examples/platforms/cc2650/crypto/aes_alt.c
new file mode 100644
index 0000000..d9994ca
--- /dev/null
+++ b/examples/platforms/cc2650/crypto/aes_alt.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2017, The OpenThread Authors.
+ * All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mbedtls/aes.h"
+#include "aes_alt.h"
+
+#ifdef MBEDTLS_AES_ALT
+
+#include <string.h>
+#include <driverlib/crypto.h>
+#include <driverlib/prcm.h>
+#include <utils/code_utils.h>
+
+#define CC2650_AES_KEY_UNUSED (-1)
+#define CC2650_AES_CTX_MAGIC  (0x7E)
+
+/**
+ * bitmap of which key stores are currently used
+ */
+static uint8_t sUsedKeys = 0;
+
+/**
+ * number of active contexts, used for power on/off of the crypto core
+ */
+static unsigned int sRefNum = 0;
+
+void mbedtls_aes_init(mbedtls_aes_context *ctx)
+{
+    if (sRefNum++ == 0)
+    {
+        /* enable the crypto core */
+        /* The TRNG should already be running before we ever ask the AES core
+         * to do anything, if there is any scenario that the TRNG powers off
+         * the peripheral power domain use this code to repower it
+
+        PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
+        while (PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_ON);
+        */
+        PRCMPeripheralRunEnable(PRCM_PERIPH_CRYPTO);
+        PRCMPeripheralSleepEnable(PRCM_PERIPH_CRYPTO);
+        PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_CRYPTO);
+        PRCMLoadSet();
+
+        while (!PRCMLoadGet());
+
+    }
+
+    ctx->magic = CC2650_AES_CTX_MAGIC;
+    ctx->key_idx = CC2650_AES_KEY_UNUSED;
+}
+
+void mbedtls_aes_free(mbedtls_aes_context *ctx)
+{
+    otEXPECT(ctx->magic == CC2650_AES_CTX_MAGIC);
+
+    if (ctx->key_idx != CC2650_AES_KEY_UNUSED)
+    {
+        sUsedKeys &= ~(1 << ctx->key_idx);
+    }
+
+    if (--sRefNum == 0)
+    {
+        /* disable the crypto core */
+        /* The TRNG core needs the peripheral power domain powered on to
+         * function. if there is a situation where the power domain must be
+         * powered off, use this code to do so.
+
+        PRCMPowerDomainOff(PRCM_DOMAIN_PERIPH);
+        while (PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_OFF);
+        */
+        PRCMPeripheralRunDisable(PRCM_PERIPH_CRYPTO);
+        PRCMPeripheralSleepDisable(PRCM_PERIPH_CRYPTO);
+        PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_CRYPTO);
+        PRCMLoadSet();
+
+        while (!PRCMLoadGet());
+
+    }
+
+    memset((void *)ctx, 0x00, sizeof(ctx));
+
+exit:
+    return;
+}
+
+int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits)
+{
+    unsigned char key_idx;
+    int retval = 0;
+
+    otEXPECT_ACTION(ctx->magic == CC2650_AES_CTX_MAGIC, retval = -1);
+
+    if (ctx->key_idx != CC2650_AES_KEY_UNUSED)
+    {
+        sUsedKeys &= ~(1 << ctx->key_idx);
+    }
+
+    /* our hardware only supports 128 bit keys */
+    otEXPECT_ACTION(keybits == 128u, retval = MBEDTLS_ERR_AES_INVALID_KEY_LENGTH);
+
+    for (key_idx = 0; ((sUsedKeys >> key_idx) & 0x01) != 0 && key_idx < 8; key_idx++);
+
+    /* we have no more room for this key */
+    otEXPECT_ACTION(key_idx < 8, retval = -2);
+
+    otEXPECT_ACTION(CRYPTOAesLoadKey((uint32_t *)key, key_idx) == AES_SUCCESS,
+                    retval = MBEDTLS_ERR_AES_INVALID_KEY_LENGTH);
+
+    sUsedKeys |= (1 << key_idx);
+    ctx->key_idx = key_idx;
+exit:
+    return retval;
+}
+
+int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits)
+{
+    unsigned char key_idx;
+    int retval = 0;
+
+    otEXPECT_ACTION(ctx->magic == CC2650_AES_CTX_MAGIC, retval = -1);
+
+    if (ctx->key_idx != CC2650_AES_KEY_UNUSED)
+    {
+        sUsedKeys &= ~(1 << ctx->key_idx);
+    }
+
+    /* our hardware only supports 128 bit keys */
+    otEXPECT_ACTION(keybits == 128u, retval = MBEDTLS_ERR_AES_INVALID_KEY_LENGTH);
+
+    for (key_idx = 0; ((sUsedKeys >> key_idx) & 0x01) != 0 && key_idx < 8; key_idx++);
+
+    /* we have no more room for this key */
+    otEXPECT_ACTION(key_idx < 8, retval = -2);
+
+    otEXPECT_ACTION(CRYPTOAesLoadKey((uint32_t *)key, key_idx) == AES_SUCCESS,
+                    retval = MBEDTLS_ERR_AES_INVALID_KEY_LENGTH);
+
+    sUsedKeys |= (1 << key_idx);
+    ctx->key_idx = key_idx;
+exit:
+    return retval;
+}
+
+/**
+ * \brief          AES-ECB block encryption/decryption
+ *
+ * \param ctx      AES context
+ * \param mode     MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT
+ * \param input    16-byte input block
+ * \param output   16-byte output block
+ *
+ * \return         0 if successful
+ */
+int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx, int mode, const unsigned char input[16], unsigned char output[16])
+{
+    int retval = -1;
+
+    retval = CRYPTOAesEcb((uint32_t *)input, (uint32_t *)output, ctx->key_idx, mode == MBEDTLS_AES_ENCRYPT, false);
+    otEXPECT(retval == AES_SUCCESS);
+
+    while ((retval = CRYPTOAesEcbStatus()) ==  AES_DMA_BSY);
+
+    CRYPTOAesEcbFinish();
+
+exit:
+    return retval;
+}
+
+#endif /* MBEDTLS_AES_ALT */
diff --git a/examples/platforms/cc2650/crypto/aes_alt.h b/examples/platforms/cc2650/crypto/aes_alt.h
new file mode 100644
index 0000000..e809cd9
--- /dev/null
+++ b/examples/platforms/cc2650/crypto/aes_alt.h
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MBEDTLS_AES_ALT_H
+#define MBEDTLS_AES_ALT_H
+
+#ifndef MBEDTLS_CONFIG_FILE
+#include "cc2650-mbedtls-config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#ifdef MBEDTLS_AES_ALT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+    uint8_t     magic;
+    signed char key_idx;
+} mbedtls_aes_context;
+
+/**
+ * @brief Initialize AES context
+ *
+ * @param [in,out] ctx AES context to be initialized
+ */
+void mbedtls_aes_init(mbedtls_aes_context *ctx);
+
+/**
+ * @brief          Clear AES context
+ *
+ * \param ctx      AES context to be cleared
+ */
+void mbedtls_aes_free(mbedtls_aes_context *ctx);
+
+/**
+ * \brief          AES key schedule (encryption)
+ *
+ * \param ctx      AES context to be initialized
+ * \param key      encryption key
+ * \param keybits  must be 128, 192 or 256
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_AES_INVALID_KEY_LENGTH
+ */
+int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key,
+                           unsigned int keybits);
+
+/**
+ * \brief          AES key schedule (decryption)
+ *
+ * \param ctx      AES context to be initialized
+ * \param key      decryption key
+ * \param keybits  must be 128, 192 or 256
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_AES_INVALID_KEY_LENGTH
+ */
+int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key,
+                           unsigned int keybits);
+
+/**
+ * \brief          AES-ECB block encryption/decryption
+ *
+ * \param ctx      AES context
+ * \param mode     MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT
+ * \param input    16-byte input block
+ * \param output   16-byte output block
+ *
+ * \return         0 if successful
+ */
+int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx, int mode, const unsigned char input[16],
+                          unsigned char output[16]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MBEDTLS_AES_ALT */
+
+#endif /* MBEDTLS_AES_ALT_H */
diff --git a/examples/platforms/cc2650/crypto/cc2650-mbedtls-config.h b/examples/platforms/cc2650/crypto/cc2650-mbedtls-config.h
new file mode 100644
index 0000000..510f326
--- /dev/null
+++ b/examples/platforms/cc2650/crypto/cc2650-mbedtls-config.h
@@ -0,0 +1,2543 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <openthread/platform/logging.h>
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+#define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+
+/**
+ * \name SECTION: System support
+ *
+ * This section sets system specific settings.
+ * \{
+ */
+
+/**
+ * \def MBEDTLS_HAVE_ASM
+ *
+ * The compiler has support for asm().
+ *
+ * Requires support for asm() in compiler.
+ *
+ * Used in:
+ *      library/timing.c
+ *      library/padlock.c
+ *      include/mbedtls/bn_mul.h
+ *
+ * Comment to disable the use of assembly code.
+ */
+#define MBEDTLS_HAVE_ASM
+
+/**
+ * \def MBEDTLS_HAVE_SSE2
+ *
+ * CPU supports SSE2 instruction set.
+ *
+ * Uncomment if the CPU supports SSE2 (IA-32 specific).
+ */
+//#define MBEDTLS_HAVE_SSE2
+
+/**
+ * \def MBEDTLS_HAVE_TIME
+ *
+ * System has time.h and time().
+ * The time does not need to be correct, only time differences are used,
+ * by contrast with MBEDTLS_HAVE_TIME_DATE
+ *
+ * Comment if your system does not support time functions
+ */
+//#define MBEDTLS_HAVE_TIME
+
+/**
+ * \def MBEDTLS_HAVE_TIME_DATE
+ *
+ * System has time.h and time(), gmtime() and the clock is correct.
+ * The time needs to be correct (not necesarily very accurate, but at least
+ * the date should be correct). This is used to verify the validity period of
+ * X.509 certificates.
+ *
+ * Comment if your system does not have a correct clock.
+ */
+//#define MBEDTLS_HAVE_TIME_DATE
+
+/**
+ * \def MBEDTLS_PLATFORM_MEMORY
+ *
+ * Enable the memory allocation layer.
+ *
+ * By default mbed TLS uses the system-provided calloc() and free().
+ * This allows different allocators (self-implemented or provided) to be
+ * provided to the platform abstraction layer.
+ *
+ * Enabling MBEDTLS_PLATFORM_MEMORY without the
+ * MBEDTLS_PLATFORM_{FREE,CALLOC}_MACROs will provide
+ * "mbedtls_platform_set_calloc_free()" allowing you to set an alternative calloc() and
+ * free() function pointer at runtime.
+ *
+ * Enabling MBEDTLS_PLATFORM_MEMORY and specifying
+ * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the
+ * alternate function at compile time.
+ *
+ * Requires: MBEDTLS_PLATFORM_C
+ *
+ * Enable this layer to allow use of alternative memory allocators.
+ */
+#define MBEDTLS_PLATFORM_MEMORY
+
+/**
+ * \def MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
+ *
+ * Do not assign standard functions in the platform layer (e.g. calloc() to
+ * MBEDTLS_PLATFORM_STD_CALLOC and printf() to MBEDTLS_PLATFORM_STD_PRINTF)
+ *
+ * This makes sure there are no linking errors on platforms that do not support
+ * these functions. You will HAVE to provide alternatives, either at runtime
+ * via the platform_set_xxx() functions or at compile time by setting
+ * the MBEDTLS_PLATFORM_STD_XXX defines, or enabling a
+ * MBEDTLS_PLATFORM_XXX_MACRO.
+ *
+ * Requires: MBEDTLS_PLATFORM_C
+ *
+ * Uncomment to prevent default assignment of standard functions in the
+ * platform layer.
+ */
+#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
+
+/**
+ * \def MBEDTLS_PLATFORM_EXIT_ALT
+ *
+ * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let mbed TLS support the
+ * function in the platform abstraction layer.
+ *
+ * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, mbed TLS will
+ * provide a function "mbedtls_platform_set_printf()" that allows you to set an
+ * alternative printf function pointer.
+ *
+ * All these define require MBEDTLS_PLATFORM_C to be defined!
+ *
+ * \note MBEDTLS_PLATFORM_SNPRINTF_ALT is required on Windows;
+ * it will be enabled automatically by check_config.h
+ *
+ * \warning MBEDTLS_PLATFORM_XXX_ALT cannot be defined at the same time as
+ * MBEDTLS_PLATFORM_XXX_MACRO!
+ *
+ * Uncomment a macro to enable alternate implementation of specific base
+ * platform function
+ */
+//#define MBEDTLS_PLATFORM_EXIT_ALT
+//#define MBEDTLS_PLATFORM_TIME_ALT
+//#define MBEDTLS_PLATFORM_FPRINTF_ALT
+//#define MBEDTLS_PLATFORM_PRINTF_ALT
+//#define MBEDTLS_PLATFORM_SNPRINTF_ALT
+
+/**
+ * \def MBEDTLS_DEPRECATED_WARNING
+ *
+ * Mark deprecated functions so that they generate a warning if used.
+ * Functions deprecated in one version will usually be removed in the next
+ * version. You can enable this to help you prepare the transition to a new
+ * major version by making sure your code is not using these functions.
+ *
+ * This only works with GCC and Clang. With other compilers, you may want to
+ * use MBEDTLS_DEPRECATED_REMOVED
+ *
+ * Uncomment to get warnings on using deprecated functions.
+ */
+//#define MBEDTLS_DEPRECATED_WARNING
+
+/**
+ * \def MBEDTLS_DEPRECATED_REMOVED
+ *
+ * Remove deprecated functions so that they generate an error if used.
+ * Functions deprecated in one version will usually be removed in the next
+ * version. You can enable this to help you prepare the transition to a new
+ * major version by making sure your code is not using these functions.
+ *
+ * Uncomment to get errors on using deprecated functions.
+ */
+//#define MBEDTLS_DEPRECATED_REMOVED
+
+/* \} name SECTION: System support */
+
+/**
+ * \name SECTION: mbed TLS feature support
+ *
+ * This section sets support for features that are or are not needed
+ * within the modules that are enabled.
+ * \{
+ */
+
+/**
+ * \def MBEDTLS_TIMING_ALT
+ *
+ * Uncomment to provide your own alternate implementation for mbedtls_timing_hardclock(),
+ * mbedtls_timing_get_timer(), mbedtls_set_alarm(), mbedtls_set/get_delay()
+ *
+ * Only works if you have MBEDTLS_TIMING_C enabled.
+ *
+ * You will need to provide a header "timing_alt.h" and an implementation at
+ * compile time.
+ */
+//#define MBEDTLS_TIMING_ALT
+
+/**
+ * \def MBEDTLS_AES_ALT
+ *
+ * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let mbed TLS use your
+ * alternate core implementation of a symmetric crypto or hash module (e.g.
+ * platform specific assembly optimized implementations). Keep in mind that
+ * the function prototypes should remain the same.
+ *
+ * This replaces the whole module. If you only want to replace one of the
+ * functions, use one of the MBEDTLS__FUNCTION_NAME__ALT flags.
+ *
+ * Example: In case you uncomment MBEDTLS_AES_ALT, mbed TLS will no longer
+ * provide the "struct mbedtls_aes_context" definition and omit the base function
+ * declarations and implementations. "aes_alt.h" will be included from
+ * "aes.h" to include the new function definitions.
+ *
+ * Uncomment a macro to enable alternate implementation of the corresponding
+ * module.
+ */
+#define MBEDTLS_AES_ALT
+//#define MBEDTLS_ARC4_ALT
+//#define MBEDTLS_BLOWFISH_ALT
+//#define MBEDTLS_CAMELLIA_ALT
+//#define MBEDTLS_DES_ALT
+//#define MBEDTLS_XTEA_ALT
+//#define MBEDTLS_MD2_ALT
+//#define MBEDTLS_MD4_ALT
+//#define MBEDTLS_MD5_ALT
+//#define MBEDTLS_RIPEMD160_ALT
+//#define MBEDTLS_SHA1_ALT
+#define MBEDTLS_SHA256_ALT
+//#define MBEDTLS_SHA512_ALT
+
+/**
+ * \def MBEDTLS_MD2_PROCESS_ALT
+ *
+ * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use you
+ * alternate core implementation of symmetric crypto or hash function. Keep in
+ * mind that function prototypes should remain the same.
+ *
+ * This replaces only one function. The header file from mbed TLS is still
+ * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags.
+ *
+ * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, mbed TLS will
+ * no longer provide the mbedtls_sha1_process() function, but it will still provide
+ * the other function (using your mbedtls_sha1_process() function) and the definition
+ * of mbedtls_sha1_context, so your implementation of mbedtls_sha1_process must be compatible
+ * with this definition.
+ *
+ * Note: if you use the AES_xxx_ALT macros, then is is recommended to also set
+ * MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES
+ * tables.
+ *
+ * Uncomment a macro to enable alternate implementation of the corresponding
+ * function.
+ */
+//#define MBEDTLS_MD2_PROCESS_ALT
+//#define MBEDTLS_MD4_PROCESS_ALT
+//#define MBEDTLS_MD5_PROCESS_ALT
+//#define MBEDTLS_RIPEMD160_PROCESS_ALT
+//#define MBEDTLS_SHA1_PROCESS_ALT
+//#define MBEDTLS_SHA256_PROCESS_ALT
+//#define MBEDTLS_SHA512_PROCESS_ALT
+//#define MBEDTLS_DES_SETKEY_ALT
+//#define MBEDTLS_DES_CRYPT_ECB_ALT
+//#define MBEDTLS_DES3_CRYPT_ECB_ALT
+//#define MBEDTLS_AES_SETKEY_ENC_ALT
+//#define MBEDTLS_AES_SETKEY_DEC_ALT
+//#define MBEDTLS_AES_ENCRYPT_ALT
+//#define MBEDTLS_AES_DECRYPT_ALT
+
+/**
+ * \def MBEDTLS_ENTROPY_HARDWARE_ALT
+ *
+ * Uncomment this macro to let mbed TLS use your own implementation of a
+ * hardware entropy collector.
+ *
+ * Your function must be called \c mbedtls_hardware_poll(), have the same
+ * prototype as declared in entropy_poll.h, and accept NULL as first argument.
+ *
+ * Uncomment to use your own hardware entropy collector.
+ */
+#define MBEDTLS_ENTROPY_HARDWARE_ALT
+
+/**
+ * \def MBEDTLS_AES_ROM_TABLES
+ *
+ * Store the AES tables in ROM.
+ *
+ * Uncomment this macro to store the AES tables in ROM.
+ */
+#define MBEDTLS_AES_ROM_TABLES
+
+/**
+ * \def MBEDTLS_CAMELLIA_SMALL_MEMORY
+ *
+ * Use less ROM for the Camellia implementation (saves about 768 bytes).
+ *
+ * Uncomment this macro to use less memory for Camellia.
+ */
+//#define MBEDTLS_CAMELLIA_SMALL_MEMORY
+
+/**
+ * \def MBEDTLS_CIPHER_MODE_CBC
+ *
+ * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers.
+ */
+//#define MBEDTLS_CIPHER_MODE_CBC
+
+/**
+ * \def MBEDTLS_CIPHER_MODE_CFB
+ *
+ * Enable Cipher Feedback mode (CFB) for symmetric ciphers.
+ */
+//#define MBEDTLS_CIPHER_MODE_CFB
+
+/**
+ * \def MBEDTLS_CIPHER_MODE_CTR
+ *
+ * Enable Counter Block Cipher mode (CTR) for symmetric ciphers.
+ */
+//#define MBEDTLS_CIPHER_MODE_CTR
+
+/**
+ * \def MBEDTLS_CIPHER_NULL_CIPHER
+ *
+ * Enable NULL cipher.
+ * Warning: Only do so when you know what you are doing. This allows for
+ * encryption or channels without any security!
+ *
+ * Requires MBEDTLS_ENABLE_WEAK_CIPHERSUITES as well to enable
+ * the following ciphersuites:
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA
+ *      MBEDTLS_TLS_RSA_WITH_NULL_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_NULL_SHA
+ *      MBEDTLS_TLS_RSA_WITH_NULL_MD5
+ *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA
+ *      MBEDTLS_TLS_PSK_WITH_NULL_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_NULL_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_NULL_SHA
+ *
+ * Uncomment this macro to enable the NULL cipher and ciphersuites
+ */
+//#define MBEDTLS_CIPHER_NULL_CIPHER
+
+/**
+ * \def MBEDTLS_CIPHER_PADDING_PKCS7
+ *
+ * MBEDTLS_CIPHER_PADDING_XXX: Uncomment or comment macros to add support for
+ * specific padding modes in the cipher layer with cipher modes that support
+ * padding (e.g. CBC)
+ *
+ * If you disable all padding modes, only full blocks can be used with CBC.
+ *
+ * Enable padding modes in the cipher layer.
+ */
+//#define MBEDTLS_CIPHER_PADDING_PKCS7
+//#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS
+//#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN
+//#define MBEDTLS_CIPHER_PADDING_ZEROS
+
+/**
+ * \def MBEDTLS_ENABLE_WEAK_CIPHERSUITES
+ *
+ * Enable weak ciphersuites in SSL / TLS.
+ * Warning: Only do so when you know what you are doing. This allows for
+ * channels with virtually no security at all!
+ *
+ * This enables the following ciphersuites:
+ *      MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA
+ *
+ * Uncomment this macro to enable weak ciphersuites
+ */
+//#define MBEDTLS_ENABLE_WEAK_CIPHERSUITES
+
+/**
+ * \def MBEDTLS_REMOVE_ARC4_CIPHERSUITES
+ *
+ * Remove RC4 ciphersuites by default in SSL / TLS.
+ * This flag removes the ciphersuites based on RC4 from the default list as
+ * returned by mbedtls_ssl_list_ciphersuites(). However, it is still possible to
+ * enable (some of) them with mbedtls_ssl_conf_ciphersuites() by including them
+ * explicitly.
+ *
+ * Uncomment this macro to remove RC4 ciphersuites by default.
+ */
+//#define MBEDTLS_REMOVE_ARC4_CIPHERSUITES
+
+/**
+ * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED
+ *
+ * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve
+ * module.  By default all supported curves are enabled.
+ *
+ * Comment macros to disable the curve and functions for it
+ */
+//#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
+#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
+//#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
+//#define MBEDTLS_ECP_DP_BP256R1_ENABLED
+//#define MBEDTLS_ECP_DP_BP384R1_ENABLED
+//#define MBEDTLS_ECP_DP_BP512R1_ENABLED
+//#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
+
+/**
+ * \def MBEDTLS_ECP_NIST_OPTIM
+ *
+ * Enable specific 'modulo p' routines for each NIST prime.
+ * Depending on the prime and architecture, makes operations 4 to 8 times
+ * faster on the corresponding curve.
+ *
+ * Comment this macro to disable NIST curves optimisation.
+ */
+#define MBEDTLS_ECP_NIST_OPTIM
+
+/**
+ * \def MBEDTLS_ECDSA_DETERMINISTIC
+ *
+ * Enable deterministic ECDSA (RFC 6979).
+ * Standard ECDSA is "fragile" in the sense that lack of entropy when signing
+ * may result in a compromise of the long-term signing key. This is avoided by
+ * the deterministic variant.
+ *
+ * Requires: MBEDTLS_HMAC_DRBG_C
+ *
+ * Comment this macro to disable deterministic ECDSA.
+ */
+//#define MBEDTLS_ECDSA_DETERMINISTIC
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+ *
+ * Enable the PSK based ciphersuite modes in SSL / TLS.
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
+ *
+ * Enable the DHE-PSK based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_DHM_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+ *
+ * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_ECDH_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
+ *
+ * Enable the RSA-PSK based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
+ *           MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
+ *
+ * Enable the RSA-only based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
+ *           MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_RSA_WITH_RC4_128_MD5
+ */
+//#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
+ *
+ * Enable the DHE-RSA based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_DHM_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
+ *           MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
+ *
+ * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
+ *           MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+ *
+ * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C,
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED
+ *
+ * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
+ *
+ * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS.
+ *
+ * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
+
+/**
+ * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+ *
+ * Enable the ECJPAKE based ciphersuite modes in SSL / TLS.
+ *
+ * \warning This is currently experimental. EC J-PAKE support is based on the
+ * Thread v1.0.0 specification; incompatible changes to the specification
+ * might still happen. For this reason, this is disabled by default.
+ *
+ * Requires: MBEDTLS_ECJPAKE_C
+ *           MBEDTLS_SHA256_C
+ *           MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
+ */
+#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+
+/**
+ * \def MBEDTLS_PK_PARSE_EC_EXTENDED
+ *
+ * Enhance support for reading EC keys using variants of SEC1 not allowed by
+ * RFC 5915 and RFC 5480.
+ *
+ * Currently this means parsing the SpecifiedECDomain choice of EC
+ * parameters (only known groups are supported, not arbitrary domains, to
+ * avoid validation issues).
+ *
+ * Disable if you only need to support RFC 5915 + 5480 key formats.
+ */
+//#define MBEDTLS_PK_PARSE_EC_EXTENDED
+
+/**
+ * \def MBEDTLS_ERROR_STRERROR_DUMMY
+ *
+ * Enable a dummy error function to make use of mbedtls_strerror() in
+ * third party libraries easier when MBEDTLS_ERROR_C is disabled
+ * (no effect when MBEDTLS_ERROR_C is enabled).
+ *
+ * You can safely disable this if MBEDTLS_ERROR_C is enabled, or if you're
+ * not using mbedtls_strerror() or error_strerror() in your application.
+ *
+ * Disable if you run into name conflicts and want to really remove the
+ * mbedtls_strerror()
+ */
+//#define MBEDTLS_ERROR_STRERROR_DUMMY
+
+/**
+ * \def MBEDTLS_GENPRIME
+ *
+ * Enable the prime-number generation code.
+ *
+ * Requires: MBEDTLS_BIGNUM_C
+ */
+//#define MBEDTLS_GENPRIME
+
+/**
+ * \def MBEDTLS_FS_IO
+ *
+ * Enable functions that use the filesystem.
+ */
+//#define MBEDTLS_FS_IO
+
+/**
+ * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
+ *
+ * Do not add default entropy sources. These are the platform specific,
+ * mbedtls_timing_hardclock and HAVEGE based poll functions.
+ *
+ * This is useful to have more control over the added entropy sources in an
+ * application.
+ *
+ * Uncomment this macro to prevent loading of default entropy functions.
+ */
+//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
+
+/**
+ * \def MBEDTLS_NO_PLATFORM_ENTROPY
+ *
+ * Do not use built-in platform entropy functions.
+ * This is useful if your platform does not support
+ * standards like the /dev/urandom or Windows CryptoAPI.
+ *
+ * Uncomment this macro to disable the built-in platform entropy functions.
+ */
+#define MBEDTLS_NO_PLATFORM_ENTROPY
+
+/**
+ * \def MBEDTLS_ENTROPY_FORCE_SHA256
+ *
+ * Force the entropy accumulator to use a SHA-256 accumulator instead of the
+ * default SHA-512 based one (if both are available).
+ *
+ * Requires: MBEDTLS_SHA256_C
+ *
+ * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option
+ * if you have performance concerns.
+ *
+ * This option is only useful if both MBEDTLS_SHA256_C and
+ * MBEDTLS_SHA512_C are defined. Otherwise the available hash module is used.
+ */
+//#define MBEDTLS_ENTROPY_FORCE_SHA256
+
+/**
+ * \def MBEDTLS_MEMORY_DEBUG
+ *
+ * Enable debugging of buffer allocator memory issues. Automatically prints
+ * (to stderr) all (fatal) messages on memory allocation issues. Enables
+ * function for 'debug output' of allocated memory.
+ *
+ * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C
+ *
+ * Uncomment this macro to let the buffer allocator print out error messages.
+ */
+//#define MBEDTLS_MEMORY_DEBUG
+
+/**
+ * \def MBEDTLS_MEMORY_BACKTRACE
+ *
+ * Include backtrace information with each allocated block.
+ *
+ * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C
+ *           GLIBC-compatible backtrace() an backtrace_symbols() support
+ *
+ * Uncomment this macro to include backtrace information
+ */
+//#define MBEDTLS_MEMORY_BACKTRACE
+
+/**
+ * \def MBEDTLS_PK_RSA_ALT_SUPPORT
+ *
+ * Support external private RSA keys (eg from a HSM) in the PK layer.
+ *
+ * Comment this macro to disable support for external private RSA keys.
+ */
+//#define MBEDTLS_PK_RSA_ALT_SUPPORT
+
+/**
+ * \def MBEDTLS_PKCS1_V15
+ *
+ * Enable support for PKCS#1 v1.5 encoding.
+ *
+ * Requires: MBEDTLS_RSA_C
+ *
+ * This enables support for PKCS#1 v1.5 operations.
+ */
+//#define MBEDTLS_PKCS1_V15
+
+/**
+ * \def MBEDTLS_PKCS1_V21
+ *
+ * Enable support for PKCS#1 v2.1 encoding.
+ *
+ * Requires: MBEDTLS_MD_C, MBEDTLS_RSA_C
+ *
+ * This enables support for RSAES-OAEP and RSASSA-PSS operations.
+ */
+//#define MBEDTLS_PKCS1_V21
+
+/**
+ * \def MBEDTLS_RSA_NO_CRT
+ *
+ * Do not use the Chinese Remainder Theorem for the RSA private operation.
+ *
+ * Uncomment this macro to disable the use of CRT in RSA.
+ *
+ */
+//#define MBEDTLS_RSA_NO_CRT
+
+/**
+ * \def MBEDTLS_SELF_TEST
+ *
+ * Enable the checkup functions (*_self_test).
+ */
+//#define MBEDTLS_SELF_TEST
+
+/**
+ * \def MBEDTLS_SHA256_SMALLER
+ *
+ * Enable an implementation of SHA-256 that has lower ROM footprint but also
+ * lower performance.
+ *
+ * The default implementation is meant to be a reasonnable compromise between
+ * performance and size. This version optimizes more aggressively for size at
+ * the expense of performance. Eg on Cortex-M4 it reduces the size of
+ * mbedtls_sha256_process() from ~2KB to ~0.5KB for a performance hit of about
+ * 30%.
+ *
+ * Uncomment to enable the smaller implementation of SHA256.
+ */
+#define MBEDTLS_SHA256_SMALLER
+
+/**
+ * \def MBEDTLS_SSL_AEAD_RANDOM_IV
+ *
+ * Generate a random IV rather than using the record sequence number as a
+ * nonce for ciphersuites using and AEAD algorithm (GCM or CCM).
+ *
+ * Using the sequence number is generally recommended.
+ *
+ * Uncomment this macro to always use random IVs with AEAD ciphersuites.
+ */
+//#define MBEDTLS_SSL_AEAD_RANDOM_IV
+
+/**
+ * \def MBEDTLS_SSL_ALL_ALERT_MESSAGES
+ *
+ * Enable sending of alert messages in case of encountered errors as per RFC.
+ * If you choose not to send the alert messages, mbed TLS can still communicate
+ * with other servers, only debugging of failures is harder.
+ *
+ * The advantage of not sending alert messages, is that no information is given
+ * about reasons for failures thus preventing adversaries of gaining intel.
+ *
+ * Enable sending of all alert messages
+ */
+//#define MBEDTLS_SSL_ALL_ALERT_MESSAGES
+
+/**
+ * \def MBEDTLS_SSL_DEBUG_ALL
+ *
+ * Enable the debug messages in SSL module for all issues.
+ * Debug messages have been disabled in some places to prevent timing
+ * attacks due to (unbalanced) debugging function calls.
+ *
+ * If you need all error reporting you should enable this during debugging,
+ * but remove this for production servers that should log as well.
+ *
+ * Uncomment this macro to report all debug messages on errors introducing
+ * a timing side-channel.
+ *
+ */
+#define MBEDTLS_SSL_DEBUG_ALL
+
+/** \def MBEDTLS_SSL_ENCRYPT_THEN_MAC
+ *
+ * Enable support for Encrypt-then-MAC, RFC 7366.
+ *
+ * This allows peers that both support it to use a more robust protection for
+ * ciphersuites using CBC, providing deep resistance against timing attacks
+ * on the padding or underlying cipher.
+ *
+ * This only affects CBC ciphersuites, and is useless if none is defined.
+ *
+ * Requires: MBEDTLS_SSL_PROTO_TLS1    or
+ *           MBEDTLS_SSL_PROTO_TLS1_1  or
+ *           MBEDTLS_SSL_PROTO_TLS1_2
+ *
+ * Comment this macro to disable support for Encrypt-then-MAC
+ */
+//#define MBEDTLS_SSL_ENCRYPT_THEN_MAC
+
+/** \def MBEDTLS_SSL_EXTENDED_MASTER_SECRET
+ *
+ * Enable support for Extended Master Secret, aka Session Hash
+ * (draft-ietf-tls-session-hash-02).
+ *
+ * This was introduced as "the proper fix" to the Triple Handshake familiy of
+ * attacks, but it is recommended to always use it (even if you disable
+ * renegotiation), since it actually fixes a more fundamental issue in the
+ * original SSL/TLS design, and has implications beyond Triple Handshake.
+ *
+ * Requires: MBEDTLS_SSL_PROTO_TLS1    or
+ *           MBEDTLS_SSL_PROTO_TLS1_1  or
+ *           MBEDTLS_SSL_PROTO_TLS1_2
+ *
+ * Comment this macro to disable support for Extended Master Secret.
+ */
+//#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET
+
+/**
+ * \def MBEDTLS_SSL_FALLBACK_SCSV
+ *
+ * Enable support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv-00).
+ *
+ * For servers, it is recommended to always enable this, unless you support
+ * only one version of TLS, or know for sure that none of your clients
+ * implements a fallback strategy.
+ *
+ * For clients, you only need this if you're using a fallback strategy, which
+ * is not recommended in the first place, unless you absolutely need it to
+ * interoperate with buggy (version-intolerant) servers.
+ *
+ * Comment this macro to disable support for FALLBACK_SCSV
+ */
+//#define MBEDTLS_SSL_FALLBACK_SCSV
+
+/**
+ * \def MBEDTLS_SSL_HW_RECORD_ACCEL
+ *
+ * Enable hooking functions in SSL module for hardware acceleration of
+ * individual records.
+ *
+ * Uncomment this macro to enable hooking functions.
+ */
+//#define MBEDTLS_SSL_HW_RECORD_ACCEL
+
+/**
+ * \def MBEDTLS_SSL_CBC_RECORD_SPLITTING
+ *
+ * Enable 1/n-1 record splitting for CBC mode in SSLv3 and TLS 1.0.
+ *
+ * This is a countermeasure to the BEAST attack, which also minimizes the risk
+ * of interoperability issues compared to sending 0-length records.
+ *
+ * Comment this macro to disable 1/n-1 record splitting.
+ */
+//#define MBEDTLS_SSL_CBC_RECORD_SPLITTING
+
+/**
+ * \def MBEDTLS_SSL_RENEGOTIATION
+ *
+ * Disable support for TLS renegotiation.
+ *
+ * The two main uses of renegotiation are (1) refresh keys on long-lived
+ * connections and (2) client authentication after the initial handshake.
+ * If you don't need renegotiation, it's probably better to disable it, since
+ * it has been associated with security issues in the past and is easy to
+ * misuse/misunderstand.
+ *
+ * Comment this to disable support for renegotiation.
+ */
+//#define MBEDTLS_SSL_RENEGOTIATION
+
+/**
+ * \def MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO
+ *
+ * Enable support for receiving and parsing SSLv2 Client Hello messages for the
+ * SSL Server module (MBEDTLS_SSL_SRV_C).
+ *
+ * Uncomment this macro to enable support for SSLv2 Client Hello messages.
+ */
+//#define MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO
+
+/**
+ * \def MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE
+ *
+ * Pick the ciphersuite according to the client's preferences rather than ours
+ * in the SSL Server module (MBEDTLS_SSL_SRV_C).
+ *
+ * Uncomment this macro to respect client's ciphersuite order
+ */
+//#define MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE
+
+/**
+ * \def MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
+ *
+ * Enable support for RFC 6066 max_fragment_length extension in SSL.
+ *
+ * Comment this macro to disable support for the max_fragment_length extension
+ */
+#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
+
+/**
+ * \def MBEDTLS_SSL_PROTO_SSL3
+ *
+ * Enable support for SSL 3.0.
+ *
+ * Requires: MBEDTLS_MD5_C
+ *           MBEDTLS_SHA1_C
+ *
+ * Comment this macro to disable support for SSL 3.0
+ */
+//#define MBEDTLS_SSL_PROTO_SSL3
+
+/**
+ * \def MBEDTLS_SSL_PROTO_TLS1
+ *
+ * Enable support for TLS 1.0.
+ *
+ * Requires: MBEDTLS_MD5_C
+ *           MBEDTLS_SHA1_C
+ *
+ * Comment this macro to disable support for TLS 1.0
+ */
+//#define MBEDTLS_SSL_PROTO_TLS1
+
+/**
+ * \def MBEDTLS_SSL_PROTO_TLS1_1
+ *
+ * Enable support for TLS 1.1 (and DTLS 1.0 if DTLS is enabled).
+ *
+ * Requires: MBEDTLS_MD5_C
+ *           MBEDTLS_SHA1_C
+ *
+ * Comment this macro to disable support for TLS 1.1 / DTLS 1.0
+ */
+//#define MBEDTLS_SSL_PROTO_TLS1_1
+
+/**
+ * \def MBEDTLS_SSL_PROTO_TLS1_2
+ *
+ * Enable support for TLS 1.2 (and DTLS 1.2 if DTLS is enabled).
+ *
+ * Requires: MBEDTLS_SHA1_C or MBEDTLS_SHA256_C or MBEDTLS_SHA512_C
+ *           (Depends on ciphersuites)
+ *
+ * Comment this macro to disable support for TLS 1.2 / DTLS 1.2
+ */
+#define MBEDTLS_SSL_PROTO_TLS1_2
+
+/**
+ * \def MBEDTLS_SSL_PROTO_DTLS
+ *
+ * Enable support for DTLS (all available versions).
+ *
+ * Enable this and MBEDTLS_SSL_PROTO_TLS1_1 to enable DTLS 1.0,
+ * and/or this and MBEDTLS_SSL_PROTO_TLS1_2 to enable DTLS 1.2.
+ *
+ * Requires: MBEDTLS_SSL_PROTO_TLS1_1
+ *        or MBEDTLS_SSL_PROTO_TLS1_2
+ *
+ * Comment this macro to disable support for DTLS
+ */
+#define MBEDTLS_SSL_PROTO_DTLS
+
+/**
+ * \def MBEDTLS_SSL_ALPN
+ *
+ * Enable support for RFC 7301 Application Layer Protocol Negotiation.
+ *
+ * Comment this macro to disable support for ALPN.
+ */
+//#define MBEDTLS_SSL_ALPN
+
+/**
+ * \def MBEDTLS_SSL_DTLS_ANTI_REPLAY
+ *
+ * Enable support for the anti-replay mechanism in DTLS.
+ *
+ * Requires: MBEDTLS_SSL_TLS_C
+ *           MBEDTLS_SSL_PROTO_DTLS
+ *
+ * \warning Disabling this is often a security risk!
+ * See mbedtls_ssl_conf_dtls_anti_replay() for details.
+ *
+ * Comment this to disable anti-replay in DTLS.
+ */
+#define MBEDTLS_SSL_DTLS_ANTI_REPLAY
+
+/**
+ * \def MBEDTLS_SSL_DTLS_HELLO_VERIFY
+ *
+ * Enable support for HelloVerifyRequest on DTLS servers.
+ *
+ * This feature is highly recommended to prevent DTLS servers being used as
+ * amplifiers in DoS attacks against other hosts. It should always be enabled
+ * unless you know for sure amplification cannot be a problem in the
+ * environment in which your server operates.
+ *
+ * \warning Disabling this can ba a security risk! (see above)
+ *
+ * Requires: MBEDTLS_SSL_PROTO_DTLS
+ *
+ * Comment this to disable support for HelloVerifyRequest.
+ */
+#define MBEDTLS_SSL_DTLS_HELLO_VERIFY
+
+/**
+ * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
+ *
+ * Enable server-side support for clients that reconnect from the same port.
+ *
+ * Some clients unexpectedly close the connection and try to reconnect using the
+ * same source port. This needs special support from the server to handle the
+ * new connection securely, as described in section 4.2.8 of RFC 6347. This
+ * flag enables that support.
+ *
+ * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY
+ *
+ * Comment this to disable support for clients reusing the source port.
+ */
+//#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
+
+/**
+ * \def MBEDTLS_SSL_DTLS_BADMAC_LIMIT
+ *
+ * Enable support for a limit of records with bad MAC.
+ *
+ * See mbedtls_ssl_conf_dtls_badmac_limit().
+ *
+ * Requires: MBEDTLS_SSL_PROTO_DTLS
+ */
+//#define MBEDTLS_SSL_DTLS_BADMAC_LIMIT
+
+/**
+ * \def MBEDTLS_SSL_SESSION_TICKETS
+ *
+ * Enable support for RFC 5077 session tickets in SSL.
+ * Client-side, provides full support for session tickets (maintainance of a
+ * session store remains the responsibility of the application, though).
+ * Server-side, you also need to provide callbacks for writing and parsing
+ * tickets, including authenticated encryption and key management. Example
+ * callbacks are provided by MBEDTLS_SSL_TICKET_C.
+ *
+ * Comment this macro to disable support for SSL session tickets
+ */
+//#define MBEDTLS_SSL_SESSION_TICKETS
+
+/**
+ * \def MBEDTLS_SSL_EXPORT_KEYS
+ *
+ * Enable support for exporting key block and master secret.
+ * This is required for certain users of TLS, e.g. EAP-TLS.
+ *
+ * Comment this macro to disable support for key export
+ */
+#define MBEDTLS_SSL_EXPORT_KEYS
+
+/**
+ * \def MBEDTLS_SSL_SERVER_NAME_INDICATION
+ *
+ * Enable support for RFC 6066 server name indication (SNI) in SSL.
+ *
+ * Requires: MBEDTLS_X509_CRT_PARSE_C
+ *
+ * Comment this macro to disable support for server name indication in SSL
+ */
+//#define MBEDTLS_SSL_SERVER_NAME_INDICATION
+
+/**
+ * \def MBEDTLS_SSL_TRUNCATED_HMAC
+ *
+ * Enable support for RFC 6066 truncated HMAC in SSL.
+ *
+ * Comment this macro to disable support for truncated HMAC in SSL
+ */
+//#define MBEDTLS_SSL_TRUNCATED_HMAC
+
+/**
+ * \def MBEDTLS_THREADING_ALT
+ *
+ * Provide your own alternate threading implementation.
+ *
+ * Requires: MBEDTLS_THREADING_C
+ *
+ * Uncomment this to allow your own alternate threading implementation.
+ */
+//#define MBEDTLS_THREADING_ALT
+
+/**
+ * \def MBEDTLS_THREADING_PTHREAD
+ *
+ * Enable the pthread wrapper layer for the threading layer.
+ *
+ * Requires: MBEDTLS_THREADING_C
+ *
+ * Uncomment this to enable pthread mutexes.
+ */
+//#define MBEDTLS_THREADING_PTHREAD
+
+/**
+ * \def MBEDTLS_VERSION_FEATURES
+ *
+ * Allow run-time checking of compile-time enabled features. Thus allowing users
+ * to check at run-time if the library is for instance compiled with threading
+ * support via mbedtls_version_check_feature().
+ *
+ * Requires: MBEDTLS_VERSION_C
+ *
+ * Comment this to disable run-time checking and save ROM space
+ */
+//#define MBEDTLS_VERSION_FEATURES
+
+/**
+ * \def MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
+ *
+ * If set, the X509 parser will not break-off when parsing an X509 certificate
+ * and encountering an extension in a v1 or v2 certificate.
+ *
+ * Uncomment to prevent an error.
+ */
+//#define MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
+
+/**
+ * \def MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
+ *
+ * If set, the X509 parser will not break-off when parsing an X509 certificate
+ * and encountering an unknown critical extension.
+ *
+ * \warning Depending on your PKI use, enabling this can be a security risk!
+ *
+ * Uncomment to prevent an error.
+ */
+//#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
+
+/**
+ * \def MBEDTLS_X509_CHECK_KEY_USAGE
+ *
+ * Enable verification of the keyUsage extension (CA and leaf certificates).
+ *
+ * Disabling this avoids problems with mis-issued and/or misused
+ * (intermediate) CA and leaf certificates.
+ *
+ * \warning Depending on your PKI use, disabling this can be a security risk!
+ *
+ * Comment to skip keyUsage checking for both CA and leaf certificates.
+ */
+//#define MBEDTLS_X509_CHECK_KEY_USAGE
+
+/**
+ * \def MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
+ *
+ * Enable verification of the extendedKeyUsage extension (leaf certificates).
+ *
+ * Disabling this avoids problems with mis-issued and/or misused certificates.
+ *
+ * \warning Depending on your PKI use, disabling this can be a security risk!
+ *
+ * Comment to skip extendedKeyUsage checking for certificates.
+ */
+//#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
+
+/**
+ * \def MBEDTLS_X509_RSASSA_PSS_SUPPORT
+ *
+ * Enable parsing and verification of X.509 certificates, CRLs and CSRS
+ * signed with RSASSA-PSS (aka PKCS#1 v2.1).
+ *
+ * Comment this macro to disallow using RSASSA-PSS in certificates.
+ */
+//#define MBEDTLS_X509_RSASSA_PSS_SUPPORT
+
+/**
+ * \def MBEDTLS_ZLIB_SUPPORT
+ *
+ * If set, the SSL/TLS module uses ZLIB to support compression and
+ * decompression of packet data.
+ *
+ * \warning TLS-level compression MAY REDUCE SECURITY! See for example the
+ * CRIME attack. Before enabling this option, you should examine with care if
+ * CRIME or similar exploits may be a applicable to your use case.
+ *
+ * \note Currently compression can't be used with DTLS.
+ *
+ * Used in: library/ssl_tls.c
+ *          library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * This feature requires zlib library and headers to be present.
+ *
+ * Uncomment to enable use of ZLIB
+ */
+//#define MBEDTLS_ZLIB_SUPPORT
+/* \} name SECTION: mbed TLS feature support */
+
+/**
+ * \name SECTION: mbed TLS modules
+ *
+ * This section enables or disables entire modules in mbed TLS
+ * \{
+ */
+
+/**
+ * \def MBEDTLS_AESNI_C
+ *
+ * Enable AES-NI support on x86-64.
+ *
+ * Module:  library/aesni.c
+ * Caller:  library/aes.c
+ *
+ * Requires: MBEDTLS_HAVE_ASM
+ *
+ * This modules adds support for the AES-NI instructions on x86-64
+ */
+//#define MBEDTLS_AESNI_C
+
+/**
+ * \def MBEDTLS_AES_C
+ *
+ * Enable the AES block cipher.
+ *
+ * Module:  library/aes.c
+ * Caller:  library/ssl_tls.c
+ *          library/pem.c
+ *          library/ctr_drbg.c
+ *
+ * This module enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
+ *
+ * PEM_PARSE uses AES for decrypting encrypted keys.
+ */
+#define MBEDTLS_AES_C
+
+/**
+ * \def MBEDTLS_ARC4_C
+ *
+ * Enable the ARCFOUR stream cipher.
+ *
+ * Module:  library/arc4.c
+ * Caller:  library/ssl_tls.c
+ *
+ * This module enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_RSA_WITH_RC4_128_MD5
+ *      MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA
+ *      MBEDTLS_TLS_PSK_WITH_RC4_128_SHA
+ */
+//#define MBEDTLS_ARC4_C
+
+/**
+ * \def MBEDTLS_ASN1_PARSE_C
+ *
+ * Enable the generic ASN1 parser.
+ *
+ * Module:  library/asn1.c
+ * Caller:  library/x509.c
+ *          library/dhm.c
+ *          library/pkcs12.c
+ *          library/pkcs5.c
+ *          library/pkparse.c
+ */
+#define MBEDTLS_ASN1_PARSE_C
+
+/**
+ * \def MBEDTLS_ASN1_WRITE_C
+ *
+ * Enable the generic ASN1 writer.
+ *
+ * Module:  library/asn1write.c
+ * Caller:  library/ecdsa.c
+ *          library/pkwrite.c
+ *          library/x509_create.c
+ *          library/x509write_crt.c
+ *          library/mbedtls_x509write_csr.c
+ */
+#define MBEDTLS_ASN1_WRITE_C
+
+/**
+ * \def MBEDTLS_BASE64_C
+ *
+ * Enable the Base64 module.
+ *
+ * Module:  library/base64.c
+ * Caller:  library/pem.c
+ *
+ * This module is required for PEM support (required by X.509).
+ */
+//#define MBEDTLS_BASE64_C
+
+/**
+ * \def MBEDTLS_BIGNUM_C
+ *
+ * Enable the multi-precision integer library.
+ *
+ * Module:  library/bignum.c
+ * Caller:  library/dhm.c
+ *          library/ecp.c
+ *          library/ecdsa.c
+ *          library/rsa.c
+ *          library/ssl_tls.c
+ *
+ * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support.
+ */
+#define MBEDTLS_BIGNUM_C
+
+/**
+ * \def MBEDTLS_BLOWFISH_C
+ *
+ * Enable the Blowfish block cipher.
+ *
+ * Module:  library/blowfish.c
+ */
+//#define MBEDTLS_BLOWFISH_C
+
+/**
+ * \def MBEDTLS_CAMELLIA_C
+ *
+ * Enable the Camellia block cipher.
+ *
+ * Module:  library/camellia.c
+ * Caller:  library/ssl_tls.c
+ *
+ * This module enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256
+ *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256
+ */
+//#define MBEDTLS_CAMELLIA_C
+
+/**
+ * \def MBEDTLS_CCM_C
+ *
+ * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher.
+ *
+ * Module:  library/ccm.c
+ *
+ * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C
+ *
+ * This module enables the AES-CCM ciphersuites, if other requisites are
+ * enabled as well.
+ */
+#define MBEDTLS_CCM_C
+
+/**
+ * \def MBEDTLS_CERTS_C
+ *
+ * Enable the test certificates.
+ *
+ * Module:  library/certs.c
+ * Caller:
+ *
+ * This module is used for testing (ssl_client/server).
+ */
+//#define MBEDTLS_CERTS_C
+
+/**
+ * \def MBEDTLS_CIPHER_C
+ *
+ * Enable the generic cipher layer.
+ *
+ * Module:  library/cipher.c
+ * Caller:  library/ssl_tls.c
+ *
+ * Uncomment to enable generic cipher wrappers.
+ */
+#define MBEDTLS_CIPHER_C
+
+/**
+ * \def MBEDTLS_CTR_DRBG_C
+ *
+ * Enable the CTR_DRBG AES-256-based random generator.
+ *
+ * Module:  library/ctr_drbg.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_AES_C
+ *
+ * This module provides the CTR_DRBG AES-256 random number generator.
+ */
+#define MBEDTLS_CTR_DRBG_C
+
+/**
+ * \def MBEDTLS_DEBUG_C
+ *
+ * Enable the debug functions.
+ *
+ * Module:  library/debug.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *          library/ssl_tls.c
+ *
+ * This module provides debugging functions.
+ */
+#define MBEDTLS_DEBUG_C
+
+/**
+ * \def MBEDTLS_DES_C
+ *
+ * Enable the DES block cipher.
+ *
+ * Module:  library/des.c
+ * Caller:  library/pem.c
+ *          library/ssl_tls.c
+ *
+ * This module enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
+ *      MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA
+ *
+ * PEM_PARSE uses DES/3DES for decrypting encrypted keys.
+ */
+//#define MBEDTLS_DES_C
+
+/**
+ * \def MBEDTLS_DHM_C
+ *
+ * Enable the Diffie-Hellman-Merkle module.
+ *
+ * Module:  library/dhm.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * This module is used by the following key exchanges:
+ *      DHE-RSA, DHE-PSK
+ */
+//#define MBEDTLS_DHM_C
+
+/**
+ * \def MBEDTLS_ECDH_C
+ *
+ * Enable the elliptic curve Diffie-Hellman library.
+ *
+ * Module:  library/ecdh.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * This module is used by the following key exchanges:
+ *      ECDHE-ECDSA, ECDHE-RSA, DHE-PSK
+ *
+ * Requires: MBEDTLS_ECP_C
+ */
+//#define MBEDTLS_ECDH_C
+
+/**
+ * \def MBEDTLS_ECDSA_C
+ *
+ * Enable the elliptic curve DSA library.
+ *
+ * Module:  library/ecdsa.c
+ * Caller:
+ *
+ * This module is used by the following key exchanges:
+ *      ECDHE-ECDSA
+ *
+ * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C
+ */
+//#define MBEDTLS_ECDSA_C
+
+/**
+ * \def MBEDTLS_ECJPAKE_C
+ *
+ * Enable the elliptic curve J-PAKE library.
+ *
+ * \warning This is currently experimental. EC J-PAKE support is based on the
+ * Thread v1.0.0 specification; incompatible changes to the specification
+ * might still happen. For this reason, this is disabled by default.
+ *
+ * Module:  library/ecjpake.c
+ * Caller:
+ *
+ * This module is used by the following key exchanges:
+ *      ECJPAKE
+ *
+ * Requires: MBEDTLS_ECP_C, MBEDTLS_MD_C
+ */
+#define MBEDTLS_ECJPAKE_C
+
+/**
+ * \def MBEDTLS_ECP_C
+ *
+ * Enable the elliptic curve over GF(p) library.
+ *
+ * Module:  library/ecp.c
+ * Caller:  library/ecdh.c
+ *          library/ecdsa.c
+ *          library/ecjpake.c
+ *
+ * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED
+ */
+#define MBEDTLS_ECP_C
+
+/**
+ * \def MBEDTLS_ENTROPY_C
+ *
+ * Enable the platform-specific entropy code.
+ *
+ * Module:  library/entropy.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C
+ *
+ * This module provides a generic entropy pool
+ */
+#define MBEDTLS_ENTROPY_C
+
+/**
+ * \def MBEDTLS_ERROR_C
+ *
+ * Enable error code to error string conversion.
+ *
+ * Module:  library/error.c
+ * Caller:
+ *
+ * This module enables mbedtls_strerror().
+ */
+//#define MBEDTLS_ERROR_C
+
+/**
+ * \def MBEDTLS_GCM_C
+ *
+ * Enable the Galois/Counter Mode (GCM) for AES.
+ *
+ * Module:  library/gcm.c
+ *
+ * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C
+ *
+ * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other
+ * requisites are enabled as well.
+ */
+//#define MBEDTLS_GCM_C
+
+/**
+ * \def MBEDTLS_HAVEGE_C
+ *
+ * Enable the HAVEGE random generator.
+ *
+ * Warning: the HAVEGE random generator is not suitable for virtualized
+ *          environments
+ *
+ * Warning: the HAVEGE random generator is dependent on timing and specific
+ *          processor traits. It is therefore not advised to use HAVEGE as
+ *          your applications primary random generator or primary entropy pool
+ *          input. As a secondary input to your entropy pool, it IS able add
+ *          the (limited) extra entropy it provides.
+ *
+ * Module:  library/havege.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_TIMING_C
+ *
+ * Uncomment to enable the HAVEGE random generator.
+ */
+//#define MBEDTLS_HAVEGE_C
+
+/**
+ * \def MBEDTLS_HMAC_DRBG_C
+ *
+ * Enable the HMAC_DRBG random generator.
+ *
+ * Module:  library/hmac_drbg.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_MD_C
+ *
+ * Uncomment to enable the HMAC_DRBG random number geerator.
+ */
+#define MBEDTLS_HMAC_DRBG_C
+
+/**
+ * \def MBEDTLS_MD_C
+ *
+ * Enable the generic message digest layer.
+ *
+ * Module:  library/mbedtls_md.c
+ * Caller:
+ *
+ * Uncomment to enable generic message digest wrappers.
+ */
+#define MBEDTLS_MD_C
+
+/**
+ * \def MBEDTLS_MD2_C
+ *
+ * Enable the MD2 hash algorithm.
+ *
+ * Module:  library/mbedtls_md2.c
+ * Caller:
+ *
+ * Uncomment to enable support for (rare) MD2-signed X.509 certs.
+ */
+//#define MBEDTLS_MD2_C
+
+/**
+ * \def MBEDTLS_MD4_C
+ *
+ * Enable the MD4 hash algorithm.
+ *
+ * Module:  library/mbedtls_md4.c
+ * Caller:
+ *
+ * Uncomment to enable support for (rare) MD4-signed X.509 certs.
+ */
+//#define MBEDTLS_MD4_C
+
+/**
+ * \def MBEDTLS_MD5_C
+ *
+ * Enable the MD5 hash algorithm.
+ *
+ * Module:  library/mbedtls_md5.c
+ * Caller:  library/mbedtls_md.c
+ *          library/pem.c
+ *          library/ssl_tls.c
+ *
+ * This module is required for SSL/TLS and X.509.
+ * PEM_PARSE uses MD5 for decrypting encrypted keys.
+ */
+//#define MBEDTLS_MD5_C
+
+/**
+ * \def MBEDTLS_MEMORY_BUFFER_ALLOC_C
+ *
+ * Enable the buffer allocator implementation that makes use of a (stack)
+ * based buffer to 'allocate' dynamic memory. (replaces calloc() and free()
+ * calls)
+ *
+ * Module:  library/memory_buffer_alloc.c
+ *
+ * Requires: MBEDTLS_PLATFORM_C
+ *           MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS)
+ *
+ * Enable this module to enable the buffer memory allocator.
+ */
+#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
+
+/**
+ * \def MBEDTLS_NET_C
+ *
+ * Enable the TCP and UDP over IPv6/IPv4 networking routines.
+ *
+ * \note This module only works on POSIX/Unix (including Linux, BSD and OS X)
+ * and Windows. For other platforms, you'll want to disable it, and write your
+ * own networking callbacks to be passed to \c mbedtls_ssl_set_bio().
+ *
+ * \note See also our Knowledge Base article about porting to a new
+ * environment:
+ * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ *
+ * Module:  library/net.c
+ *
+ * This module provides networking routines.
+ */
+//#define MBEDTLS_NET_C
+
+/**
+ * \def MBEDTLS_OID_C
+ *
+ * Enable the OID database.
+ *
+ * Module:  library/oid.c
+ * Caller:  library/asn1write.c
+ *          library/pkcs5.c
+ *          library/pkparse.c
+ *          library/pkwrite.c
+ *          library/rsa.c
+ *          library/x509.c
+ *          library/x509_create.c
+ *          library/mbedtls_x509_crl.c
+ *          library/mbedtls_x509_crt.c
+ *          library/mbedtls_x509_csr.c
+ *          library/x509write_crt.c
+ *          library/mbedtls_x509write_csr.c
+ *
+ * This modules translates between OIDs and internal values.
+ */
+#define MBEDTLS_OID_C
+
+/**
+ * \def MBEDTLS_PADLOCK_C
+ *
+ * Enable VIA Padlock support on x86.
+ *
+ * Module:  library/padlock.c
+ * Caller:  library/aes.c
+ *
+ * Requires: MBEDTLS_HAVE_ASM
+ *
+ * This modules adds support for the VIA PadLock on x86.
+ */
+//#define MBEDTLS_PADLOCK_C
+
+/**
+ * \def MBEDTLS_PEM_PARSE_C
+ *
+ * Enable PEM decoding / parsing.
+ *
+ * Module:  library/pem.c
+ * Caller:  library/dhm.c
+ *          library/pkparse.c
+ *          library/mbedtls_x509_crl.c
+ *          library/mbedtls_x509_crt.c
+ *          library/mbedtls_x509_csr.c
+ *
+ * Requires: MBEDTLS_BASE64_C
+ *
+ * This modules adds support for decoding / parsing PEM files.
+ */
+//#define MBEDTLS_PEM_PARSE_C
+
+/**
+ * \def MBEDTLS_PEM_WRITE_C
+ *
+ * Enable PEM encoding / writing.
+ *
+ * Module:  library/pem.c
+ * Caller:  library/pkwrite.c
+ *          library/x509write_crt.c
+ *          library/mbedtls_x509write_csr.c
+ *
+ * Requires: MBEDTLS_BASE64_C
+ *
+ * This modules adds support for encoding / writing PEM files.
+ */
+//#define MBEDTLS_PEM_WRITE_C
+
+/**
+ * \def MBEDTLS_PK_C
+ *
+ * Enable the generic public (asymetric) key layer.
+ *
+ * Module:  library/pk.c
+ * Caller:  library/ssl_tls.c
+ *          library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * Requires: MBEDTLS_RSA_C or MBEDTLS_ECP_C
+ *
+ * Uncomment to enable generic public key wrappers.
+ */
+#define MBEDTLS_PK_C
+
+/**
+ * \def MBEDTLS_PK_PARSE_C
+ *
+ * Enable the generic public (asymetric) key parser.
+ *
+ * Module:  library/pkparse.c
+ * Caller:  library/mbedtls_x509_crt.c
+ *          library/mbedtls_x509_csr.c
+ *
+ * Requires: MBEDTLS_PK_C
+ *
+ * Uncomment to enable generic public key parse functions.
+ */
+#define MBEDTLS_PK_PARSE_C
+
+/**
+ * \def MBEDTLS_PK_WRITE_C
+ *
+ * Enable the generic public (asymetric) key writer.
+ *
+ * Module:  library/pkwrite.c
+ * Caller:  library/x509write.c
+ *
+ * Requires: MBEDTLS_PK_C
+ *
+ * Uncomment to enable generic public key write functions.
+ */
+//#define MBEDTLS_PK_WRITE_C
+
+/**
+ * \def MBEDTLS_PKCS5_C
+ *
+ * Enable PKCS#5 functions.
+ *
+ * Module:  library/pkcs5.c
+ *
+ * Requires: MBEDTLS_MD_C
+ *
+ * This module adds support for the PKCS#5 functions.
+ */
+//#define MBEDTLS_PKCS5_C
+
+/**
+ * \def MBEDTLS_PKCS11_C
+ *
+ * Enable wrapper for PKCS#11 smartcard support.
+ *
+ * Module:  library/pkcs11.c
+ * Caller:  library/pk.c
+ *
+ * Requires: MBEDTLS_PK_C
+ *
+ * This module enables SSL/TLS PKCS #11 smartcard support.
+ * Requires the presence of the PKCS#11 helper library (libpkcs11-helper)
+ */
+//#define MBEDTLS_PKCS11_C
+
+/**
+ * \def MBEDTLS_PKCS12_C
+ *
+ * Enable PKCS#12 PBE functions.
+ * Adds algorithms for parsing PKCS#8 encrypted private keys
+ *
+ * Module:  library/pkcs12.c
+ * Caller:  library/pkparse.c
+ *
+ * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_CIPHER_C, MBEDTLS_MD_C
+ * Can use:  MBEDTLS_ARC4_C
+ *
+ * This module enables PKCS#12 functions.
+ */
+//#define MBEDTLS_PKCS12_C
+
+/**
+ * \def MBEDTLS_PLATFORM_C
+ *
+ * Enable the platform abstraction layer that allows you to re-assign
+ * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit().
+ *
+ * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT
+ * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned
+ * above to be specified at runtime or compile time respectively.
+ *
+ * \note This abstraction layer must be enabled on Windows (including MSYS2)
+ * as other module rely on it for a fixed snprintf implementation.
+ *
+ * Module:  library/platform.c
+ * Caller:  Most other .c files
+ *
+ * This module enables abstraction of common (libc) functions.
+ */
+#define MBEDTLS_PLATFORM_C
+
+/**
+ * \def MBEDTLS_RIPEMD160_C
+ *
+ * Enable the RIPEMD-160 hash algorithm.
+ *
+ * Module:  library/mbedtls_ripemd160.c
+ * Caller:  library/mbedtls_md.c
+ *
+ */
+//#define MBEDTLS_RIPEMD160_C
+
+/**
+ * \def MBEDTLS_RSA_C
+ *
+ * Enable the RSA public-key cryptosystem.
+ *
+ * Module:  library/rsa.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *          library/ssl_tls.c
+ *          library/x509.c
+ *
+ * This module is used by the following key exchanges:
+ *      RSA, DHE-RSA, ECDHE-RSA, RSA-PSK
+ *
+ * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C
+ */
+//#define MBEDTLS_RSA_C
+
+/**
+ * \def MBEDTLS_SHA1_C
+ *
+ * Enable the SHA1 cryptographic hash algorithm.
+ *
+ * Module:  library/mbedtls_sha1.c
+ * Caller:  library/mbedtls_md.c
+ *          library/ssl_cli.c
+ *          library/ssl_srv.c
+ *          library/ssl_tls.c
+ *          library/x509write_crt.c
+ *
+ * This module is required for SSL/TLS and SHA1-signed certificates.
+ */
+//#define MBEDTLS_SHA1_C
+
+/**
+ * \def MBEDTLS_SHA256_C
+ *
+ * Enable the SHA-224 and SHA-256 cryptographic hash algorithms.
+ *
+ * Module:  library/mbedtls_sha256.c
+ * Caller:  library/entropy.c
+ *          library/mbedtls_md.c
+ *          library/ssl_cli.c
+ *          library/ssl_srv.c
+ *          library/ssl_tls.c
+ *
+ * This module adds support for SHA-224 and SHA-256.
+ * This module is required for the SSL/TLS 1.2 PRF function.
+ */
+#define MBEDTLS_SHA256_C
+
+/**
+ * \def MBEDTLS_SHA512_C
+ *
+ * Enable the SHA-384 and SHA-512 cryptographic hash algorithms.
+ *
+ * Module:  library/mbedtls_sha512.c
+ * Caller:  library/entropy.c
+ *          library/mbedtls_md.c
+ *          library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * This module adds support for SHA-384 and SHA-512.
+ */
+//#define MBEDTLS_SHA512_C
+
+/**
+ * \def MBEDTLS_SSL_CACHE_C
+ *
+ * Enable simple SSL cache implementation.
+ *
+ * Module:  library/ssl_cache.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_SSL_CACHE_C
+ */
+//#define MBEDTLS_SSL_CACHE_C
+
+/**
+ * \def MBEDTLS_SSL_COOKIE_C
+ *
+ * Enable basic implementation of DTLS cookies for hello verification.
+ *
+ * Module:  library/ssl_cookie.c
+ * Caller:
+ */
+#define MBEDTLS_SSL_COOKIE_C
+
+/**
+ * \def MBEDTLS_SSL_TICKET_C
+ *
+ * Enable an implementation of TLS server-side callbacks for session tickets.
+ *
+ * Module:  library/ssl_ticket.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_CIPHER_C
+ */
+//#define MBEDTLS_SSL_TICKET_C
+
+/**
+ * \def MBEDTLS_SSL_CLI_C
+ *
+ * Enable the SSL/TLS client code.
+ *
+ * Module:  library/ssl_cli.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_SSL_TLS_C
+ *
+ * This module is required for SSL/TLS client support.
+ */
+#define MBEDTLS_SSL_CLI_C
+
+/**
+ * \def MBEDTLS_SSL_SRV_C
+ *
+ * Enable the SSL/TLS server code.
+ *
+ * Module:  library/ssl_srv.c
+ * Caller:
+ *
+ * Requires: MBEDTLS_SSL_TLS_C
+ *
+ * This module is required for SSL/TLS server support.
+ */
+#define MBEDTLS_SSL_SRV_C
+
+/**
+ * \def MBEDTLS_SSL_TLS_C
+ *
+ * Enable the generic SSL/TLS code.
+ *
+ * Module:  library/ssl_tls.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *
+ * Requires: MBEDTLS_CIPHER_C, MBEDTLS_MD_C
+ *           and at least one of the MBEDTLS_SSL_PROTO_XXX defines
+ *
+ * This module is required for SSL/TLS.
+ */
+#define MBEDTLS_SSL_TLS_C
+
+/**
+ * \def MBEDTLS_THREADING_C
+ *
+ * Enable the threading abstraction layer.
+ * By default mbed TLS assumes it is used in a non-threaded environment or that
+ * contexts are not shared between threads. If you do intend to use contexts
+ * between threads, you will need to enable this layer to prevent race
+ * conditions. See also our Knowledge Base article about threading:
+ * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading
+ *
+ * Module:  library/threading.c
+ *
+ * This allows different threading implementations (self-implemented or
+ * provided).
+ *
+ * You will have to enable either MBEDTLS_THREADING_ALT or
+ * MBEDTLS_THREADING_PTHREAD.
+ *
+ * Enable this layer to allow use of mutexes within mbed TLS
+ */
+//#define MBEDTLS_THREADING_C
+
+/**
+ * \def MBEDTLS_TIMING_C
+ *
+ * Enable the semi-portable timing interface.
+ *
+ * \note The provided implementation only works on POSIX/Unix (including Linux,
+ * BSD and OS X) and Windows. On other platforms, you can either disable that
+ * module and provide your own implementations of the callbacks needed by
+ * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide
+ * your own implementation of the whole module by setting
+ * \c MBEDTLS_TIMING_ALT in the current file.
+ *
+ * \note See also our Knowledge Base article about porting to a new
+ * environment:
+ * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ *
+ * Module:  library/timing.c
+ * Caller:  library/havege.c
+ *
+ * This module is used by the HAVEGE random number generator.
+ */
+//#define MBEDTLS_TIMING_C
+
+/**
+ * \def MBEDTLS_VERSION_C
+ *
+ * Enable run-time version information.
+ *
+ * Module:  library/version.c
+ *
+ * This module provides run-time version information.
+ */
+//#define MBEDTLS_VERSION_C
+
+/**
+ * \def MBEDTLS_X509_USE_C
+ *
+ * Enable X.509 core for using certificates.
+ *
+ * Module:  library/x509.c
+ * Caller:  library/mbedtls_x509_crl.c
+ *          library/mbedtls_x509_crt.c
+ *          library/mbedtls_x509_csr.c
+ *
+ * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C,
+ *           MBEDTLS_PK_PARSE_C
+ *
+ * This module is required for the X.509 parsing modules.
+ */
+//#define MBEDTLS_X509_USE_C
+
+/**
+ * \def MBEDTLS_X509_CRT_PARSE_C
+ *
+ * Enable X.509 certificate parsing.
+ *
+ * Module:  library/mbedtls_x509_crt.c
+ * Caller:  library/ssl_cli.c
+ *          library/ssl_srv.c
+ *          library/ssl_tls.c
+ *
+ * Requires: MBEDTLS_X509_USE_C
+ *
+ * This module is required for X.509 certificate parsing.
+ */
+//#define MBEDTLS_X509_CRT_PARSE_C
+
+/**
+ * \def MBEDTLS_X509_CRL_PARSE_C
+ *
+ * Enable X.509 CRL parsing.
+ *
+ * Module:  library/mbedtls_x509_crl.c
+ * Caller:  library/mbedtls_x509_crt.c
+ *
+ * Requires: MBEDTLS_X509_USE_C
+ *
+ * This module is required for X.509 CRL parsing.
+ */
+//#define MBEDTLS_X509_CRL_PARSE_C
+
+/**
+ * \def MBEDTLS_X509_CSR_PARSE_C
+ *
+ * Enable X.509 Certificate Signing Request (CSR) parsing.
+ *
+ * Module:  library/mbedtls_x509_csr.c
+ * Caller:  library/x509_crt_write.c
+ *
+ * Requires: MBEDTLS_X509_USE_C
+ *
+ * This module is used for reading X.509 certificate request.
+ */
+//#define MBEDTLS_X509_CSR_PARSE_C
+
+/**
+ * \def MBEDTLS_X509_CREATE_C
+ *
+ * Enable X.509 core for creating certificates.
+ *
+ * Module:  library/x509_create.c
+ *
+ * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_WRITE_C
+ *
+ * This module is the basis for creating X.509 certificates and CSRs.
+ */
+//#define MBEDTLS_X509_CREATE_C
+
+/**
+ * \def MBEDTLS_X509_CRT_WRITE_C
+ *
+ * Enable creating X.509 certificates.
+ *
+ * Module:  library/x509_crt_write.c
+ *
+ * Requires: MBEDTLS_X509_CREATE_C
+ *
+ * This module is required for X.509 certificate creation.
+ */
+//#define MBEDTLS_X509_CRT_WRITE_C
+
+/**
+ * \def MBEDTLS_X509_CSR_WRITE_C
+ *
+ * Enable creating X.509 Certificate Signing Requests (CSR).
+ *
+ * Module:  library/x509_csr_write.c
+ *
+ * Requires: MBEDTLS_X509_CREATE_C
+ *
+ * This module is required for X.509 certificate request writing.
+ */
+//#define MBEDTLS_X509_CSR_WRITE_C
+
+/**
+ * \def MBEDTLS_XTEA_C
+ *
+ * Enable the XTEA block cipher.
+ *
+ * Module:  library/xtea.c
+ * Caller:
+ */
+//#define MBEDTLS_XTEA_C
+
+/* \} name SECTION: mbed TLS modules */
+
+/**
+ * \name SECTION: Module configuration options
+ *
+ * This section allows for the setting of module specific sizes and
+ * configuration options. The default values are already present in the
+ * relevant header files and should suffice for the regular use cases.
+ *
+ * Our advice is to enable options and change their values here
+ * only if you have a good reason and know the consequences.
+ *
+ * Please check the respective header file for documentation on these
+ * parameters (to prevent duplicate documentation).
+ * \{
+ */
+
+/* MPI / BIGNUM options */
+#define MBEDTLS_MPI_WINDOW_SIZE            1 /**< Maximum windows size used. */
+#define MBEDTLS_MPI_MAX_SIZE            32 /**< Maximum number of bytes for usable MPIs. */
+
+/* CTR_DRBG options */
+//#define MBEDTLS_CTR_DRBG_ENTROPY_LEN               48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */
+//#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL        10000 /**< Interval before reseed is performed by default */
+//#define MBEDTLS_CTR_DRBG_MAX_INPUT                256 /**< Maximum number of additional input bytes */
+//#define MBEDTLS_CTR_DRBG_MAX_REQUEST             1024 /**< Maximum number of requested bytes per call */
+//#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT           384 /**< Maximum size of (re)seed buffer */
+
+/* HMAC_DRBG options */
+//#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL   10000 /**< Interval before reseed is performed by default */
+//#define MBEDTLS_HMAC_DRBG_MAX_INPUT           256 /**< Maximum number of additional input bytes */
+//#define MBEDTLS_HMAC_DRBG_MAX_REQUEST        1024 /**< Maximum number of requested bytes per call */
+//#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT      384 /**< Maximum size of (re)seed buffer */
+
+/* ECP options */
+#define MBEDTLS_ECP_MAX_BITS             256 /**< Maximum bit size of groups */
+#define MBEDTLS_ECP_WINDOW_SIZE            2 /**< Maximum window size used */
+#define MBEDTLS_ECP_FIXED_POINT_OPTIM      0 /**< Enable fixed-point speed-up */
+
+/* Entropy options */
+//#define MBEDTLS_ENTROPY_MAX_SOURCES                20 /**< Maximum number of sources supported */
+//#define MBEDTLS_ENTROPY_MAX_GATHER                128 /**< Maximum amount requested from entropy sources */
+
+/* Memory buffer allocator options */
+//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE      4 /**< Align on multiples of this value */
+
+/* Platform options */
+//#define MBEDTLS_PLATFORM_STD_MEM_HDR   <stdlib.h> /**< Header to include if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */
+//#define MBEDTLS_PLATFORM_STD_CALLOC        calloc /**< Default allocator to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_FREE            free /**< Default free to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_EXIT            exit /**< Default exit to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_TIME            time /**< Default time to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_FPRINTF      fprintf /**< Default fprintf to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_PRINTF        printf /**< Default printf to use, can be undefined */
+/* Note: your snprintf must correclty zero-terminate the buffer! */
+//#define MBEDTLS_PLATFORM_STD_SNPRINTF    snprintf /**< Default snprintf to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS       0 /**< Default exit value to use, can be undefined */
+//#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE       1 /**< Default exit value to use, can be undefined */
+
+/* To Use Function Macros MBEDTLS_PLATFORM_C must be enabled */
+/* MBEDTLS_PLATFORM_XXX_MACRO and MBEDTLS_PLATFORM_XXX_ALT cannot both be defined */
+//#define MBEDTLS_PLATFORM_CALLOC_MACRO        calloc /**< Default allocator macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_FREE_MACRO            free /**< Default free macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_EXIT_MACRO            exit /**< Default exit macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_TIME_MACRO            time /**< Default time macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_TIME_TYPE_MACRO     uint32_t /**< Default time macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_FPRINTF_MACRO      fprintf /**< Default fprintf macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_PRINTF_MACRO        printf /**< Default printf macro to use, can be undefined */
+/* Note: your snprintf must correclty zero-terminate the buffer! */
+//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO    snprintf /**< Default snprintf macro to use, can be undefined */
+
+/* SSL Cache options */
+//#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT       86400 /**< 1 day  */
+//#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES      50 /**< Maximum entries in cache */
+
+/* SSL options */
+#define MBEDTLS_SSL_MAX_CONTENT_LEN             768 /**< Maxium fragment length in bytes, determines the size of each of the two internal I/O buffers */
+//#define MBEDTLS_SSL_DEFAULT_TICKET_LIFETIME     86400 /**< Lifetime of session tickets (if enabled) */
+//#define MBEDTLS_PSK_MAX_LEN               32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */
+//#define MBEDTLS_SSL_COOKIE_TIMEOUT        60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */
+
+/**
+ * Complete list of ciphersuites to use, in order of preference.
+ *
+ * \warning No dependency checking is done on that field! This option can only
+ * be used to restrict the set of available ciphersuites. It is your
+ * responsibility to make sure the needed modules are active.
+ *
+ * Use this to save a few hundred bytes of ROM (default ordering of all
+ * available ciphersuites) and a few to a few hundred bytes of RAM.
+ *
+ * The value below is only an example, not the default.
+ */
+#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
+
+/* X509 options */
+//#define MBEDTLS_X509_MAX_INTERMEDIATE_CA   8   /**< Maximum number of intermediate CAs in a verification chain. */
+
+/* \} name SECTION: Module configuration options */
+
+#if defined(TARGET_LIKE_MBED)
+#include "mbedtls/target_config.h"
+#endif
+
+/*
+ * Allow user to override any previous default.
+ *
+ * Use two macro names for that, as:
+ * - with yotta the prefix YOTTA_CFG_ is forced
+ * - without yotta is looks weird to have a YOTTA prefix.
+ */
+#if defined(YOTTA_CFG_MBEDTLS_USER_CONFIG_FILE)
+#include YOTTA_CFG_MBEDTLS_USER_CONFIG_FILE
+#elif defined(MBEDTLS_USER_CONFIG_FILE)
+#include MBEDTLS_USER_CONFIG_FILE
+#endif
+
+#include "mbedtls/check_config.h"
+
+#endif /* MBEDTLS_CONFIG_H */
diff --git a/examples/platforms/cc2650/crypto/sha256_alt.c b/examples/platforms/cc2650/crypto/sha256_alt.c
new file mode 100644
index 0000000..1f3e794
--- /dev/null
+++ b/examples/platforms/cc2650/crypto/sha256_alt.c
@@ -0,0 +1,123 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "sha256_alt.h"
+
+#ifdef MBEDTLS_SHA256_ALT
+
+#include <string.h>
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_init(mbedtls_sha256_context *ctx)
+{
+    memset((void *)ctx, 0x00, sizeof(ctx));
+}
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_free(mbedtls_sha256_context *ctx)
+{
+    memset((void *)ctx, 0x00, sizeof(ctx));
+}
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_clone(mbedtls_sha256_context *dst, const mbedtls_sha256_context *src)
+{
+    *dst = *src;
+}
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224)
+{
+    SHA256_initialize(ctx);
+
+    if (is224 != 0)
+    {
+        /* SHA-224 */
+        ctx->state[0] = 0xC1059ED8;
+        ctx->state[1] = 0x367CD507;
+        ctx->state[2] = 0x3070DD17;
+        ctx->state[3] = 0xF70E5939;
+        ctx->state[4] = 0xFFC00B31;
+        ctx->state[5] = 0x68581511;
+        ctx->state[6] = 0x64F98FA7;
+        ctx->state[7] = 0xBEFA4FA4;
+    }
+}
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_update(mbedtls_sha256_context *ctx, const unsigned char *input, size_t ilen)
+{
+    SHA256_execute(ctx, (uint8_t *)input, (uint32_t)ilen);
+}
+
+char *workaround_cc2650_rom;
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_finish(mbedtls_sha256_context *ctx, unsigned char output[32])
+{
+    /* workaround for error in copy subroutine of SHA256 ROM implementation.
+     * Allocate an extra 64 bytes on the stack to make sure we have buffer
+     * room. This could be optomized out if you never call this function with
+     * a call stack shorter than 16 words, approx. 8 stack frames.
+     *
+     * The simple description is this:
+     *    If the stack pointer is within 64bytes of the end of RAM
+     *    the bug exposes it self.
+     *    If the stack pointer is more then 64bytes from end of RAM
+     *    There is no bug...
+     * Solution:
+     *    Make a 64byte buffer on the stack..
+     *    And force the compiler to think it requires this buffer.
+     */
+    char buffer[ 64 ];
+    workaround_cc2650_rom = &buffer[0];
+    SHA256_output(ctx, (uint8_t *)output);
+    return;
+}
+
+/**
+ * documented in sha256_alt.h
+ */
+void mbedtls_sha256_process(mbedtls_sha256_context *ctx, const unsigned char data[64])
+{
+    SHA256_execute(ctx, (uint8_t *)data, sizeof(unsigned char) * 64);
+}
+
+#endif /* MBEDTLS_SHA256_ALT */
diff --git a/examples/platforms/cc2650/crypto/sha256_alt.h b/examples/platforms/cc2650/crypto/sha256_alt.h
new file mode 100644
index 0000000..a674eb2
--- /dev/null
+++ b/examples/platforms/cc2650/crypto/sha256_alt.h
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MBEDTLS_SHA256_ALT_H
+#define MBEDTLS_SHA256_ALT_H
+
+#ifndef MBEDTLS_CONFIG_FILE
+#include "cc2650-mbedtls-config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#ifdef MBEDTLS_SHA256_ALT
+
+#include "driverlib/rom_crypto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief translating the mbedtls SHA256 workspace to the cc2650 SHA256 workspace.
+ */
+typedef SHA256_memory_t mbedtls_sha256_context;
+
+/**
+ * @brief Initialize SHA-256 context
+ *
+ * @param [in,out] ctx SHA-256 context to be initialized
+ */
+void mbedtls_sha256_init(mbedtls_sha256_context *ctx);
+
+/**
+ * @brief Clear SHA-256 context
+ *
+ * @param [in,out] ctx SHA-256 context to be cleared
+ */
+void mbedtls_sha256_free(mbedtls_sha256_context *ctx);
+
+/**
+ * @brief Clone (the state of) a SHA-256 context
+ *
+ * @param [out] dst The destination context
+ * @param [in] src The context to be cloned
+ */
+void mbedtls_sha256_clone(mbedtls_sha256_context *dst,
+                          const mbedtls_sha256_context *src);
+
+/**
+ * @brief SHA-256 context setup
+ *
+ * @param [in,out] ctx context to be initialized
+ * @param [in] is224 0 = use SHA256, 1 = use SHA224
+ */
+void mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224);
+
+/**
+ * @brief SHA-256 process buffer
+ *
+ * @param [in,out] ctx SHA-256 context
+ * @param [in] input buffer holding the  data
+ * @param [in] ilen length of the input data
+ */
+void mbedtls_sha256_update(mbedtls_sha256_context *ctx, const unsigned char *input, size_t ilen);
+
+/**
+ * @brief SHA-256 final digest
+ *
+ * @param [in,out] ctx SHA-256 context
+ * @param [out] output SHA-224/256 checksum result
+ */
+void mbedtls_sha256_finish(mbedtls_sha256_context *ctx, unsigned char output[32]);
+
+/* Internal use */
+void mbedtls_sha256_process(mbedtls_sha256_context *ctx, const unsigned char data[64]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MBEDTLS_SHA256_ALT */
+
+#endif /* MBEDTLS_SHA256_ALT_H */
diff --git a/examples/platforms/cc2650/diag.c b/examples/platforms/cc2650/diag.c
new file mode 100644
index 0000000..c59285f
--- /dev/null
+++ b/examples/platforms/cc2650/diag.c
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <openthread/platform/diag.h>
+
+
+/**
+ * Diagnostics mode variables.
+ *
+ */
+static bool sDiagMode = false;
+
+void otPlatDiagProcess(otInstance *aInstance, int argc, char *argv[], char *aOutput, size_t aOutputMaxLen)
+{
+    // Add more plarform specific diagnostics features here.
+    if (argc > 1)
+    {
+        snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", argv[0]);
+    }
+
+    (void) argc;
+    (void) aInstance;
+}
+
+void otPlatDiagModeSet(bool aMode)
+{
+    sDiagMode = aMode;
+}
+
+bool otPlatDiagModeGet()
+{
+    return sDiagMode;
+}
+
+void otPlatDiagChannelSet(uint8_t aChannel)
+{
+    (void) aChannel;
+}
+
+void otPlatDiagTxPowerSet(int8_t aTxPower)
+{
+    (void) aTxPower;
+}
+
+void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
+{
+    (void) aInstance;
+    (void) aFrame;
+    (void) aError;
+}
+
+void otPlatDiagAlarmCallback(otInstance *aInstance)
+{
+    (void) aInstance;
+}
diff --git a/examples/platforms/cc2650/flash.c b/examples/platforms/cc2650/flash.c
new file mode 100644
index 0000000..54aa123
--- /dev/null
+++ b/examples/platforms/cc2650/flash.c
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "platform-cc2650.h"
+
+/**
+ * @warning this file only implements stubs for the function calls. There is
+ * not enough space on the cc2650 to support NV as an SoC.
+ */
+
+otError utilsFlashInit(void)
+{
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+uint32_t utilsFlashGetSize(void)
+{
+    return 0;
+}
+
+otError utilsFlashErasePage(uint32_t aAddress)
+{
+    (void)aAddress;
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError utilsFlashStatusWait(uint32_t aTimeout)
+{
+    (void)aTimeout;
+    return OT_ERROR_NONE;
+}
+
+uint32_t utilsFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+{
+    (void)aAddress;
+    (void)aData;
+    (void)aSize;
+    return 0;
+}
+
+uint32_t utilsFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+{
+    (void)aAddress;
+    (void)aData;
+    (void)aSize;
+    return 0;
+}
diff --git a/examples/platforms/cc2650/misc.c b/examples/platforms/cc2650/misc.c
new file mode 100644
index 0000000..cf204f3
--- /dev/null
+++ b/examples/platforms/cc2650/misc.c
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/types.h>
+#include <openthread/platform/misc.h>
+#include <driverlib/sys_ctrl.h>
+
+/**
+ * Function documented in platform/misc.h
+ */
+void otPlatReset(otInstance *aInstance)
+{
+    (void)aInstance;
+    SysCtrlSystemReset();
+}
+
+/**
+ * Function documented in platform/misc.h
+ */
+otPlatResetReason otPlatGetResetReason(otInstance *aInstance)
+{
+    (void)aInstance;
+
+    switch (SysCtrlResetSourceGet())
+    {
+    case RSTSRC_PWR_ON:
+        return OT_PLAT_RESET_REASON_POWER_ON;
+
+    case RSTSRC_PIN_RESET:
+        return OT_PLAT_RESET_REASON_EXTERNAL;
+
+    case RSTSRC_VDDS_LOSS:
+    case RSTSRC_VDD_LOSS:
+    case RSTSRC_VDDR_LOSS:
+    case RSTSRC_CLK_LOSS:
+        return OT_PLAT_RESET_REASON_CRASH;
+
+    case RSTSRC_WARMRESET:
+    case RSTSRC_SYSRESET:
+    case RSTSRC_WAKEUP_FROM_SHUTDOWN:
+        return OT_PLAT_RESET_REASON_SOFTWARE;
+
+    default:
+        return OT_PLAT_RESET_REASON_UNKNOWN;
+    }
+}
+
+void otPlatWakeHost(void)
+{
+    // TODO: implement an operation to wake the host from sleep state.
+}
diff --git a/examples/platforms/cc2650/openthread-core-cc2650-config.h b/examples/platforms/cc2650/openthread-core-cc2650-config.h
new file mode 100644
index 0000000..c03f7b9
--- /dev/null
+++ b/examples/platforms/cc2650/openthread-core-cc2650-config.h
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPENTHREAD_CORE_CC2650_CONFIG_H_
+#define OPENTHREAD_CORE_CC2650_CONFIG_H_
+
+/**
+ * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
+ *
+ * The number of message buffers in buffer pool
+ */
+#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 32
+
+/**
+  * @def OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
+  *
+  * Define to 1 if you want use legacy transmit done.
+  *
+  */
+#define OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE 1
+
+#endif /* OPENTHREAD_CORE_CC2650_CONFIG_H_ */
+
diff --git a/examples/platforms/cc2650/platform-cc2650.h b/examples/platforms/cc2650/platform-cc2650.h
new file mode 100644
index 0000000..68266ed
--- /dev/null
+++ b/examples/platforms/cc2650/platform-cc2650.h
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+#include <stdint.h>
+#include "openthread/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Global OpenThread instance structure
+extern otInstance *sInstance;
+
+/**
+ * This method initializes the alarm service used by OpenThread.
+ *
+ */
+void cc2650AlarmInit(void);
+
+/**
+ * This method performs alarm driver processing.
+ *
+ */
+void cc2650AlarmProcess(otInstance *aInstance);
+
+/**
+ * This method initializes the radio service used by OpenThread.
+ *
+ */
+void cc2650RadioInit(void);
+
+/**
+ * This method performs radio driver processing.
+ *
+ */
+void cc2650RadioProcess(otInstance *aInstance);
+
+/**
+ * This method initializes the random number service used by OpenThread.
+ *
+ */
+void cc2650RandomInit(void);
+
+/**
+ * This method performs radio driver processing.
+ *
+ */
+void cc2650UartProcess(void);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // PLATFORM_H_
diff --git a/examples/platforms/cc2650/platform.c b/examples/platforms/cc2650/platform.c
new file mode 100644
index 0000000..b98bc31
--- /dev/null
+++ b/examples/platforms/cc2650/platform.c
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <openthread/types.h>
+#include "platform-cc2650.h"
+
+extern const char __ccfg[];
+
+const char *dummy_ccfg_ref = ((const char *)(&(__ccfg[0])));
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void PlatformInit(int argc, char *argv[])
+{
+    (void) argc;
+    (void) argv;
+
+    while (dummy_ccfg_ref == NULL)
+    {
+        /*
+          * This provides a code reference to the customer configuration
+          * area of the flash, otherwise the data is skipped by the
+          * linker and not put into the final flash image.
+          */
+    }
+
+    cc2650AlarmInit();
+    cc2650RandomInit();
+    cc2650RadioInit();
+}
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void PlatformProcessDrivers(otInstance *aInstance)
+{
+    // should sleep and wait for interrupts here
+
+    cc2650UartProcess();
+    cc2650RadioProcess(aInstance);
+    cc2650AlarmProcess(aInstance);
+}
diff --git a/examples/platforms/cc2650/radio.c b/examples/platforms/cc2650/radio.c
new file mode 100644
index 0000000..7ac31b6
--- /dev/null
+++ b/examples/platforms/cc2650/radio.c
@@ -0,0 +1,1865 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/types.h>
+
+#include <assert.h>
+#include <utils/code_utils.h>
+#include "cc2650_radio.h"
+#include <openthread/platform/radio.h>
+#include <openthread/platform/random.h> /* to seed the CSMA-CA funciton */
+
+#include <driverlib/prcm.h>
+#include <inc/hw_prcm.h>
+#include <inc/hw_memmap.h>
+#include <inc/hw_fcfg1.h>
+#include <inc/hw_ccfg.h>
+#include <driverlib/rfc.h>
+#include <driverlib/osc.h>
+#include <driverlib/rf_data_entry.h>
+#include <driverlib/rf_mailbox.h>
+#include <driverlib/rf_common_cmd.h>
+#include <driverlib/rf_ieee_mailbox.h>
+#include <driverlib/rf_ieee_cmd.h>
+#include <driverlib/chipinfo.h>
+
+enum
+{
+    CC2650_RECEIVE_SENSITIVITY = -100,  // dBm
+};
+
+/* phy state as defined by openthread */
+static volatile cc2650_PhyState sState;
+
+static output_config_t const *sCurrentOutputPower = &(rgOutputPower[OUTPUT_CONFIG_COUNT - 1]);
+
+/* Overrides for IEEE 802.15.4, differential mode */
+static uint32_t sIEEEOverrides[] =
+{
+    0x00354038, /* Synth: Set RTRIM (POTAILRESTRIM) to 5 */
+    0x4001402D, /* Synth: Correct CKVD latency setting (address) */
+    0x00608402, /* Synth: Correct CKVD latency setting (value) */
+    0x000784A3, /* Synth: Set FREF = 3.43 MHz (24 MHz / 7) */
+    0xA47E0583, /* Synth: Set loop bandwidth after lock to 80 kHz (K2) */
+    0xEAE00603, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, LSB) */
+    0x00010623, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, MSB) */
+    0x002B50DC, /* Adjust AGC DC filter */
+    0x05000243, /* Increase synth programming timeout */
+    0x002082C3, /* Increase synth programming timeout */
+    0xFFFFFFFF, /* End of override list */
+};
+
+/*
+ * status of the pending bit of the last ack packet found by the
+ * sTransmitRxAckCmd radio command
+ *
+ * used to pass data from the radio state ISR to the processing loop
+ */
+static volatile bool sReceivedAckPendingBit = false;
+
+/*
+ * number of retry counts left to the currently transmitting frame
+ *
+ * initialized when a frame is passed to be sent over the air, and decremented
+ * by the radio ISR every time the transmit command string fails to receive a
+ * corresponding ack
+ */
+static volatile unsigned int sTransmitRetryCount = 0;
+
+/*
+ * offset of the radio timer from the rtc
+ *
+ * used when we start and stop the RAT
+ */
+static uint32_t sRatOffset = 0;
+
+/*
+ * radio command structures that run on the CM0
+ */
+static volatile rfc_CMD_SYNC_START_RAT_t     sStartRatCmd;
+static volatile rfc_CMD_RADIO_SETUP_t        sRadioSetupCmd;
+
+static volatile rfc_CMD_FS_POWERDOWN_t       sFsPowerdownCmd;
+static volatile rfc_CMD_SYNC_STOP_RAT_t      sStopRatCmd;
+
+static volatile rfc_CMD_CLEAR_RX_t           sClearReceiveQueueCmd;
+static volatile rfc_CMD_IEEE_MOD_FILT_t      sModifyReceiveFilterCmd;
+static volatile rfc_CMD_IEEE_MOD_SRC_MATCH_t sModifyReceiveSrcMatchCmd;
+
+static volatile rfc_CMD_IEEE_ED_SCAN_t       sEdScanCmd;
+
+static volatile rfc_CMD_IEEE_RX_t            sReceiveCmd;
+
+static volatile rfc_CMD_IEEE_CSMA_t          sCsmacaBackoffCmd;
+static volatile rfc_CMD_IEEE_TX_t            sTransmitCmd;
+static volatile rfc_CMD_IEEE_RX_ACK_t        sTransmitRxAckCmd;
+
+static volatile ext_src_match_data_t         sSrcMatchExtData;
+static volatile short_src_match_data_t       sSrcMatchShortData;
+
+/* struct containing radio stats */
+static rfc_ieeeRxOutput_t sRfStats;
+
+#define RX_BUF_SIZE 144
+/* two receive buffers entries with room for 1 max IEEE802.15.4 frame in each */
+static uint8_t sRxBuf0[RX_BUF_SIZE] __attribute__((aligned(4)));
+static uint8_t sRxBuf1[RX_BUF_SIZE] __attribute__((aligned(4)));
+
+/* The RX Data Queue */
+static dataQueue_t sRxDataQueue = { 0 };
+
+/* openthread data primatives */
+static otRadioFrame sTransmitFrame;
+static otRadioFrame sReceiveFrame;
+static otError sTransmitError;
+static otError sReceiveError;
+
+static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE] __attribute__((aligned(4))) ;
+static uint8_t sReceivePsdu[OT_RADIO_FRAME_MAX_SIZE] __attribute__((aligned(4))) ;
+
+/**
+ * Interrupt handlers forward declared for register function
+ */
+void RFCCPE0IntHandler(void);
+void RFCCPE1IntHandler(void);
+
+/**
+ * @brief initialize the RX/TX buffers
+ *
+ * Zeros out the receive and transmit buffers and sets up the data structures
+ * of the receive queue.
+ */
+static void rfCoreInitBufs(void)
+{
+    rfc_dataEntry_t *entry;
+    memset(sRxBuf0, 0x00, RX_BUF_SIZE);
+    memset(sRxBuf1, 0x00, RX_BUF_SIZE);
+
+    entry = (rfc_dataEntry_t *)sRxBuf0;
+    entry->pNextEntry = sRxBuf1;
+    entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE;
+    entry->length = sizeof(sRxBuf0) - sizeof(rfc_dataEntry_t);
+
+    entry = (rfc_dataEntry_t *)sRxBuf1;
+    entry->pNextEntry = sRxBuf0;
+    entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE;
+    entry->length = sizeof(sRxBuf0) - sizeof(rfc_dataEntry_t);
+
+    sTransmitFrame.mPsdu = sTransmitPsdu;
+    sTransmitFrame.mLength = 0;
+    sReceiveFrame.mPsdu = sReceivePsdu;
+    sReceiveFrame.mLength = 0;
+}
+
+/**
+ * @brief initialize the RX command structure
+ *
+ * Sets the default values for the receive command structure.
+ */
+static void rfCoreInitReceiveParams(void)
+{
+    static const rfc_CMD_IEEE_RX_t cReceiveCmd =
+    {
+        .commandNo                  = CMD_IEEE_RX,
+        .status                     = IDLE,
+        .pNextOp                    = NULL,
+        .startTime                  = 0u,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+        .channel                    = OT_RADIO_CHANNEL_MIN,
+        .rxConfig                   =
+        {
+            .bAutoFlushCrc          = 1,
+            .bAutoFlushIgn          = 0,
+            .bIncludePhyHdr         = 0,
+            .bIncludeCrc            = 0,
+            .bAppendRssi            = 1,
+            .bAppendCorrCrc         = 1,
+            .bAppendSrcInd          = 0,
+            .bAppendTimestamp       = 0,
+        },
+        .frameFiltOpt               =
+        {
+            .frameFiltEn            = 1,
+            .frameFiltStop          = 1,
+            .autoAckEn              = 1,
+            .slottedAckEn           = 0,
+            .autoPendEn             = 0,
+            .defaultPend            = 0,
+            .bPendDataReqOnly       = 0,
+            .bPanCoord              = 0,
+            .maxFrameVersion        = 3,
+            .bStrictLenFilter       = 1,
+        },
+        .frameTypes                 =
+        {
+            .bAcceptFt0Beacon       = 1,
+            .bAcceptFt1Data         = 1,
+            .bAcceptFt2Ack          = 0,
+            .bAcceptFt3MacCmd       = 1,
+            .bAcceptFt4Reserved     = 1,
+            .bAcceptFt5Reserved     = 1,
+            .bAcceptFt6Reserved     = 1,
+            .bAcceptFt7Reserved     = 1,
+        },
+        .ccaOpt                     =
+        {
+            .ccaEnEnergy            = 1,
+            .ccaEnCorr              = 1,
+            .ccaEnSync              = 1,
+            .ccaCorrOp              = 1,
+            .ccaSyncOp              = 0,
+            .ccaCorrThr             = 3,
+        },
+        .ccaRssiThr                 = -90,
+        .endTrigger                 =
+        {
+            .triggerType            = TRIG_NEVER,
+        },
+        .endTime                    = 0u,
+    };
+    sReceiveCmd = cReceiveCmd;
+
+    sReceiveCmd.pRxQ    = &sRxDataQueue;
+    sReceiveCmd.pOutput = &sRfStats;
+
+    sReceiveCmd.numShortEntries = CC2650_SHORTADD_SRC_MATCH_NUM;
+    sReceiveCmd.pShortEntryList = (void *)&sSrcMatchShortData;
+
+    sReceiveCmd.numExtEntries = CC2650_EXTADD_SRC_MATCH_NUM;
+    sReceiveCmd.pExtEntryList = (uint32_t *)&sSrcMatchExtData;
+}
+
+/**
+ * @brief sends the direct abort command to the radio core
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreExecuteAbortCmd(void)
+{
+    return (RFCDoorbellSendTo(CMDR_DIR_CMD(CMD_ABORT)) & 0xFF);
+}
+
+/**
+ * @brief sends the direct ping command to the radio core
+ *
+ * Check that the Radio core is alive and able to respond to commands.
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreExecutePingCmd(void)
+{
+    return (RFCDoorbellSendTo(CMDR_DIR_CMD(CMD_PING)) & 0xFF);
+}
+
+/**
+ * @brief sends the immediate clear rx queue command to the radio core
+ *
+ * Uses the radio core to mark all of the entries in the receive queue as
+ * pending. This is used instead of clearing the entries manually to avoid race
+ * conditions between the main processor and the radio core.
+ *
+ * @param [in] aQueue a pointer to the receive queue to be cleared
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreClearReceiveQueue(dataQueue_t *aQueue)
+{
+    /* memset skipped because sClearReceiveQueueCmd has only 2 members and padding */
+    sClearReceiveQueueCmd.commandNo = CMD_CLEAR_RX;
+    sClearReceiveQueueCmd.pQueue = aQueue;
+
+    return (RFCDoorbellSendTo((uint32_t)&sClearReceiveQueueCmd) & 0xFF);
+}
+
+/**
+ * @brief enable/disable filtering
+ *
+ * Uses the radio core to alter the current running RX command filtering
+ * options. This ensures there is no access fault between the CM3 and CM0 for
+ * the RX command.
+ *
+ * This function leaves the type of frames to be filtered the same as the
+ * receive command.
+ *
+ * @note An IEEE RX command *must* be running while this command executes.
+ *
+ * @param [in] aEnable TRUE: enable frame filtering, FALSE: disable frame filtering
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreModifyRxFrameFilter(bool aEnable)
+{
+    /* memset skipped because sModifyReceiveFilterCmd has only 3 members */
+    sModifyReceiveFilterCmd.commandNo = CMD_IEEE_MOD_FILT;
+    /* copy current frame filtering and frame types from running RX command */
+    memcpy((void *)&sModifyReceiveFilterCmd.newFrameFiltOpt, (void *)&sReceiveCmd.frameFiltOpt,
+           sizeof(sModifyReceiveFilterCmd.newFrameFiltOpt));
+    memcpy((void *)&sModifyReceiveFilterCmd.newFrameTypes, (void *)&sReceiveCmd.frameTypes,
+           sizeof(sModifyReceiveFilterCmd.newFrameTypes));
+
+    sModifyReceiveFilterCmd.newFrameFiltOpt.frameFiltEn = aEnable ? 1 : 0;
+
+    return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveFilterCmd) & 0xFF);
+}
+
+/**
+ * @brief enable/disable autoPend
+ *
+ * Uses the radio core to alter the current running RX command filtering
+ * options. This ensures there is no access fault between the CM3 and CM0 for
+ * the RX command.
+ *
+ * This function leaves the type of frames to be filtered the same as the
+ * receive command.
+ *
+ * @note An IEEE RX command *must* be running while this command executes.
+ *
+ * @param [in] aEnable TRUE: enable autoPend, FALSE: disable autoPend
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreModifyRxAutoPend(bool aEnable)
+{
+    /* memset skipped because sModifyReceiveFilterCmd has only 3 members */
+    sModifyReceiveFilterCmd.commandNo = CMD_IEEE_MOD_FILT;
+    /* copy current frame filtering and frame types from running RX command */
+    memcpy((void *)&sModifyReceiveFilterCmd.newFrameFiltOpt, (void *)&sReceiveCmd.frameFiltOpt,
+           sizeof(sModifyReceiveFilterCmd.newFrameFiltOpt));
+    memcpy((void *)&sModifyReceiveFilterCmd.newFrameTypes, (void *)&sReceiveCmd.frameTypes,
+           sizeof(sModifyReceiveFilterCmd.newFrameTypes));
+
+    sModifyReceiveFilterCmd.newFrameFiltOpt.autoPendEn = aEnable ? 1 : 0;
+
+    return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveFilterCmd) & 0xFF);
+}
+
+/**
+ * @brief sends the immediate modify source matching command to the radio core
+ *
+ * Uses the radio core to alter the current source matching parameters used by
+ * the running RX command. This ensures there is no access fault between the
+ * CM3 and CM0, and ensures that the RX command has cohesive view of the data.
+ * The CM3 may make alterations to the source matching entries if the entry is
+ * marked as disabled.
+ *
+ * @note An IEEE RX command *must* be running while this command executes.
+ *
+ * @param [in] aEntryNo the index of the entry to alter
+ * @param [in] aType TRUE: the entry is a short address, FALSE: the entry is an extended address
+ * @param [in] aEnable whether the given entry is to be enabled or disabled
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreModifySourceMatchEntry(uint8_t aEntryNo, cc2650_address_t aType, bool aEnable)
+{
+    /* memset kept to save 60 bytes of text space, gcc can't optimize the
+     * following bitfield operation if it doesn't know the fields are zero
+     * already.
+     */
+    memset((void *)&sModifyReceiveSrcMatchCmd, 0, sizeof(sModifyReceiveSrcMatchCmd));
+
+    sModifyReceiveSrcMatchCmd.commandNo = CMD_IEEE_MOD_SRC_MATCH;
+
+    /* we only use source matching for pending data bit, so enabling and
+     * pending are the same to us.
+     */
+    if (aEnable)
+    {
+        sModifyReceiveSrcMatchCmd.options.bEnable = 1;
+        sModifyReceiveSrcMatchCmd.options.srcPend = 1;
+    }
+    else
+    {
+        sModifyReceiveSrcMatchCmd.options.bEnable = 0;
+        sModifyReceiveSrcMatchCmd.options.srcPend = 0;
+    }
+
+    sModifyReceiveSrcMatchCmd.options.entryType = aType;
+    sModifyReceiveSrcMatchCmd.entryNo = aEntryNo;
+
+    return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveSrcMatchCmd) & 0xFF);
+}
+
+/**
+ * @brief walks the short address source match list to find an address
+ *
+ * @param [in] address the short address to search for
+ *
+ * @return the index where the address was found
+ * @retval CC2650_SRC_MATCH_NONE the address was not found
+ */
+static uint8_t rfCoreFindShortSrcMatchIdx(const uint16_t aAddress)
+{
+    uint8_t i;
+
+    for (i = 0; i < CC2650_SHORTADD_SRC_MATCH_NUM; i++)
+    {
+        if (sSrcMatchShortData.extAddrEnt[i].shortAddr == aAddress)
+        {
+            return i;
+        }
+    }
+
+    return CC2650_SRC_MATCH_NONE;
+}
+
+/**
+ * @brief walks the short address source match list to find an empty slot
+ *
+ * @return the index of an unused address slot
+ * @retval CC2650_SRC_MATCH_NONE no unused slots available
+ */
+static uint8_t rfCoreFindEmptyShortSrcMatchIdx(void)
+{
+    uint8_t i;
+
+    for (i = 0; i < CC2650_SHORTADD_SRC_MATCH_NUM; i++)
+    {
+        if ((sSrcMatchShortData.srcMatchEn[i / 32] & (1 << (i % 32))) == 0u)
+        {
+            return i;
+        }
+    }
+
+    return CC2650_SRC_MATCH_NONE;
+}
+
+/**
+ * @brief walks the ext address source match list to find an address
+ *
+ * @param [in] address the ext address to search for
+ *
+ * @return the index where the address was found
+ * @retval CC2650_SRC_MATCH_NONE the address was not found
+ */
+static uint8_t rfCoreFindExtSrcMatchIdx(const uint64_t *aAddress)
+{
+    uint8_t i;
+
+    for (i = 0; i < CC2650_EXTADD_SRC_MATCH_NUM; i++)
+    {
+        if (sSrcMatchExtData.extAddrEnt[i] == *aAddress)
+        {
+            return i;
+        }
+    }
+
+    return CC2650_SRC_MATCH_NONE;
+}
+
+/**
+ * @brief walks the ext address source match list to find an empty slot
+ *
+ * @return the index of an unused address slot
+ * @retval CC2650_SRC_MATCH_NONE no unused slots available
+ */
+static uint8_t rfCoreFindEmptyExtSrcMatchIdx(void)
+{
+    uint8_t i;
+
+    for (i = 0; i < CC2650_EXTADD_SRC_MATCH_NUM; i++)
+    {
+        if ((sSrcMatchExtData.srcMatchEn[i / 32] & (1 << (i % 32))) != 0u)
+        {
+            return i;
+        }
+    }
+
+    return CC2650_SRC_MATCH_NONE;
+}
+
+/**
+ * @brief sends the tx command to the radio core
+ *
+ * Sends the packet to the radio core to be sent asynchronously.
+ *
+ * @param [in] aPsdu a pointer to the data to be sent
+ * @note this *must* be 4 byte aligned and not include the FCS, that is
+ * calculated in hardware.
+ * @param [in] aLen the length in bytes of data pointed to by psdu.
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreSendTransmitCmd(uint8_t *aPsdu, uint8_t aLen)
+{
+    static const rfc_CMD_IEEE_CSMA_t cCsmacaBackoffCmd =
+    {
+        .commandNo                  = CMD_IEEE_CSMA,
+        .status                     = IDLE,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_ALWAYS,
+        },
+        .macMaxBE                   = IEEE802154_MAC_MAX_BE,
+        .macMaxCSMABackoffs         = IEEE802154_MAC_MAX_CSMA_BACKOFFS,
+        .csmaConfig                 =
+        {
+            .initCW                 = 1,
+            .bSlotted               = 0,
+            .rxOffMode              = 0,
+        },
+        .NB                         = 0,
+        .BE                         = IEEE802154_MAC_MIN_BE,
+        .remainingPeriods           = 0,
+        .endTrigger                 =
+        {
+            .triggerType            = TRIG_NEVER,
+        },
+        .endTime                    = 0x00000000,
+    };
+    static const rfc_CMD_IEEE_TX_t cTransmitCmd =
+    {
+        .commandNo                  = CMD_IEEE_TX,
+        .status                     = IDLE,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+        .pNextOp                    = NULL,
+    };
+    static const rfc_CMD_IEEE_RX_ACK_t cTransmitRxAckCmd =
+    {
+        .commandNo                  = CMD_IEEE_RX_ACK,
+        .status                     = IDLE,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .endTrigger                 =
+        {
+            .triggerType            = TRIG_REL_START,
+            .pastTrig               = 1,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+        .pNextOp                    = NULL,
+        /* number of RAT ticks to wait before claiming we haven't received an ack */
+        .endTime                    = ((IEEE802154_MAC_ACK_WAIT_DURATION * CC2650_RAT_TICKS_PER_SEC) / IEEE802154_SYMBOLS_PER_SEC),
+    };
+
+    /* reset retry count */
+    sTransmitRetryCount = 0;
+
+    sCsmacaBackoffCmd               = cCsmacaBackoffCmd;
+    /* initialize the random state with a true random seed for the radio core's
+     * psudo rng */
+    sCsmacaBackoffCmd.randomState   = otPlatRandomGet();
+    sCsmacaBackoffCmd.pNextOp       = (rfc_radioOp_t *) &sTransmitCmd;
+
+    sTransmitCmd = cTransmitCmd;
+    /* no need to look for an ack if the tx operation was stopped */
+    sTransmitCmd.payloadLen = aLen;
+    sTransmitCmd.pPayload = aPsdu;
+
+    if (aPsdu[0] & IEEE802154_ACK_REQUEST)
+    {
+        /* setup the receive ack command to follow the tx command */
+        sTransmitCmd.condition.rule = COND_STOP_ON_FALSE;
+        sTransmitCmd.pNextOp = (rfc_radioOp_t *) &sTransmitRxAckCmd;
+
+        sTransmitRxAckCmd = cTransmitRxAckCmd;
+        sTransmitRxAckCmd.seqNo = aPsdu[IEEE802154_DSN_OFFSET];
+    }
+
+    return (RFCDoorbellSendTo((uint32_t)&sCsmacaBackoffCmd) & 0xFF);
+}
+
+/**
+ * @brief sends the rx command to the radio core
+ *
+ * Sends the pre-built receive command to the radio core. This sets up the
+ * radio to receive packets according to the settings in the global rx command.
+ *
+ * @note This function does not alter any of the parameters of the rx command.
+ * It is only concerned with sending the command to the radio core. See @ref
+ * otPlatRadioSetPanId for an example of how the rx settings are set changed.
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command completed correctly
+ */
+static uint_fast8_t rfCoreSendReceiveCmd(void)
+{
+    sReceiveCmd.status = IDLE;
+    return (RFCDoorbellSendTo((uint32_t)&sReceiveCmd) & 0xFF);
+}
+
+static uint_fast8_t rfCoreSendEdScanCmd(uint8_t aChannel, uint16_t aDurration)
+{
+    static const rfc_CMD_IEEE_ED_SCAN_t cEdScanCmd =
+    {
+        .commandNo                  = CMD_IEEE_ED_SCAN,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+        .ccaOpt                     =
+        {
+            .ccaEnEnergy            = 1,
+            .ccaEnCorr              = 1,
+            .ccaEnSync              = 1,
+            .ccaCorrOp              = 1,
+            .ccaSyncOp              = 0,
+            .ccaCorrThr             = 3,
+        },
+        .ccaRssiThr                 = -90,
+        .endTrigger                 =
+        {
+            .triggerType            = TRIG_REL_START,
+            .pastTrig               = 1,
+        },
+    };
+    sEdScanCmd = cEdScanCmd;
+
+    sEdScanCmd.channel = aChannel;
+
+    /* durration is in ms */
+    sEdScanCmd.endTime = aDurration * (CC2650_RAT_TICKS_PER_SEC / 1000);
+
+    return (RFCDoorbellSendTo((uint32_t)&sEdScanCmd) & 0xFF);
+}
+
+/**
+ * @brief enables the cpe0 and cpe1 radio interrupts
+ *
+ * Enables the @ref IRQ_LAST_COMMAND_DONE and @ref IRQ_LAST_FG_COMMAND_DONE to
+ * be handled by the @ref RFCCPE0IntHandler interrupt handler.
+ */
+static void rfCoreSetupInt(void)
+{
+    bool interruptsWereDisabled;
+
+    /* We are already turned on by the caller, so this should not happen */
+    if (!PRCMRfReady())
+    {
+        return;
+    }
+
+    interruptsWereDisabled = IntMasterDisable();
+
+    /* Set all interrupt channels to CPE0 channel, error to CPE1 */
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = IRQ_INTERNAL_ERROR;
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = IRQ_LAST_COMMAND_DONE | IRQ_LAST_FG_COMMAND_DONE;
+
+    IntRegister(INT_RFC_CPE_0, RFCCPE0IntHandler);
+    IntRegister(INT_RFC_CPE_1, RFCCPE1IntHandler);
+    IntPendClear(INT_RFC_CPE_0);
+    IntPendClear(INT_RFC_CPE_1);
+    IntEnable(INT_RFC_CPE_0);
+    IntEnable(INT_RFC_CPE_1);
+
+    if (!interruptsWereDisabled)
+    {
+        IntMasterEnable();
+    }
+}
+
+/**
+ * @brief disables and clears the cpe0 and cpe1 radio interrupts
+ */
+static void rfCoreStopInt(void)
+{
+    bool interruptsWereDisabled;
+
+    interruptsWereDisabled = IntMasterDisable();
+
+    /* clear and disable interrupts */
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0;
+
+    IntUnregister(INT_RFC_CPE_0);
+    IntUnregister(INT_RFC_CPE_1);
+    IntPendClear(INT_RFC_CPE_0);
+    IntPendClear(INT_RFC_CPE_1);
+    IntDisable(INT_RFC_CPE_0);
+    IntDisable(INT_RFC_CPE_1);
+
+    if (!interruptsWereDisabled)
+    {
+        IntMasterEnable();
+    }
+}
+
+/**
+ * @brief Sets the mode for the radio core to IEEE 802.15.4
+ */
+static void rfCoreSetModeSelect(void)
+{
+    switch (ChipInfo_GetChipType())
+    {
+    case CHIP_TYPE_CC2650:
+        HWREG(PRCM_BASE + PRCM_O_RFCMODESEL) = PRCM_RFCMODESEL_CURR_MODE5;
+        break;
+
+    case CHIP_TYPE_CC2630:
+        HWREG(PRCM_BASE + PRCM_O_RFCMODESEL) = PRCM_RFCMODESEL_CURR_MODE2;
+        break;
+
+    default:
+        /* This code must be run on a valid cc26xx chip */
+        assert(false);
+        break;
+    }
+}
+
+/**
+ * @brief turns on the radio core
+ *
+ * Sets up the power and resources for the radio core.
+ * - switches the high frequency clock to the xosc crystal
+ * - sets the mode for the radio core to IEEE 802.15.4
+ * - initializes the rx buffers and command
+ * - powers on the radio core power domain
+ * - enables the radio core power domain
+ * - sets up the interrupts
+ * - sends the ping command to the radio core to make sure it is running
+ *
+ * @return the value from the ping command to the radio core
+ * @retval CMDSTA_Done the radio core is alive and responding
+ */
+static uint_fast8_t rfCorePowerOn(void)
+{
+    bool interruptsWereDisabled;
+
+    /* Request the HF XOSC as the source for the HF clock. Needed before we can
+     * use the FS. This will only request, it will _not_ perform the switch.
+     */
+    if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF)
+    {
+        /* Request to switch to the crystal to enable radio operation. It takes a
+         * while for the XTAL to be ready so instead of performing the actual
+         * switch, we do other stuff while the XOSC is getting ready.
+         */
+        OSCClockSourceSet(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
+    }
+
+    rfCoreSetModeSelect();
+
+    /* Set of RF Core data queue. Circular buffer, no last entry */
+    sRxDataQueue.pCurrEntry = sRxBuf0;
+    sRxDataQueue.pLastEntry = NULL;
+
+    rfCoreInitBufs();
+
+    /*
+     * Trigger a switch to the XOSC, so that we can subsequently use the RF FS
+     * This will block until the XOSC is actually ready, but give how we
+     * requested it early on, this won't be too long a wait.
+     * This should be done before starting the RAT.
+     */
+    if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF)
+    {
+        /* Switch the HF clock source (cc26xxware executes this from ROM) */
+        OSCHfSourceSwitch();
+    }
+
+    interruptsWereDisabled = IntMasterDisable();
+
+    /* Enable RF Core power domain */
+    PRCMPowerDomainOn(PRCM_DOMAIN_RFCORE);
+
+    while (PRCMPowerDomainStatus(PRCM_DOMAIN_RFCORE) != PRCM_DOMAIN_POWER_ON);
+
+    PRCMDomainEnable(PRCM_DOMAIN_RFCORE);
+    PRCMLoadSet();
+
+    while (!PRCMLoadGet());
+
+    rfCoreSetupInt();
+
+    if (!interruptsWereDisabled)
+    {
+        IntMasterEnable();
+    }
+
+    /* Let CPE boot */
+    HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = (RFC_PWR_PWMCLKEN_RFC_M | RFC_PWR_PWMCLKEN_CPE_M |
+                                                       RFC_PWR_PWMCLKEN_CPERAM_M);
+
+    /* Send ping (to verify RFCore is ready and alive) */
+    return rfCoreExecutePingCmd();
+}
+
+/**
+ * @brief turns off the radio core
+ *
+ * Switches off the power and resources for the radio core.
+ * - disables the interrupts
+ * - disables the radio core power domain
+ * - powers off the radio core power domain
+ * - switches the high frequency clock to the rcosc to save power
+ */
+static void rfCorePowerOff(void)
+{
+    rfCoreStopInt();
+
+    PRCMDomainDisable(PRCM_DOMAIN_RFCORE);
+    PRCMLoadSet();
+
+    while (!PRCMLoadGet());
+
+    PRCMPowerDomainOff(PRCM_DOMAIN_RFCORE);
+
+    while (PRCMPowerDomainStatus(PRCM_DOMAIN_RFCORE) != PRCM_DOMAIN_POWER_OFF);
+
+    if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_RCOSC_HF)
+    {
+        /* Request to switch to the RC osc for low power mode. */
+        OSCClockSourceSet(OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_RCOSC_HF);
+        /* Switch the HF clock source (cc26xxware executes this from ROM) */
+        OSCHfSourceSwitch();
+    }
+}
+
+/**
+ * @brief sends the setup command string to the radio core
+ *
+ * Enables the clock line from the RTC to the RF core RAT. Enables the RAT
+ * timer and sets up the radio in IEEE mode.
+ *
+ * @return the value from the command status register
+ * @retval CMDSTA_Done the command was received
+ */
+static uint_fast16_t rfCoreSendEnableCmd(void)
+{
+    uint8_t ret;
+    bool interruptsWereDisabled;
+    static const rfc_CMD_SYNC_START_RAT_t cStartRatCmd =
+    {
+        .commandNo                  = CMD_SYNC_START_RAT,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_STOP_ON_FALSE,
+        },
+    };
+    static const rfc_CMD_RADIO_SETUP_t cRadioSetupCmd =
+    {
+        .commandNo                  = CMD_RADIO_SETUP,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+        .mode                       = 1, // IEEE 802.15.4 mode
+    };
+    /* turn on the clock line to the radio core */
+    HWREGBITW(AON_RTC_BASE + AON_RTC_O_CTL, AON_RTC_CTL_RTC_UPD_EN_BITN) = 1;
+
+    /* initialize the rat start command */
+    sStartRatCmd         = cStartRatCmd;
+    sStartRatCmd.pNextOp = (rfc_radioOp_t *) &sRadioSetupCmd;
+    sStartRatCmd.rat0    = sRatOffset;
+
+    /* initialize radio setup command */
+    sRadioSetupCmd              = cRadioSetupCmd;
+    /* initally set the radio tx power to the max */
+    sRadioSetupCmd.txPower      = sCurrentOutputPower->value;
+    sRadioSetupCmd.pRegOverride = sIEEEOverrides;
+
+    interruptsWereDisabled = IntMasterDisable();
+
+    if ((ret = (RFCDoorbellSendTo((uint32_t)&sStartRatCmd) & 0xFF)) != CMDSTA_Done)
+    {
+        if (!interruptsWereDisabled)
+        {
+            IntMasterEnable();
+        }
+
+        return ret;
+    }
+
+    /* synchronously wait for the CM0 to stop executing */
+    while ((HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE) == 0x00);
+
+    if (!interruptsWereDisabled)
+    {
+        IntMasterEnable();
+    }
+
+    return sRadioSetupCmd.status;
+}
+
+/**
+ * @brief sends the shutdown command string to the radio core
+ *
+ * Powers down the frequency synthesizer and stops the RAT.
+ *
+ * @note synchronously waits until the command string completes.
+ *
+ * @return the status of the RAT stop command
+ * @retval DONE_OK the command string executed properly
+ */
+static uint_fast16_t rfCoreSendDisableCmd(void)
+{
+    uint8_t doorbellRet;
+    bool interruptsWereDisabled;
+    static const rfc_CMD_FS_POWERDOWN_t cFsPowerdownCmd =
+    {
+        .commandNo                  = CMD_FS_POWERDOWN,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_ALWAYS,
+        },
+    };
+    static const rfc_CMD_SYNC_STOP_RAT_t cStopRatCmd =
+    {
+        .commandNo                  = CMD_SYNC_STOP_RAT,
+        .startTrigger               =
+        {
+            .triggerType            = TRIG_NOW,
+        },
+        .condition                  = {
+            .rule                   = COND_NEVER,
+        },
+    };
+
+    HWREGBITW(AON_RTC_BASE + AON_RTC_O_CTL, AON_RTC_CTL_RTC_UPD_EN_BITN) = 1;
+
+    /* initialize the command to power down the frequency synth */
+    sFsPowerdownCmd = cFsPowerdownCmd;
+    sFsPowerdownCmd.pNextOp = (rfc_radioOp_t *)&sStopRatCmd;
+
+    sStopRatCmd = cStopRatCmd;
+
+    interruptsWereDisabled = IntMasterDisable();
+
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_COMMAND_DONE;
+
+    doorbellRet = (RFCDoorbellSendTo((uint32_t)&sFsPowerdownCmd) & 0xFF);
+
+    if (doorbellRet != CMDSTA_Done)
+    {
+        if (!interruptsWereDisabled)
+        {
+            IntMasterEnable();
+        }
+
+        return doorbellRet;
+    }
+
+    /* synchronously wait for the CM0 to stop */
+    while ((HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE) == 0x00);
+
+    if (!interruptsWereDisabled)
+    {
+        IntMasterEnable();
+    }
+
+    if (sStopRatCmd.status == DONE_OK)
+    {
+        sRatOffset = sStopRatCmd.rat0;
+    }
+
+    return sStopRatCmd.status;
+}
+
+/**
+ * error interrupt handler
+ */
+void RFCCPE1IntHandler(void)
+{
+    /* Clear INTERNAL_ERROR interrupt flag */
+    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x7FFFFFFF;
+}
+
+/**
+ * command done handler
+ */
+void RFCCPE0IntHandler(void)
+{
+    if (HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE)
+    {
+        HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_COMMAND_DONE;
+
+        if (sState == cc2650_stateReceive &&
+            sReceiveCmd.status != ACTIVE &&
+            sReceiveCmd.status != IEEE_SUSPENDED)
+        {
+            /* the rx command was probably aborted to change the channel */
+            sState = cc2650_stateSleep;
+        }
+        else if (sState == cc2650_stateEdScan)
+        {
+            sState = cc2650_stateSleep;
+        }
+    }
+
+    if (HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_FG_COMMAND_DONE)
+    {
+        HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_FG_COMMAND_DONE;
+
+        if (sState == cc2650_stateTransmit)
+        {
+            if (sTransmitCmd.pPayload[0] & IEEE802154_ACK_REQUEST)
+            {
+                /* we are looking for an ack */
+                switch (sTransmitRxAckCmd.status)
+                {
+                case IEEE_DONE_TIMEOUT:
+                    if (sTransmitRetryCount < IEEE802154_MAC_MAX_FRAMES_RETRIES)
+                    {
+                        /* re-submit the tx command chain */
+                        sTransmitRetryCount++;
+                        RFCDoorbellSendTo((uint32_t)&sCsmacaBackoffCmd);
+                    }
+                    else
+                    {
+                        sTransmitError = OT_ERROR_NO_ACK;
+                        /* signal polling function we are done transmitting, we failed to send the packet */
+                        sState = cc2650_stateTransmitComplete;
+                    }
+
+                    break;
+
+                case IEEE_DONE_ACK:
+                    sReceivedAckPendingBit = false;
+                    sTransmitError = OT_ERROR_NONE;
+                    /* signal polling function we are done transmitting */
+                    sState = cc2650_stateTransmitComplete;
+                    break;
+
+                case IEEE_DONE_ACKPEND:
+                    sReceivedAckPendingBit = true;
+                    sTransmitError = OT_ERROR_NONE;
+                    /* signal polling function we are done transmitting */
+                    sState = cc2650_stateTransmitComplete;
+                    break;
+
+                default:
+                    sTransmitError = OT_ERROR_FAILED;
+                    /* signal polling function we are done transmitting */
+                    sState = cc2650_stateTransmitComplete;
+                    break;
+                }
+            }
+            else
+            {
+                /* The TX command was either stopped or we are not looking for
+                 * an ack */
+                switch (sTransmitCmd.status)
+                {
+                case IEEE_DONE_OK:
+                    sReceivedAckPendingBit = false;
+                    sTransmitError = OT_ERROR_NONE;
+                    break;
+
+                case IEEE_DONE_TIMEOUT:
+                    sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE;
+                    break;
+
+                case IEEE_ERROR_NO_SETUP:
+                case IEEE_ERROR_NO_FS:
+                case IEEE_ERROR_SYNTH_PROG:
+                    sTransmitError = OT_ERROR_INVALID_STATE;
+                    break;
+
+                case IEEE_ERROR_TXUNF:
+                    sTransmitError = OT_ERROR_NO_BUFS;
+                    break;
+
+                default:
+                    sTransmitError = OT_ERROR_GENERIC;
+                    break;
+                }
+
+                /* signal polling function we are done transmitting */
+                sState = cc2650_stateTransmitComplete;
+            }
+        }
+    }
+}
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void cc2650RadioInit(void)
+{
+    /* Populate the RX parameters data structure with default values */
+    rfCoreInitReceiveParams();
+
+    sState = cc2650_stateDisabled;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioEnable(otInstance *aInstance)
+{
+    otError error = OT_ERROR_BUSY;
+    (void)aInstance;
+
+    if (sState == cc2650_stateSleep)
+    {
+        error = OT_ERROR_NONE;
+    }
+    else if (sState == cc2650_stateDisabled)
+    {
+        otEXPECT_ACTION(rfCorePowerOn() == CMDSTA_Done, error = OT_ERROR_FAILED);
+        otEXPECT_ACTION(rfCoreSendEnableCmd() == DONE_OK, error = OT_ERROR_FAILED);
+        sState = cc2650_stateSleep;
+    }
+
+exit:
+
+    if (error == OT_ERROR_FAILED)
+    {
+        rfCorePowerOff();
+        sState = cc2650_stateDisabled;
+    }
+
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+bool otPlatRadioIsEnabled(otInstance *aInstance)
+{
+    (void)aInstance;
+    return (sState != cc2650_stateDisabled);
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioDisable(otInstance *aInstance)
+{
+    otError error = OT_ERROR_BUSY;
+    (void)aInstance;
+
+    if (sState == cc2650_stateDisabled)
+    {
+        error = OT_ERROR_NONE;
+    }
+    else if (sState == cc2650_stateSleep)
+    {
+        rfCoreSendDisableCmd();
+        /* we don't want to fail if this command string doesn't work, just turn
+         * off the whole core
+         */
+        rfCorePowerOff();
+        sState = cc2650_stateDisabled;
+        error = OT_ERROR_NONE;
+    }
+
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
+{
+    otError error = OT_ERROR_BUSY;
+    (void)aInstance;
+
+    if (sState == cc2650_stateSleep)
+    {
+        sState = cc2650_stateEdScan;
+        otEXPECT_ACTION(rfCoreSendEdScanCmd(aScanChannel, aScanDuration) == CMDSTA_Done, error = OT_ERROR_FAILED);
+        error = OT_ERROR_NONE;
+    }
+
+exit:
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower)
+{
+    unsigned int i;
+    output_config_t const *powerCfg = &(rgOutputPower[0]);
+    (void)aInstance;
+
+    for (i = 1; i < OUTPUT_CONFIG_COUNT; i++)
+    {
+        if (rgOutputPower[i].dbm >= aPower)
+        {
+            powerCfg = &(rgOutputPower[i]);
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    sCurrentOutputPower = powerCfg;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
+{
+    otError error = OT_ERROR_BUSY;
+    (void)aInstance;
+
+    if (sState == cc2650_stateSleep)
+    {
+        sState = cc2650_stateReceive;
+
+        /* initialize the receive command
+         * XXX: no memset here because we assume init has been called and we
+         *      may have changed some values in the rx command
+         */
+        sReceiveCmd.channel = aChannel;
+        otEXPECT_ACTION(rfCoreSendReceiveCmd() == CMDSTA_Done, error = OT_ERROR_FAILED);
+        error = OT_ERROR_NONE;
+    }
+    else if (sState == cc2650_stateReceive)
+    {
+        if (sReceiveCmd.status == ACTIVE && sReceiveCmd.channel == aChannel)
+        {
+            /* we are already running on the right channel */
+            sState = cc2650_stateReceive;
+            error = OT_ERROR_NONE;
+        }
+        else
+        {
+            /* we have either not fallen back into our receive command or
+             * we are running on the wrong channel. Either way assume the
+             * caller correctly called us and abort all running commands.
+             */
+            otEXPECT_ACTION(rfCoreExecuteAbortCmd() == CMDSTA_Done, error = OT_ERROR_FAILED);
+
+            /* any frames in the queue will be for the old channel */
+            otEXPECT_ACTION(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done, error = OT_ERROR_FAILED);
+
+            sReceiveCmd.channel = aChannel;
+            otEXPECT_ACTION(rfCoreSendReceiveCmd() == CMDSTA_Done, error = OT_ERROR_FAILED);
+
+            sState = cc2650_stateReceive;
+            error = OT_ERROR_NONE;
+        }
+    }
+
+exit:
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioSleep(otInstance *aInstance)
+{
+    otError error = OT_ERROR_BUSY;
+    (void)aInstance;
+
+    if (sState == cc2650_stateSleep)
+    {
+        error = OT_ERROR_NONE;
+    }
+    else if (sState == cc2650_stateReceive)
+    {
+        if (rfCoreExecuteAbortCmd() != CMDSTA_Done)
+        {
+            error = OT_ERROR_BUSY;
+            return error;
+        }
+
+        sState = cc2650_stateSleep;
+        return error;
+    }
+
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
+{
+    (void)aInstance;
+    return &sTransmitFrame;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
+{
+    otError error = OT_ERROR_BUSY;
+
+    if (sState == cc2650_stateReceive)
+    {
+        /*
+         * This is the easiest way to setup the frequency synthesizer.
+         * And we are supposed to fall into the receive state afterwards.
+         */
+        error = otPlatRadioReceive(aInstance, aFrame->mChannel);
+
+        if (error == OT_ERROR_NONE)
+        {
+            sState = cc2650_stateTransmit;
+
+            /* removing 2 bytes of CRC placeholder because we generate that in hardware */
+            otEXPECT_ACTION(rfCoreSendTransmitCmd(aFrame->mPsdu, aFrame->mLength - 2) == CMDSTA_Done,
+                            error = OT_ERROR_FAILED);
+            error = OT_ERROR_NONE;
+        }
+    }
+
+exit:
+    sTransmitError = error;
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+int8_t otPlatRadioGetRssi(otInstance *aInstance)
+{
+    (void)aInstance;
+    return sRfStats.maxRssi;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
+{
+    (void)aInstance;
+    return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_RETRIES;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
+{
+    (void)aInstance;
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        rfCoreModifyRxAutoPend(aEnable);
+    }
+    else
+    {
+        /* if we are promiscuous, then frame filtering should be disabled */
+        sReceiveCmd.frameFiltOpt.autoPendEn = aEnable ? 1 : 0;
+    }
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
+{
+    otError error = OT_ERROR_NONE;
+    (void)aInstance;
+    uint8_t idx = rfCoreFindShortSrcMatchIdx(aShortAddress);
+
+    if (idx == CC2650_SRC_MATCH_NONE)
+    {
+        /* the entry does not exist already, add it */
+        otEXPECT_ACTION((idx = rfCoreFindEmptyShortSrcMatchIdx()) != CC2650_SRC_MATCH_NONE,
+                        error = OT_ERROR_NO_BUFS);
+        sSrcMatchShortData.extAddrEnt[idx].shortAddr = aShortAddress;
+        sSrcMatchShortData.extAddrEnt[idx].panId = sReceiveCmd.localPanID;
+    }
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, SHORT_ADDRESS, true) == CMDSTA_Done,
+                        error = OT_ERROR_FAILED);
+    }
+    else
+    {
+        /* we are not running, so we must update the values ourselves */
+        sSrcMatchShortData.srcPendEn[idx / 32] |= (1 << (idx % 32));
+        sSrcMatchShortData.srcMatchEn[idx / 32] |= (1 << (idx % 32));
+    }
+
+exit:
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
+{
+    otError error = OT_ERROR_NONE;
+    (void)aInstance;
+    uint8_t idx;
+    otEXPECT_ACTION((idx = rfCoreFindShortSrcMatchIdx(aShortAddress)) != CC2650_SRC_MATCH_NONE,
+                    error = OT_ERROR_NO_ADDRESS);
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, SHORT_ADDRESS, false) == CMDSTA_Done,
+                        error = OT_ERROR_FAILED);
+    }
+    else
+    {
+        /* we are not running, so we must update the values ourselves */
+        sSrcMatchShortData.srcPendEn[idx / 32] &= ~(1 << (idx % 32));
+        sSrcMatchShortData.srcMatchEn[idx / 32] &= ~(1 << (idx % 32));
+    }
+
+exit:
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress)
+{
+    otError error = OT_ERROR_NONE;
+    (void)aInstance;
+    uint8_t idx = rfCoreFindExtSrcMatchIdx((uint64_t *)aExtAddress);
+
+    if (idx == CC2650_SRC_MATCH_NONE)
+    {
+        /* the entry does not exist already, add it */
+        otEXPECT_ACTION((idx = rfCoreFindEmptyExtSrcMatchIdx()) != CC2650_SRC_MATCH_NONE, error = OT_ERROR_NO_BUFS);
+        sSrcMatchExtData.extAddrEnt[idx] = *((uint64_t *)aExtAddress);
+    }
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, EXT_ADDRESS, true) == CMDSTA_Done,
+                        error = OT_ERROR_FAILED);
+    }
+    else
+    {
+        /* we are not running, so we must update the values ourselves */
+        sSrcMatchExtData.srcPendEn[idx / 32] |= (1 << (idx % 32));
+        sSrcMatchExtData.srcMatchEn[idx / 32] |= (1 << (idx % 32));
+    }
+
+exit:
+    return error;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress)
+{
+    otError error = OT_ERROR_NONE;
+    (void)aInstance;
+    uint8_t idx;
+    otEXPECT_ACTION((idx = rfCoreFindExtSrcMatchIdx((uint64_t *)aExtAddress)) != CC2650_SRC_MATCH_NONE,
+                    error = OT_ERROR_NO_ADDRESS);
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, EXT_ADDRESS, false) == CMDSTA_Done,
+                        error = OT_ERROR_FAILED);
+    }
+    else
+    {
+        /* we are not running, so we must update the values ourselves */
+        sSrcMatchExtData.srcPendEn[idx] = 0u;
+        sSrcMatchExtData.srcMatchEn[idx] = 0u;
+        sSrcMatchExtData.srcPendEn[idx / 32] &= ~(1 << (idx % 32));
+        sSrcMatchExtData.srcMatchEn[idx / 32] &= ~(1 << (idx % 32));
+    }
+
+exit:
+    return error;
+}
+
+/**
+* Function documented in platform/radio.h
+*/
+void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
+{
+    (void)aInstance;
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        unsigned int i;
+
+        for (i = 0; i < CC2650_SHORTADD_SRC_MATCH_NUM; i++)
+        {
+            /* we have a running or backgrounded rx command */
+            otEXPECT(rfCoreModifySourceMatchEntry(i, SHORT_ADDRESS, false) == CMDSTA_Done);
+        }
+    }
+    else
+    {
+        /* we are not running, so we can erase them ourselves */
+        memset((void *)&sSrcMatchShortData, 0, sizeof(sSrcMatchShortData));
+    }
+
+exit:
+    return;
+}
+
+/**
+* Function documented in platform/radio.h
+*/
+void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
+{
+    (void)aInstance;
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        unsigned int i;
+
+        for (i = 0; i < CC2650_EXTADD_SRC_MATCH_NUM; i++)
+        {
+            /* we have a running or backgrounded rx command */
+            otEXPECT(rfCoreModifySourceMatchEntry(i, EXT_ADDRESS, false) == CMDSTA_Done);
+        }
+    }
+    else
+    {
+        /* we are not running, so we can erase them ourselves */
+        memset((void *)&sSrcMatchExtData, 0, sizeof(sSrcMatchExtData));
+    }
+
+exit:
+    return;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+bool otPlatRadioGetPromiscuous(otInstance *aInstance)
+{
+    (void)aInstance;
+    /* we are promiscuous if we are not filtering */
+    return sReceiveCmd.frameFiltOpt.frameFiltEn == 0;
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
+{
+    (void)aInstance;
+
+    if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED)
+    {
+        /* we have a running or backgrounded rx command */
+        /* if we are promiscuous, then frame filtering should be disabled */
+        rfCoreModifyRxFrameFilter(!aEnable);
+        /* XXX should we dump any queued messages ? */
+    }
+    else
+    {
+        /* if we are promiscuous, then frame filtering should be disabled */
+        sReceiveCmd.frameFiltOpt.frameFiltEn = aEnable ? 0 : 1;
+    }
+}
+
+/**
+ * Function documented in platform/radio.h
+ */
+void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
+{
+    uint8_t *eui64;
+    unsigned int i;
+    (void)aInstance;
+
+    /* The IEEE MAC address can be stored two places. We check the Customer
+     * Configuration was not set before defaulting to the Factory
+     * Configuration.
+     */
+    eui64 = (uint8_t *)(CCFG_BASE + CCFG_O_IEEE_MAC_0);
+
+    for (i = 0; i < OT_EXT_ADDRESS_SIZE; i++)
+    {
+        if (eui64[i] != CC2650_UNKNOWN_EUI64)
+        {
+            break;
+        }
+    }
+
+    if (i >= OT_EXT_ADDRESS_SIZE)
+    {
+        /* The ccfg address was all 0xFF, switch to the fcfg */
+        eui64 = (uint8_t *)(FCFG1_BASE + FCFG1_O_MAC_15_4_0);
+    }
+
+    /* The IEEE MAC address is stored in network byte order (big endian).
+     * The caller seems to want the address stored in little endian format,
+     * which is backwards of the conventions setup by @ref
+     * otPlatRadioSetExtendedAddress. otPlatRadioSetExtendedAddress assumes
+     * that the address being passed to it is in network byte order (big
+     * endian), so the caller of otPlatRadioSetExtendedAddress must swap the
+     * endianness before calling.
+     *
+     * It may be easier to have the caller of this function store the IEEE
+     * address in network byte order (big endian).
+     */
+    for (i = 0; i < OT_EXT_ADDRESS_SIZE; i++)
+    {
+        aIeeeEui64[i] = eui64[(OT_EXT_ADDRESS_SIZE - 1) - i];
+    }
+}
+
+/**
+ * Function documented in platform/radio.h
+ *
+ * @note it is entirely possible for this function to fail, but there is no
+ * valid way to return that error since the funciton prototype was changed.
+ */
+void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid)
+{
+    (void)aInstance;
+
+    /* XXX: if the pan id is the broadcast pan id (0xFFFF) the auto ack will
+     * not work. This is due to the design of the CM0 and follows IEEE 802.15.4
+     */
+    if (sState == cc2650_stateReceive)
+    {
+        otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done);
+        sReceiveCmd.localPanID = aPanid;
+        otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done);
+        otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done);
+        /* the interrupt from abort changed our state to sleep */
+        sState = cc2650_stateReceive;
+    }
+    else if (sState != cc2650_stateTransmit)
+    {
+        sReceiveCmd.localPanID = aPanid;
+    }
+
+exit:
+    return;
+}
+
+/**
+ * Function documented in platform/radio.h
+ *
+ * @note it is entirely possible for this function to fail, but there is no
+ * valid way to return that error since the funciton prototype was changed.
+ */
+void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aAddress)
+{
+    (void)aInstance;
+
+    /* XXX: assuming little endian format */
+    if (sState == cc2650_stateReceive)
+    {
+        otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done);
+        sReceiveCmd.localExtAddr = *((uint64_t *)(aAddress));
+        otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done);
+        otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done);
+        /* the interrupt from abort changed our state to sleep */
+        sState = cc2650_stateReceive;
+    }
+    else if (sState != cc2650_stateTransmit)
+    {
+        sReceiveCmd.localExtAddr = *((uint64_t *)(aAddress));
+    }
+
+exit:
+    return;
+}
+
+/**
+ * Function documented in platform/radio.h
+ *
+ * @note it is entirely possible for this function to fail, but there is no
+ * valid way to return that error since the funciton prototype was changed.
+ */
+void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
+{
+    (void)aInstance;
+
+    if (sState == cc2650_stateReceive)
+    {
+        otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done);
+        sReceiveCmd.localShortAddr = aAddress;
+        otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done);
+        otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done);
+        /* the interrupt from abort changed our state to sleep */
+        sState = cc2650_stateReceive;
+    }
+    else if (sState != cc2650_stateTransmit)
+    {
+        sReceiveCmd.localShortAddr = aAddress;
+    }
+
+exit:
+    return;
+}
+
+/**
+ * @brief search the receive queue for unprocessed messages
+ *
+ * Loop through the receive queue structure looking for data entries that the
+ * radio core has marked as finished. Then place those in @ref sReceiveFrame
+ * and mark any errors in @ref sReceiveError.
+ */
+static void readFrame(void)
+{
+    rfc_ieeeRxCorrCrc_t *crcCorr;
+    uint8_t rssi;
+    rfc_dataEntryGeneral_t *startEntry = (rfc_dataEntryGeneral_t *)sRxDataQueue.pCurrEntry;
+    rfc_dataEntryGeneral_t *curEntry = startEntry;
+
+    /* loop through receive queue */
+    do
+    {
+        uint8_t *payload = &(curEntry->data);
+
+        if (sReceiveFrame.mLength == 0 && curEntry->status == DATA_ENTRY_FINISHED)
+        {
+            uint8_t len = payload[0];
+            /* get the information appended to the end of the frame.
+             * This array access looks like it is a fencepost error, but the
+             * first byte is the number of bytes that follow.
+             */
+            crcCorr = (rfc_ieeeRxCorrCrc_t *)&payload[len];
+            rssi = payload[len - 1];
+
+            if (crcCorr->status.bCrcErr == 0 && (len - 2) < OT_RADIO_FRAME_MAX_SIZE)
+            {
+                sReceiveFrame.mLength = len;
+                memcpy(sReceiveFrame.mPsdu, &(payload[1]), len - 2);
+                sReceiveFrame.mChannel = sReceiveCmd.channel;
+                sReceiveFrame.mPower = rssi;
+                sReceiveFrame.mLqi = crcCorr->status.corr;
+                sReceiveError = OT_ERROR_NONE;
+            }
+            else
+            {
+                sReceiveError = OT_ERROR_FCS;
+            }
+
+            curEntry->status = DATA_ENTRY_PENDING;
+            break;
+        }
+        else if (curEntry->status == DATA_ENTRY_UNFINISHED)
+        {
+            curEntry->status = DATA_ENTRY_PENDING;
+        }
+
+        curEntry = (rfc_dataEntryGeneral_t *)(curEntry->pNextEntry);
+    }
+    while (curEntry != startEntry);
+
+    return;
+}
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void cc2650RadioProcess(otInstance *aInstance)
+{
+    if (sState == cc2650_stateEdScan)
+    {
+        if (sEdScanCmd.status == IEEE_DONE_OK)
+        {
+            otPlatRadioEnergyScanDone(aInstance, sEdScanCmd.maxRssi);
+        }
+        else if (sEdScanCmd.status == ACTIVE)
+        {
+            otPlatRadioEnergyScanDone(aInstance, CC2650_INVALID_RSSI);
+        }
+    }
+
+    if (sState == cc2650_stateTransmitComplete || sTransmitError != OT_ERROR_NONE)
+    {
+        /* we are not looking for an ACK packet, or failed */
+        sState = cc2650_stateReceive;
+#if OPENTHREAD_ENABLE_DIAG
+
+        if (otPlatDiagModeGet())
+        {
+            otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError);
+        }
+        else
+#endif /* OPENTHREAD_ENABLE_DIAG */
+        {
+            otPlatRadioTransmitDone(aInstance, &sTransmitFrame, sReceivedAckPendingBit, sTransmitError);
+        }
+    }
+
+    if (sState == cc2650_stateReceive || sState == cc2650_stateTransmit)
+    {
+        readFrame();
+
+        if (sReceiveFrame.mLength > 0)
+        {
+#if OPENTHREAD_ENABLE_DIAG
+
+            if (otPlatDiagModeGet())
+            {
+                otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError);
+            }
+            else
+#endif /* OPENTHREAD_ENABLE_DIAG */
+            {
+                otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError);
+            }
+        }
+
+        sReceiveFrame.mLength = 0;
+    }
+}
+
+int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
+{
+    (void)aInstance;
+    return CC2650_RECEIVE_SENSITIVITY;
+}
diff --git a/examples/platforms/cc2650/random.c b/examples/platforms/cc2650/random.c
new file mode 100644
index 0000000..e31d930
--- /dev/null
+++ b/examples/platforms/cc2650/random.c
@@ -0,0 +1,136 @@
+/*
+ *  Copyright (c) 2017, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/types.h>
+#include <utils/code_utils.h>
+
+#include <driverlib/prcm.h>
+#include <driverlib/trng.h>
+
+#include <openthread/platform/random.h>
+
+#include <mbedtls/entropy_poll.h>
+
+enum
+{
+    CC2650_TRNG_MIN_SAMPLES_PER_CYCLE = (1 << 6),
+    CC2650_TRNG_MAX_SAMPLES_PER_CYCLE = (1 << 24),
+    CC2650_TRNG_CLOCKS_PER_SAMPLE     = 0,
+};
+
+/**
+ * \note if more than 32 bits of entropy are needed, the TRNG core produces
+ * 64 bytes of random data, we just ignore the upper 32 bytes
+ */
+
+/**
+ * Function documented in platform-cc2650.h
+ */
+void cc2650RandomInit(void)
+{
+    PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
+
+    while (PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_ON);
+
+    PRCMPeripheralRunEnable(PRCM_PERIPH_TRNG);
+    PRCMPeripheralSleepEnable(PRCM_DOMAIN_PERIPH);
+    PRCMPeripheralDeepSleepEnable(PRCM_DOMAIN_PERIPH);
+    PRCMLoadSet();
+    TRNGConfigure(CC2650_TRNG_MIN_SAMPLES_PER_CYCLE, CC2650_TRNG_MAX_SAMPLES_PER_CYCLE, CC2650_TRNG_CLOCKS_PER_SAMPLE);
+    TRNGEnable();
+}
+
+/**
+ * Function documented in platform/random.h
+ */
+uint32_t otPlatRandomGet(void)
+{
+    while (!(TRNGStatusGet() & TRNG_NUMBER_READY));
+
+    return TRNGNumberGet(TRNG_LOW_WORD);
+}
+
+/**
+ * Fill an arbitrary area with random data
+ *
+ * @param [out] aOutput area to place the random data
+ * @param [in] aLen size of the area to place random data
+ * @param [out] oLen how much of the output was written to
+ *
+ * @return indication of error
+ * @retval 0 no error occured
+ */
+static int TRNGPoll(unsigned char *aOutput, size_t aLen)
+{
+    size_t length = 0;
+    union
+    {
+        uint32_t u32[2];
+        uint8_t u8[8];
+    } buffer;
+
+    while (length < aLen)
+    {
+        if (length % 8 == 0)
+        {
+            /* we've run to the end of the buffer */
+            while (!(TRNGStatusGet() & TRNG_NUMBER_READY));
+
+            /*
+             * don't use TRNGNumberGet here because it will tell the TRNG to
+             * refil the entropy pool, instad we do it ourself.
+             */
+            buffer.u32[0] = HWREG(TRNG_BASE + TRNG_O_OUT0);
+            buffer.u32[1] = HWREG(TRNG_BASE + TRNG_O_OUT1);
+            HWREG(TRNG_BASE + TRNG_O_IRQFLAGCLR) = 0x1;
+        }
+
+        aOutput[length] = buffer.u8[length % 8];
+
+        length++;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Function documented in platform/random.h
+ */
+otError otPlatRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength)
+{
+    otError error = OT_ERROR_NONE;
+    size_t length = aOutputLength;
+
+    otEXPECT_ACTION(aOutput, error = OT_ERROR_INVALID_ARGS);
+
+    otEXPECT_ACTION(TRNGPoll((unsigned char *)aOutput, length) != 0, error = OT_ERROR_FAILED);
+
+exit:
+