diff --git a/.appveyor/install_driver.cmd b/.appveyor/install_driver.cmd
index 36ecb6b1a..73e0742a8 100644
--- a/.appveyor/install_driver.cmd
+++ b/.appveyor/install_driver.cmd
@@ -36,7 +36,7 @@ certutil -addstore TrustedPublisher otLwf.cer
cd otLwf
-REM Install the NDSI LWF driver, otLwf.sys
+REM Install the NDIS LWF driver, otLwf.sys
netcfg.exe -v -l otlwf.inf -c s -i otLwf
diff --git a/.appveyor/test_driver.cmd b/.appveyor/test_driver.cmd
index 0455577d3..74254195a 100644
--- a/.appveyor/test_driver.cmd
+++ b/.appveyor/test_driver.cmd
@@ -34,6 +34,10 @@ 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
diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj
index d41ec3bf1..614990728 100644
--- a/etc/visual-studio/UnitTests.vcxproj
+++ b/etc/visual-studio/UnitTests.vcxproj
@@ -59,12 +59,6 @@
%(AdditionalLibraryDirectories);
$(VCInstallDir)UnitTest\lib;
-
- %(AdditionalDependencies);
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-uart.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib;
-
diff --git a/etc/visual-studio/openthread.sln b/etc/visual-studio/openthread.sln
index d76e83ddc..8313dcd5d 100644
--- a/etc/visual-studio/openthread.sln
+++ b/etc/visual-studio/openthread.sln
@@ -48,6 +48,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otLwf", "otLwf.vcxproj", "{
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otApi", "otApi.vcxproj", "{ED0EA262-C222-42C7-98D3-E70C72978ED2}"
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}
@@ -245,6 +249,30 @@ Global
{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
+ {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
@@ -282,6 +310,8 @@ Global
{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}
+ {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}
EndGlobalSection
EndGlobal
diff --git a/etc/visual-studio/ot-cli.vcxproj b/etc/visual-studio/ot-cli.vcxproj
index 10c7073e2..121890ab2 100644
--- a/etc/visual-studio/ot-cli.vcxproj
+++ b/etc/visual-studio/ot-cli.vcxproj
@@ -52,12 +52,7 @@
Console
- %(AdditionalDependencies);
- ws2_32.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-cli.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib;
+ mincore.lib;
diff --git a/etc/visual-studio/ot-ncp-spi.vcxproj b/etc/visual-studio/ot-ncp-spi.vcxproj
index 09ef434fa..cc56e3d19 100644
--- a/etc/visual-studio/ot-ncp-spi.vcxproj
+++ b/etc/visual-studio/ot-ncp-spi.vcxproj
@@ -52,12 +52,7 @@
Console
- %(AdditionalDependencies);
- ws2_32.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-spi.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib;
+ mincore.lib;
diff --git a/etc/visual-studio/ot-ncp-uart.vcxproj b/etc/visual-studio/ot-ncp-uart.vcxproj
index 9f0cbc99d..c1c8767ab 100644
--- a/etc/visual-studio/ot-ncp-uart.vcxproj
+++ b/etc/visual-studio/ot-ncp-uart.vcxproj
@@ -52,12 +52,7 @@
Console
- %(AdditionalDependencies);
- ws2_32.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-uart.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib;
+ mincore.lib;
diff --git a/etc/visual-studio/otApi.vcxproj b/etc/visual-studio/otApi.vcxproj
index a65173ca1..fb7f10050 100644
--- a/etc/visual-studio/otApi.vcxproj
+++ b/etc/visual-studio/otApi.vcxproj
@@ -39,7 +39,6 @@
Level3
%(PreprocessorDefinitions);
- WIN32;_WINDOWS;_USRDLL;
OTAPI_EXPORTS;
true
@@ -53,17 +52,8 @@
..\..\include\platform\logging-windows.h
otApi
true
- Sync
- Sync
+ Sync
-
-
- onecore.lib;
- onecoreuap.lib;
- mincore.lib;
- ntdll.lib;
-
-
diff --git a/etc/visual-studio/otLwf.vcxproj b/etc/visual-studio/otLwf.vcxproj
index 315961910..116e7ef95 100644
--- a/etc/visual-studio/otLwf.vcxproj
+++ b/etc/visual-studio/otLwf.vcxproj
@@ -1,6 +1,6 @@
-
+
{3F1F7F6C-2A33-4635-9880-08FC5BC4E435}
{8b1800b9-d017-4029-9785-13ef5e5b328e}
@@ -76,8 +76,6 @@
netio.lib;
uuid.lib;
cng.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread_k.lib;
- ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls_k.lib
@@ -88,6 +86,7 @@
+
..;.;%(AdditionalIncludeDirectories)
@@ -98,9 +97,6 @@
-
-
-
@@ -109,6 +105,8 @@
+
+
diff --git a/etc/visual-studio/otNodeApi.vcxproj b/etc/visual-studio/otNodeApi.vcxproj
new file mode 100644
index 000000000..ec3f11940
--- /dev/null
+++ b/etc/visual-studio/otNodeApi.vcxproj
@@ -0,0 +1,77 @@
+
+
+
+
+ {B7C6F344-7287-4930-AF38-223622BD8CBB}
+ Win32Proj
+ Thread
+ 12.0
+ Universal
+
+
+
+ DynamicLibrary
+ true
+ WindowsApplicationForDrivers10.0
+
+
+ DynamicLibrary
+ false
+ WindowsApplicationForDrivers10.0
+ true
+
+
+
+
+
+
+
+
+
+ ..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\
+ ..\..\build\bin\$(Platform)\$(Configuration)\dll\
+ true
+ false
+
+
+
+
+ %(PreprocessorDefinitions);
+ OTAPI_EXPORTS;
+
+
+ %(AdditionalIncludeDirectories);
+ ..\..\examples\drivers\windows\include;
+ ..\..\examples\drivers\windows\otApi;
+ ..\..\include;
+
+ Level3
+ true
+ true
+ ..\..\include\platform\logging-windows.h
+ otNodeApi
+ true
+ Sync
+
+
+
+ ntdll.lib;
+
+
+
+
+
+
+
+
+
+
+
+
+ {ed0ea262-c222-42c7-98d3-e70c72978ed2}
+
+
+
+
+
+
\ 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 000000000..8e527aeea
--- /dev/null
+++ b/etc/visual-studio/otNodeApi.vcxproj.filters
@@ -0,0 +1,30 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+
\ 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 000000000..958403786
--- /dev/null
+++ b/etc/visual-studio/otTestRunner.csproj
@@ -0,0 +1,112 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D5577E51-FA31-4802-8669-1DB32805935E}
+ Exe
+ Properties
+ otTestRunner
+ otTestRunner
+ v4.5.2
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ DEBUG;TRACE
+ full
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ TRACE
+ true
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ true
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\
+ ..\..\build\bin\$(Platform)\$(Configuration)\exe\
+ TRACE
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/drivers/windows/include/otNode.h b/examples/drivers/windows/include/otNode.h
new file mode 100644
index 000000000..175c2d30e
--- /dev/null
+++ b/examples/drivers/windows/include/otNode.h
@@ -0,0 +1,372 @@
+/*
+ * 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
+
+#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);
+
+/**
+ * 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);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // OTNODE_H_
diff --git a/examples/drivers/windows/otApi/otApi.cpp b/examples/drivers/windows/otApi/otApi.cpp
index be7595169..015b905c3 100644
--- a/examples/drivers/windows/otApi/otApi.cpp
+++ b/examples/drivers/windows/otApi/otApi.cpp
@@ -2837,6 +2837,17 @@ otPlatformReset(
if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_PLATFORM_RESET);
}
+OTAPI
+void
+OTCALL
+otFactoryReset(
+ _In_ otInstance *aInstance
+ )
+{
+ UNREFERENCED_PARAMETER(aInstance);
+ // TODO ...
+}
+
OTAPI
ThreadError
OTCALL
diff --git a/examples/drivers/windows/otNodeApi/dllmain.cpp b/examples/drivers/windows/otNodeApi/dllmain.cpp
new file mode 100644
index 000000000..beae2f684
--- /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 000000000..74a29b4de
--- /dev/null
+++ b/examples/drivers/windows/otNodeApi/otNodeApi.cpp
@@ -0,0 +1,2056 @@
+/*
+ * 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);
+
+fp_otvmpOpenHandle otvmpOpenHandle = nullptr;
+fp_otvmpCloseHandle otvmpCloseHandle = nullptr;
+fp_otvmpAddVirtualBus otvmpAddVirtualBus = nullptr;
+fp_otvmpRemoveVirtualBus otvmpRemoveVirtualBus = nullptr;
+fp_otvmpSetAdapterTopologyGuid otvmpSetAdapterTopologyGuid = nullptr;
+
+HMODULE gVmpModule = nullptr;
+HANDLE gVmpHandle = nullptr;
+
+ULONG gNextBusNumber = 1;
+GUID gTopologyGuid = {0};
+
+volatile LONG gNumberOfInterfaces = 0;
+
+otApiInstance *gApiInstance = nullptr;
+
+_Success_(return == kThreadError_None)
+ThreadError 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 kThreadError_InvalidArgs;
+ }
+
+ *prefixLengthStr++ = '\0';
+
+ auto error = otIp6AddressFromString(aStrPrefix, &aPrefix->mPrefix);
+ if (error != kThreadError_None)
+ {
+ printf("ipaddr (%s) to string failed, 0x%x!\r\n", aStrPrefix, error);
+ return error;
+ }
+
+ aPrefix->mLength = static_cast(strtol(prefixLengthStr, &endptr, 0));
+
+ if (*endptr != '\0')
+ {
+ printf("invalid prefix ending (%s)!\r\n", aStrPrefix);
+ return kThreadError_Parse;
+ }
+
+ return kThreadError_None;
+}
+
+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");
+ return nullptr;
+ }
+
+ gVmpModule = LoadLibrary(TEXT("otvmpapi.dll"));
+ if (gVmpModule == nullptr)
+ {
+ printf("LoadLibrary(\"otvmpapi\") failed!\r\n");
+ 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");
+
+ assert(otvmpOpenHandle);
+ assert(otvmpCloseHandle);
+ assert(otvmpAddVirtualBus);
+ assert(otvmpRemoveVirtualBus);
+ assert(otvmpSetAdapterTopologyGuid);
+
+ 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");
+
+ (VOID)otvmpOpenHandle(&gVmpHandle);
+ if (gVmpHandle == nullptr)
+ {
+ printf("otvmpOpenHandle failed!\r\n");
+ return nullptr;
+ }
+
+ auto status = UuidCreate(&gTopologyGuid);
+ if (status != NO_ERROR)
+ {
+ printf("UuidCreate failed, 0x%x!\r\n", status);
+ return nullptr;
+ }
+
+ auto offset = getenv("INSTANCE");
+ if (offset)
+ {
+ gNextBusNumber = (atoi(offset) * 32) % 1000 + 1;
+ }
+ else
+ {
+ srand(gTopologyGuid.Data1);
+ gNextBusNumber = rand() % 1000 + 1;
+ }
+
+ printf("New topology created\r\n" GUID_FORMAT " [%d]\r\n\r\n", GUID_ARG(gTopologyGuid), gNextBusNumber);
+ }
+
+ return gApiInstance;
+}
+
+void Unload()
+{
+ if (gNumberOfInterfaces != 0)
+ {
+ printf("Unloaded with %d outstanding nodes!\r\n", gNumberOfInterfaces);
+ }
+
+ if (gApiInstance)
+ {
+ if (gVmpHandle != nullptr)
+ {
+ otvmpCloseHandle(gVmpHandle);
+ gVmpHandle = nullptr;
+ }
+
+ if (gVmpModule != nullptr)
+ {
+ CloseHandle(gVmpModule);
+ gVmpModule = nullptr;
+ }
+
+ 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(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;
+ otInstance* mInstance;
+ HANDLE mEnergyScanEvent;
+ HANDLE mPanIdConflictEvent;
+ CRITICAL_SECTION mCS;
+ vector mPingHandlers;
+ vector mMemoryToFree;
+} otNode;
+
+const char* otDeviceRoleToString(otDeviceRole role)
+{
+ switch (role)
+ {
+ case kDeviceRoleOffline: return "offline";
+ case kDeviceRoleDisabled: return "disabled";
+ case kDeviceRoleDetached: return "detached";
+ case kDeviceRoleChild: return "child";
+ case kDeviceRoleRouter: return "router";
+ case kDeviceRoleLeader: 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 = otGetDeviceRole(aPingHandler->mParentNode->mInstance);
+ if (Role != kDeviceRoleLeader && Role != kDeviceRoleRouter)
+ 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 = otGetMeshLocalEid(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 = otGetUnicastAddresses(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 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_NET_ROLE) != 0)
+ {
+ auto Role = otGetDeviceRole(aNode->mInstance);
+ printf("%d: new role: %s\r\n", aNode->mId, otDeviceRoleToString(Role));
+ }
+
+ if ((aFlags & OT_IP6_ADDRESS_ADDED) != 0 || (aFlags & OT_IP6_ADDRESS_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;
+ }
+
+ DWORD newBusIndex;
+ NET_IFINDEX ifIndex = {};
+
+ 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)
+ {
+ 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");
+ return nullptr;
+ }
+ }
+
+ if (tries == 1000)
+ {
+ printf("otvmpAddVirtualBus failed to find an empty bus!\r\n");
+ otLogFuncExitMsg("otvmpAddVirtualBus failed to find an empty bus");
+ return nullptr;
+ }
+
+ if ((dwError = otvmpSetAdapterTopologyGuid(gVmpHandle, newBusIndex, &gTopologyGuid)) != ERROR_SUCCESS)
+ {
+ printf("otvmpSetAdapterTopologyGuid failed, 0x%x!\r\n", dwError);
+ otLogFuncExitMsg("otvmpSetAdapterTopologyGuid failed");
+ otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
+ return nullptr;
+ }
+
+ NET_LUID ifLuid = {};
+ if (ERROR_SUCCESS != ConvertInterfaceIndexToLuid(ifIndex, &ifLuid))
+ {
+ printf("ConvertInterfaceIndexToLuid(%u) failed!\r\n", ifIndex);
+ otLogFuncExitMsg("ConvertInterfaceIndexToLuid failed");
+ otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
+ return nullptr;
+ }
+
+ GUID ifGuid = {};
+ if (ERROR_SUCCESS != ConvertInterfaceLuidToGuid(&ifLuid, &ifGuid))
+ {
+ printf("ConvertInterfaceLuidToGuid failed!\r\n");
+ otLogFuncExitMsg("ConvertInterfaceLuidToGuid failed");
+ otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
+ return nullptr;
+ }
+
+ auto instance = otInstanceInit(ApiInstance, &ifGuid);
+ if (instance == nullptr)
+ {
+ printf("otInstanceInit failed!\r\n");
+ otLogFuncExitMsg("otInstanceInit failed");
+ otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
+ return nullptr;
+ }
+
+ InterlockedIncrement(&gNumberOfInterfaces);
+
+ GUID DeviceGuid = otGetDeviceGuid(instance);
+ uint32_t Compartment = otGetCompartmentId(instance);
+
+ otNode *node = new otNode();
+ printf("%d: New Device " GUID_FORMAT " in compartment %d\r\n", id, GUID_ARG(DeviceGuid), Compartment);
+
+ node->mId = id;
+ node->mBusIndex = newBusIndex;
+ node->mInstance = instance;
+
+ node->mEnergyScanEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ node->mPanIdConflictEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+
+ InitializeCriticalSection(&node->mCS);
+
+ otSetStateChangedCallback(instance, otNodeStateChangedCallback, node);
+
+ HandleAddressChanges(node);
+
+ otLogFuncExitMsg("success. [%d] = %!GUID!", id, &DeviceGuid);
+
+ 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);
+
+ // 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;
+
+ if (0 == InterlockedDecrement(&gNumberOfInterfaces))
+ {
+ // Uninitialize everything else if this is the last ref
+ Unload();
+ }
+ }
+ 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 = otSetLinkMode(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 = otInterfaceUp(aNode->mInstance);
+
+ otLogFuncExit();
+ return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ printf("%d: ifconfig down\r\n", aNode->mId);
+
+ (void)otInterfaceDown(aNode->mInstance);
+
+ otLogFuncExit();
+ return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ printf("%d: thread start\r\n", aNode->mId);
+
+ auto error = otThreadStart(aNode->mInstance);
+
+ otLogFuncExit();
+ return error;
+}
+
+OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ printf("%d: thread stop\r\n", aNode->mId);
+
+ (void)otThreadStop(aNode->mInstance);
+
+ 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);
+
+ ThreadError error;
+
+ if (strcmp(aExtAddr, "*") == 0)
+ {
+ error = otCommissionerAddJoiner(aNode->mInstance, nullptr, aPSKd);
+ }
+ else
+ {
+ otExtAddress extAddr;
+ if (Hex2Bin(aExtAddr, extAddr.m8, sizeof(extAddr)) != sizeof(extAddr))
+ return kThreadError_Parse;
+
+ error = otCommissionerAddJoiner(aNode->mInstance, &extAddr, aPSKd);
+ }
+
+ 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);
+
+ auto error = otJoinerStart(aNode->mInstance, aPSKd, aProvisioningUrl);
+
+ 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);
+
+ otClearMacWhitelist(aNode->mInstance);
+ otLogFuncExit();
+ return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ printf("%d: whitelist enable\r\n", aNode->mId);
+
+ otEnableMacWhitelist(aNode->mInstance);
+ otLogFuncExit();
+ return 0;
+}
+
+OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ printf("%d: whitelist disable\r\n", aNode->mId);
+
+ otDisableMacWhitelist(aNode->mInstance);
+ 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 kThreadError_Parse;
+
+ ThreadError error;
+ if (aRssi == 0)
+ {
+ error = otAddMacWhitelist(aNode->mInstance, extAddr);
+ }
+ else
+ {
+ error = otAddMacWhitelistRssi(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 kThreadError_InvalidArgs;
+
+ otRemoveMacWhitelist(aNode->mInstance, extAddr);
+ otLogFuncExit();
+ return 0;
+}
+
+OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetRloc16(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 = {};
+ otGetHashMacAddress(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 = otGetExtendedAddress(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 = otSetChannel(aNode->mInstance, aChannel);
+ otLogFuncExit();
+ return result;
+}
+
+OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetChannel(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;
+ uint8_t key[OT_MASTER_KEY_SIZE];
+ if ((keyLength = Hex2Bin(aMasterkey, key, sizeof(key))) != OT_MASTER_KEY_SIZE)
+ {
+ printf("invalid length key %d\r\n", keyLength);
+ return kThreadError_Parse;
+ }
+
+ auto error = otSetMasterKey(aNode->mInstance, key, (uint8_t)keyLength);
+ otLogFuncExit();
+ return error;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ uint8_t aKeyLength = 0;
+ auto aMasterKey = otGetMasterKey(aNode->mInstance, &aKeyLength);
+ uint8_t strLength = 2*aKeyLength + 1;
+ char* str = (char*)malloc(strLength);
+ if (str != nullptr)
+ {
+ aNode->mMemoryToFree.push_back(str);
+ for (int i = 0; i < aKeyLength; i++)
+ sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aMasterKey[i]);
+ printf("%d: masterkey\r\n%s\r\n", aNode->mId, str);
+ }
+ otFreeMemory(aMasterKey);
+ otLogFuncExit();
+ return str;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetKeySequenceCounter(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);
+ otSetKeySequenceCounter(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);
+ otSetKeySwitchGuardTime(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);
+ otSetNetworkIdTimeout(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 = otSetNetworkName(aNode->mInstance, aName);
+ otLogFuncExit();
+ return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetNetworkName(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 = otGetPanId(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 = otSetPanId(aNode->mInstance, aPanId);
+ otLogFuncExit();
+ return result;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetLocalLeaderPartitionId(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);
+ otSetLocalLeaderPartitionId(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);
+ otSetRouterUpgradeThreshold(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);
+ otSetRouterDowngradeThreshold(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 = otReleaseRouterId(aNode->mInstance, aRouterId);
+ otLogFuncExit();
+ return result;
+}
+
+OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto role = otGetDeviceRole(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);
+
+ ThreadError error;
+ if (strcmp(aState, "detached") == 0)
+ {
+ error = otBecomeDetached(aNode->mInstance);
+ }
+ else if (strcmp(aState, "child") == 0)
+ {
+ error = otBecomeChild(aNode->mInstance, kMleAttachAnyPartition);
+ }
+ else if (strcmp(aState, "router") == 0)
+ {
+ error = otBecomeRouter(aNode->mInstance);
+ }
+ else if (strcmp(aState, "leader") == 0)
+ {
+ error = otBecomeLeader(aNode->mInstance);
+ }
+ else
+ {
+ error = kThreadError_InvalidArgs;
+ }
+ otLogFuncExit();
+ return error;
+}
+
+OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetChildTimeout(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);
+ otSetChildTimeout(aNode->mInstance, aTimeout);
+ otLogFuncExit();
+ return 0;
+}
+
+OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode)
+{
+ otLogFuncEntryMsg("[%d]", aNode->mId);
+ auto result = otGetLeaderWeight(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);
+ otSetLocalLeaderWeight(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 != kThreadError_None) return error;
+
+ aAddress.mPrefixLength = 64;
+ aAddress.mPreferredLifetime = 0xffffffff;
+ aAddress.mValidLifetime = 0xffffffff;
+ auto result = otAddUnicastAddress(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 = otGetUnicastAddresses(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 = otGetContextIdReuseDelay(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);
+ otSetContextIdReuseDelay(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 != kThreadError_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 kThreadError_InvalidArgs;
+ }
+
+ index++;
+ }
+
+ if (strcmp(aPreference, "high") == 0)
+ {
+ config.mPreference = 1;
+ }
+ else if (strcmp(aPreference, "med") == 0)
+ {
+ config.mPreference = 1;
+ }
+ else if (strcmp(aPreference, "low") == 0)
+ {
+ config.mPreference = -1;
+ }
+ else
+ {
+ return kThreadError_InvalidArgs;
+ }
+
+ auto result = otAddBorderRouter(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 != kThreadError_None) return error;
+
+ auto result = otRemoveBorderRouter(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 != kThreadError_None) return error;
+
+ if (strcmp(aPreference, "high") == 0)
+ {
+ config.mPreference = 1;
+ }
+ else if (strcmp(aPreference, "med") == 0)
+ {
+ config.mPreference = 1;
+ }
+ else if (strcmp(aPreference, "low") == 0)
+ {
+ config.mPreference = -1;
+ }
+ else
+ {
+ return kThreadError_InvalidArgs;
+ }
+
+ auto result = otAddExternalRoute(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 != kThreadError_None) return error;
+
+ auto result = otRemoveExternalRoute(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 = otSendServerData(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 != kThreadError_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 != kThreadError_None)
+ {
+ printf("otCommissionerEnergyScan failed, 0x%x!\r\n", error);
+ return error;
+ }
+
+ auto result = WaitForSingleObject(aNode->mEnergyScanEvent, 8000) == WAIT_OBJECT_0 ? kThreadError_None : kThreadError_NotFound;
+ 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 != kThreadError_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 != kThreadError_None)
+ {
+ printf("otCommissionerPanIdQuery failed, 0x%x!\r\n", error);
+ return error;
+ }
+
+ auto result = WaitForSingleObject(aNode->mPanIdConflictEvent, 8000) == WAIT_OBJECT_0 ? kThreadError_None : kThreadError_NotFound;
+ 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)
+{
+ 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 != kThreadError_None)
+ {
+ printf("otIp6AddressFromString(%s) failed!\r\n", aAddr);
+ return 0;
+ }
+
+ // Get ML-EID as source address for ping
+ auto otSourceAddress = otGetMeshLocalEid(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
+ result = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, (DWORD)(2000 - (GetTickCount64() - StartTick)), 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);
+ otSetRouterSelectionJitter(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 != kThreadError_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 kThreadError_Parse;
+ }
+ aDataset.mIsMasterKeySet = true;
+ }
+
+ auto result = otSetActiveDataset(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 = otSetPendingDataset(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 kThreadError_Parse;
+ }
+ aDataset.mIsMasterKeySet = true;
+ }
+
+ if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
+ {
+ otIp6Address prefix;
+ auto error = otIp6AddressFromString(aMeshLocal, &prefix);
+ if (error != kThreadError_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 = otSendPendingSet(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 kThreadError_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 kThreadError_Parse;
+ }
+ aDataset.mIsMasterKeySet = true;
+ }
+
+ if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
+ {
+ otIp6Address prefix;
+ auto error = otIp6AddressFromString(aMeshLocal, &prefix);
+ if (error != kThreadError_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 kThreadError_Parse;
+ }
+ tlvsLength = (uint8_t)length;
+ }
+
+ auto result = otSendActiveSet(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 = otSetMaxAllowedChildren(aNode->mInstance, aMaxChildren);
+ otLogFuncExit();
+ return result;
+}
diff --git a/examples/drivers/windows/otNodeApi/precomp.h b/examples/drivers/windows/otNodeApi/precomp.h
new file mode 100644
index 000000000..d234355ca
--- /dev/null
+++ b/examples/drivers/windows/otNodeApi/precomp.h
@@ -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.
+ */
+
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+// Define to export necessary functions
+#define OTDLL
+#define OTNODEAPI EXTERN_C __declspec(dllexport)
+
+#include
+#include
+#include
+#include
+#include
+
+void Unload();
diff --git a/include/openthread-ip6.h b/include/openthread-ip6.h
index 63904d77a..c7fba834e 100644
--- a/include/openthread-ip6.h
+++ b/include/openthread-ip6.h
@@ -138,6 +138,135 @@ ThreadError otSetLinkPromiscuous(otInstance *aInstance, bool aPromiscuous);
*
*/
+/**
+ * Get the list of IPv6 multicast addresses subscribed to the Thread interface.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @returns A pointer to the first Network Interface Multicast Address.
+ */
+const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance);
+
+/**
+ * Subscribe the Thread interface to a Network Interface Multicast Address.
+ *
+ * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only
+ * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aAddress A pointer to an IP Address.
+ *
+ * @retval kThreadErrorNone Successfully subscribed to the Network Interface Multicast Address.
+ * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is invalid address.
+ * @retval kThreadError_NoBufs The Network Interface is already storing the maximum allowed external multicast addresses.
+ */
+ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
+
+/**
+ * Unsubscribe the Thread interface to a Network Interface Multicast Address.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aAddress A pointer to an IP Address.
+ *
+ * @retval kThreadErrorNone Successfully unsubscribed to the Network Interface Multicast Address.
+ * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is an internal address.
+ * @retval kThreadError_NotFound The IP Address indicated by @p aAddress was not found.
+ */
+ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
+
+/**
+ * Check if multicast promiscuous mode is enabled on the Thread interface.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @sa otEnableMulticastPromiscuousMode
+ * @sa otDisableMulticastPromiscuousMode
+ */
+bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance);
+
+/**
+ * Enable multicast promiscuous mode on the Thread interface.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @sa otIsMulticastPromiscuousModeEnabled
+ * @sa otDisableMulticastPromiscuousMode
+ */
+void otEnableMulticastPromiscuousMode(otInstance *aInstance);
+
+/**
+ * Disable multicast promiscuous mode on the Thread interface.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @sa otIsMulticastPromiscuousModeEnabled
+ * @sa otEnableMulticastPromiscuousMode
+ */
+void otDisableMulticastPromiscuousMode(otInstance *aInstance);
+
+/**
+ * This function pointer is called to create IPv6 IID during SLAAC procedure.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
+ * @param[inout] aContext A pointer to creator-specific context.
+ *
+ * @retval kThreadError_None Created valid IID for given IPv6 address.
+ * @retval kThreadError_Ipv6AddressCreationFailure Creation of valid IID for given IPv6 address failed.
+ *
+ */
+typedef ThreadError(*otSlaacIidCreate)(otInstance *aInstance, otNetifAddress *aAddress, void *aContext);
+
+/**
+ * Update all automatically created IPv6 addresses for prefixes from current Network Data with SLAAC procedure.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[inout] aAddresses A pointer to an array of automatically created IPv6 addresses.
+ * @param[in] aNumAddresses The number of slots in aAddresses array.
+ * @param[in] aIidCreate A pointer to a function that is called to create IPv6 IIDs.
+ * @param[in] aContext A pointer to data passed to aIidCreate function.
+ *
+ */
+void otSlaacUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint32_t aNumAddresses,
+ otSlaacIidCreate aIidCreate, void *aContext);
+
+/**
+ * Create random IID for given IPv6 address.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
+ * @param[in] aContext A pointer to unused data.
+ *
+ * @retval kThreadError_None Created valid IID for given IPv6 address.
+ *
+ */
+ThreadError otCreateRandomIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
+
+/**
+ * Create IID for given IPv6 address using extended MAC address.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
+ * @param[in] aContext A pointer to unused data.
+ *
+ * @retval kThreadError_None Created valid IID for given IPv6 address.
+ *
+ */
+ThreadError otCreateMacIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
+
+/**
+ * Create semantically opaque IID for given IPv6 address.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
+ * @param[inout] aContext A pointer to a otSemanticallyOpaqueIidGeneratorData structure.
+ *
+ * @retval kThreadError_None Created valid IID for given IPv6 address.
+ * @retval kThreadError_Ipv6AddressCreationFailure Could not create valid IID for given IPv6 address.
+ *
+ */
+ThreadError otCreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
+
/**
* Allocate a new message buffer for sending an IPv6 message.
*
diff --git a/include/openthread.h b/include/openthread.h
index 60b1c3702..ed0ed46cc 100644
--- a/include/openthread.h
+++ b/include/openthread.h
@@ -858,135 +858,6 @@ OTAPI ThreadError OTCALL otAddUnicastAddress(otInstance *aInstance, const otNeti
*/
OTAPI ThreadError OTCALL otRemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress);
-/**
- * Get the list of IPv6 multicast addresses subscribed to the Thread interface.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- *
- * @returns A pointer to the first Network Interface Multicast Address.
- */
-const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance);
-
-/**
- * Subscribe the Thread interface to a Network Interface Multicast Address.
- *
- * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only
- * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[in] aAddress A pointer to an IP Address.
- *
- * @retval kThreadErrorNone Successfully subscribed to the Network Interface Multicast Address.
- * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is invalid address.
- * @retval kThreadError_NoBufs The Network Interface is already storing the maximum allowed external multicast addresses.
- */
-ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
-
-/**
- * Unsubscribe the Thread interface to a Network Interface Multicast Address.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[in] aAddress A pointer to an IP Address.
- *
- * @retval kThreadErrorNone Successfully unsubscribed to the Network Interface Multicast Address.
- * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is an internal address.
- * @retval kThreadError_NotFound The IP Address indicated by @p aAddress was not found.
- */
-ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
-
-/**
- * Check if multicast promiscuous mode is enabled on the Thread interface.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- *
- * @sa otEnableMulticastPromiscuousMode
- * @sa otDisableMulticastPromiscuousMode
- */
-bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance);
-
-/**
- * Enable multicast promiscuous mode on the Thread interface.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- *
- * @sa otIsMulticastPromiscuousModeEnabled
- * @sa otDisableMulticastPromiscuousMode
- */
-void otEnableMulticastPromiscuousMode(otInstance *aInstance);
-
-/**
- * Disable multicast promiscuous mode on the Thread interface.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- *
- * @sa otIsMulticastPromiscuousModeEnabled
- * @sa otEnableMulticastPromiscuousMode
- */
-void otDisableMulticastPromiscuousMode(otInstance *aInstance);
-
-/**
- * This function pointer is called to create IPv6 IID during SLAAC procedure.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
- * @param[inout] aContext A pointer to creator-specific context.
- *
- * @retval kThreadError_None Created valid IID for given IPv6 address.
- * @retval kThreadError_Ipv6AddressCreationFailure Creation of valid IID for given IPv6 address failed.
- *
- */
-typedef ThreadError(*otSlaacIidCreate)(otInstance *aInstance, otNetifAddress *aAddress, void *aContext);
-
-/**
- * Update all automatically created IPv6 addresses for prefixes from current Network Data with SLAAC procedure.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[inout] aAddresses A pointer to an array of automatically created IPv6 addresses.
- * @param[in] aNumAddresses The number of slots in aAddresses array.
- * @param[in] aIidCreate A pointer to a function that is called to create IPv6 IIDs.
- * @param[in] aContext A pointer to data passed to aIidCreate function.
- *
- */
-void otSlaacUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint32_t aNumAddresses,
- otSlaacIidCreate aIidCreate, void *aContext);
-
-/**
- * Create random IID for given IPv6 address.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
- * @param[in] aContext A pointer to unused data.
- *
- * @retval kThreadError_None Created valid IID for given IPv6 address.
- *
- */
-ThreadError otCreateRandomIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
-
-/**
- * Create IID for given IPv6 address using extended MAC address.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
- * @param[in] aContext A pointer to unused data.
- *
- * @retval kThreadError_None Created valid IID for given IPv6 address.
- *
- */
-ThreadError otCreateMacIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
-
-/**
- * Create semantically opaque IID for given IPv6 address.
- *
- * @param[in] aInstance A pointer to an OpenThread instance.
- * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created.
- * @param[inout] aContext A pointer to a otSemanticallyOpaqueIidGeneratorData structure.
- *
- * @retval kThreadError_None Created valid IID for given IPv6 address.
- * @retval kThreadError_Ipv6AddressCreationFailure Could not create valid IID for given IPv6 address.
- *
- */
-ThreadError otCreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext);
-
/**
* This function pointer is called to notify certain configuration or state changes within OpenThread.
*
@@ -1802,7 +1673,7 @@ OTAPI void OTCALL otPlatformReset(otInstance *aInstance);
*
* @param[in] aInstance A pointer to an OpenThread instance.
*/
-void otFactoryReset(otInstance *aInstance);
+OTAPI void OTCALL otFactoryReset(otInstance *aInstance);
/**
* Get the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Router role.
diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp
index b9ea61193..2eda0fdd8 100644
--- a/src/core/net/ip6_mpl.cpp
+++ b/src/core/net/ip6_mpl.cpp
@@ -164,7 +164,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque
Message *messageCopy = NULL;
MplBufferedMessageMetadata messageMetadata;
uint32_t nextTransmissionTime;
- uint8_t hopLimit;
+ uint8_t hopLimit = 0;
VerifyOrExit(GetTimerExpirations() > 0,);
VerifyOrExit((messageCopy = aMessage.Clone()) != NULL, error = kThreadError_NoBufs);
@@ -172,7 +172,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque
if (!aIsOutbound)
{
aMessage.Read(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit);
- VerifyOrExit(--hopLimit > 0, error = kThreadError_Drop);
+ VerifyOrExit(hopLimit-- > 1, error = kThreadError_Drop);
messageCopy->Write(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit);
}
diff --git a/tests/otTestRunner/App.config b/tests/otTestRunner/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/tests/otTestRunner/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/otTestRunner/AssemblyInfo.cs b/tests/otTestRunner/AssemblyInfo.cs
new file mode 100644
index 000000000..c3f2b7cd3
--- /dev/null
+++ b/tests/otTestRunner/AssemblyInfo.cs
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+ using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("otTestRunner")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("otTestRunner")]
+[assembly: AssemblyCopyright("Copyright © The OpenThread Authors 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d5577e51-fa31-4802-8669-1db32805935e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/otTestRunner/Program.cs b/tests/otTestRunner/Program.cs
new file mode 100644
index 000000000..f1dddce80
--- /dev/null
+++ b/tests/otTestRunner/Program.cs
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace otTestRunner
+{
+ class Program
+ {
+ struct TestResults
+ {
+ public bool Pass;
+ public List Output;
+ public string Error;
+ }
+
+ static string EscapeJson(string data)
+ {
+ return data.Replace("\\", "\\\\").Replace("\"", "\\\"");
+ }
+
+ ///
+ /// Executes an exe with the given args and captures the output
+ ///
+ static async Task ExecuteAsync(string name, string args = null, int timeoutMilliseconds = -1, int instanceIndex = -1)
+ {
+ ProcessStartInfo startInfo = new ProcessStartInfo();
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ startInfo.CreateNoWindow = true;
+ startInfo.UseShellExecute = false;
+ startInfo.RedirectStandardError = true;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.FileName = name;
+ startInfo.Arguments = args;
+
+ startInfo.EnvironmentVariables["NODE_TYPE"] = "win-sim";
+
+ if (instanceIndex != -1)
+ {
+ startInfo.EnvironmentVariables["INSTANCE"] = instanceIndex.ToString();
+ }
+
+ TestResults Results = new TestResults();
+ Results.Pass = false;
+ Results.Output = new List();
+ Results.Error = null;
+ var Errors = new List();
+
+ Results.Output.Add(string.Format("> set NODE_TYPE=win-sim"));
+ Results.Output.Add(string.Format("> {0} {1}", name, args));
+ Results.Output.Add("----------------------------------------------------------------------");
+
+ try
+ {
+ // Execute process
+ using (Process process = Process.Start(startInfo))
+ {
+ process.OutputDataReceived +=
+ (object sender, DataReceivedEventArgs e) => {
+ if (e.Data != null && e.Data.Length > 0)
+ lock (Results.Output) { Results.Output.Add(e.Data); }
+ };
+ process.ErrorDataReceived +=
+ (object sender, DataReceivedEventArgs e) => {
+ if (e.Data != null && e.Data.Length > 0)
+ lock (Results.Output) { Errors.Add(e.Data); }
+ };
+
+ process.BeginErrorReadLine();
+ process.BeginOutputReadLine();
+
+#if DEBUG
+ Console.WriteLine("Starting {0} {1}", name, args);
+#endif
+
+ // Wait for process to complete
+ await Task.Run(
+ () => {
+ if (timeoutMilliseconds == -1)
+ process.WaitForExit();
+ else if (!process.WaitForExit(timeoutMilliseconds))
+ {
+ process.Kill();
+ Results.Output.Add(string.Format("Killed {0} on execution timeout!", name));
+ }
+ });
+
+ // Wait a bit for any output to collect
+ await Task.Delay(1000);
+
+ process.CancelOutputRead();
+ process.CancelErrorRead();
+
+ Results.Output.AddRange(Errors);
+ Results.Pass = Errors.Count > 0 && Errors[Errors.Count - 1] == "OK";
+
+ if (!Results.Pass) Results.Error = EscapeJson(string.Join("\\r\\n", Errors));
+
+ // Make sure the process is killed
+ try { process.Kill(); } catch (Exception) { }
+
+#if DEBUG
+ Console.WriteLine("Completed {0} {1}", name, args);
+#endif
+ }
+ }
+ catch (Exception e)
+ {
+ Results.Output.Add("Encountered exception: " + e.Message);
+ Results.Output.Add(e.StackTrace);
+ }
+
+ return Results;
+ }
+
+ static bool VerboseOutput = false;
+ static int RetiresOnFailure = 0;
+ static bool AppVeyorMode = false;
+ static string AppVeyorApiUrl = null;
+ static string ResultsFolder = "Results_" + DateTime.Now.ToString("yyyyMMdd_HH.mm.ss");
+
+ static void UploadAppVeyorTestResult(string name, bool passed, long durationMS, string error = null)
+ {
+ if (AppVeyorApiUrl == null) return;
+
+ string jsonData = null;
+
+ try
+ {
+ var request = (HttpWebRequest)WebRequest.Create(Path.Combine(AppVeyorApiUrl, "api/tests"));
+
+ jsonData =
+ string.Format(
+ "{{" +
+ "\"testName\": \"{0}\", " +
+ "\"testFramework\": \"MSTest\", " +
+ "\"fileName\": \"{0}.py\", " +
+ "\"outcome\": \"{1}\", " +
+ "\"durationMilliseconds\": \"{2}\", " +
+ "\"ErrorMessage\": \"{3}\"" +
+ "}}",
+ name,
+ passed ? "Passed" : "Failed",
+ durationMS,
+ error == null ? "" : error
+ );
+
+ var data = Encoding.UTF8.GetBytes(jsonData);
+
+ request.Method = "POST";
+ request.ContentType = "application/json";
+ request.ContentLength = data.Length;
+
+ using (var stream = request.GetRequestStream())
+ {
+ stream.Write(data, 0, data.Length);
+ }
+
+ var response = (HttpWebResponse)request.GetResponse();
+ var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Encountered exception for http post:");
+ Console.WriteLine(e.Message);
+ Console.WriteLine(e.StackTrace);
+
+ Console.WriteLine("Json content:");
+ Console.WriteLine(jsonData);
+ }
+ }
+
+ ///
+ /// Runs a python test file and returns success/failure
+ ///
+ static async Task RunTest(string file, int index)
+ {
+ string pythonPath = "python.exe";
+ if (AppVeyorMode)
+ {
+ if (Environment.GetEnvironmentVariable("Platform").ToLower() == "x64")
+ {
+ pythonPath = @"c:\python35-x64\python.exe";
+ }
+ else
+ {
+ pythonPath = @"c:\python35\python.exe";
+ }
+ }
+
+ int tries = 0;
+ Stopwatch Timer;
+ TestResults Results;
+
+ do
+ {
+ Timer = new Stopwatch();
+ Timer.Start();
+
+ Results = await ExecuteAsync(pythonPath, file, 30 * 60 * 1000, index);
+
+ Timer.Stop();
+
+ } while (++tries < RetiresOnFailure + 1 && Results.Pass == false);
+
+ if (VerboseOutput)
+ {
+ lock (ResultsFolder)
+ {
+ foreach (var line in Results.Output)
+ Console.WriteLine(line);
+ }
+ }
+
+ UploadAppVeyorTestResult(Path.GetFileNameWithoutExtension(file), Results.Pass, Timer.ElapsedMilliseconds, Results.Error);
+
+ // Write the output to a file
+ var filePrefix = Results.Pass ? "P_" : "F_";
+ var outputFilePath = Path.Combine(ResultsFolder, filePrefix + Path.GetFileNameWithoutExtension(file) + ".txt");
+ try {
+ File.WriteAllLines(outputFilePath, Results.Output);
+ } catch (Exception e) {
+ Console.WriteLine("Exception while trying to write {0}:\n{1}!", outputFilePath, e.Message);
+ }
+
+ return Results.Pass;
+ }
+
+ ///
+ /// Runs all the tests as indicated by input arguments
+ ///
+ static void Main(string[] args)
+ {
+ if (args.Length < 2)
+ {
+ Console.WriteLine("Usage: otTestRunner.exe [path] [search pattern] (parallel:n) (verbose)");
+ return;
+ }
+
+ var files = Directory.GetFiles(args[0], args[1]);
+ if (files.Length == 0)
+ {
+ Console.WriteLine("No tests found with that path & pattern!");
+ return;
+ }
+
+ var NumberOfTestsToRunInParallel = 1;
+ for (var i = 2; i < args.Length; i++)
+ {
+ if (args[i].StartsWith("parallel:"))
+ NumberOfTestsToRunInParallel = int.Parse(args[i].Substring(9));
+ else if (args[i].StartsWith("retry:"))
+ RetiresOnFailure = int.Parse(args[i].Substring(6));
+ else if (args[i].StartsWith("verbose"))
+ VerboseOutput = true;
+ else if (args[i].StartsWith("appveyor"))
+ {
+ AppVeyorMode = true;
+ AppVeyorApiUrl = Environment.GetEnvironmentVariable("APPVEYOR_API_URL");
+ //Console.WriteLine("AppVeyorApiUrl = {0}", AppVeyorApiUrl);
+ }
+ }
+
+ var CurNumTestsRunning = 0;
+ var ReadyToRunEvent = new ManualResetEvent(true);
+
+ var TestPassCount = 0;
+ Stopwatch Timer = new Stopwatch();
+
+ Directory.CreateDirectory(ResultsFolder);
+ Console.WriteLine("Test results saved: .\\{0}", ResultsFolder);
+
+ Console.WriteLine("Running {0} tests, {1} at a time:", files.Length, NumberOfTestsToRunInParallel);
+ /*for (var i = 0; i < files.Length; i++)
+ Console.WriteLine(Path.GetFileName(files[i]));*/
+ Console.WriteLine("");
+
+ Timer.Start();
+ for (var i = 0; i < files.Length; i++)
+ {
+ // Wait for the event to be set, if not already
+ ReadyToRunEvent.WaitOne();
+
+ if (i != 0)
+ {
+ // Wait a bit to stagger the starts
+ Task.Delay(1000).Wait();
+ }
+
+ lock (ResultsFolder)
+ {
+ if (++CurNumTestsRunning == NumberOfTestsToRunInParallel)
+ ReadyToRunEvent.Reset();
+ }
+
+ var index = i;
+ var fileName = files[i];
+
+ // Start the test, but don't wait for it
+ Task.Run(
+ async () =>
+ {
+ var result = await RunTest(fileName, index);
+ lock (ResultsFolder)
+ {
+ var PrevColor = Console.ForegroundColor;
+ if (result)
+ {
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.Write("PASS");
+ TestPassCount++;
+ }
+ else
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.Write("FAIL");
+ }
+ Console.ForegroundColor = PrevColor;
+ Console.WriteLine(": {0}", Path.GetFileNameWithoutExtension(fileName));
+
+ CurNumTestsRunning--;
+ ReadyToRunEvent.Set();
+ }
+ });
+ }
+
+ // Wait for all the tests to complete
+ while (CurNumTestsRunning != 0)
+ ReadyToRunEvent.WaitOne();
+
+ Timer.Stop();
+ TimeSpan ts = Timer.Elapsed;
+ string elapsedTime =
+ String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
+ ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
+
+ Console.WriteLine("{0} tests run in {1}", files.Length, elapsedTime);
+ Console.WriteLine("{0} passed and {1} failed", TestPassCount, files.Length - TestPassCount);
+
+ if (!AppVeyorMode)
+ Environment.ExitCode = files.Length == TestPassCount ? 0 : 1;
+ }
+ }
+}
diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am
index 4c6e78ee7..984656b2e 100644
--- a/tests/scripts/Makefile.am
+++ b/tests/scripts/Makefile.am
@@ -131,6 +131,7 @@ EXTRA_DIST = \
thread-cert/net_crypto.py \
thread-cert/network_data.py \
thread-cert/node.py \
+ thread-cert/node_cli.py \
thread-cert/sniffer.py \
$(NULL)
diff --git a/tests/scripts/thread-cert/Test_otLwf.py b/tests/scripts/thread-cert/Test_otLwf.py
new file mode 100644
index 000000000..b97ad39a2
--- /dev/null
+++ b/tests/scripts/thread-cert/Test_otLwf.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+#
+# 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.
+#
+
+import unittest
+import ctypes
+
+class GUID(ctypes.Structure):
+ _fields_ = [("Data1", ctypes.c_uint),
+ ("Data2", ctypes.c_ushort),
+ ("Data3", ctypes.c_ushort),
+ ("Data4", ctypes.c_ubyte * 8)]
+
+class otDeviceList(ctypes.Structure):
+ _fields_ = [("aDevicesLength", ctypes.c_ushort),
+ ("aDevices", GUID * 64)]
+
+class Cert_otLwf(unittest.TestCase):
+ def setUp(self):
+
+ # Load the DLL
+ self.Api = ctypes.WinDLL("otApi.dll")
+ if self.Api == None:
+ raise OSError("Failed to load otApi.dll!")
+
+ # Define the functions
+ self.Api.otApiInit.restype = ctypes.c_void_p
+ self.Api.otApiFinalize.argtypes = [ctypes.c_void_p]
+ self.Api.otApiFinalize.restype = None
+ self.Api.otFreeMemory.argtypes = [ctypes.c_void_p]
+ self.Api.otFreeMemory.restype = None
+ self.Api.otEnumerateDevices.argtypes = [ctypes.c_void_p]
+ self.Api.otEnumerateDevices.restype = ctypes.POINTER(otDeviceList)
+
+ def tearDown(self):
+ if self.ApiInstance:
+ self.Api.otApiFinalize(self.ApiInstance)
+
+ def test(self):
+ # Instantiate the API
+ self.ApiInstance = self.Api.otApiInit()
+ # Assert that it didn't return NULL
+ self.assertNotEqual(self.ApiInstance, None)
+ # Query the device list
+ devices = self.Api.otEnumerateDevices(self.ApiInstance)
+ # Assert that it didn't return NULL
+ self.assertNotEqual(devices, None)
+ # Print the number of devices
+ print("devices found: %d" % devices.contents.aDevicesLength)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 4e35aefb6..d5954890f 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -30,568 +30,217 @@
import os
import sys
import time
-import pexpect
+if sys.platform != 'win32':
+ import node_cli
+else:
+ import node_api
import unittest
class Node:
def __init__(self, nodeid):
- self.nodeid = nodeid
- self.verbose = int(float(os.getenv('VERBOSE', 0)))
- self.node_type = os.getenv('NODE_TYPE', 'sim')
-
- if self.node_type == 'soc':
- self.__init_soc(nodeid)
- elif self.node_type == 'ncp-sim':
- self.__init_ncp_sim(nodeid)
+ if sys.platform != 'win32':
+ self.interface = node_cli.otCli(nodeid)
else:
- self.__init_sim(nodeid)
+ self.interface = node_api.otApi(nodeid)
- if self.verbose:
- self.pexpect.logfile_read = sys.stdout
-
- self.clear_whitelist()
- self.disable_whitelist()
- self.set_timeout(100)
-
- def __init_sim(self, nodeid):
- """ Initialize a simulation node. """
- if "OT_CLI_PATH" in os.environ.keys():
- cmd = os.environ['OT_CLI_PATH']
- elif "top_builddir" in os.environ.keys():
- srcdir = os.environ['top_builddir']
- cmd = '%s/examples/apps/cli/ot-cli-ftd' % srcdir
- else:
- cmd = './ot-cli-ftd'
- cmd += ' %d' % nodeid
- print ("%s" % cmd)
-
- self.pexpect = pexpect.spawn(cmd, timeout=4)
-
- # Add delay to ensure that the process is ready to receive commands.
- time.sleep(0.2)
-
-
- def __init_ncp_sim(self, nodeid):
- """ Initialize an NCP simulation node. """
- if "top_builddir" in os.environ.keys():
- builddir = os.environ['top_builddir']
- if "top_srcdir" in os.environ.keys():
- srcdir = os.environ['top_srcdir']
- else:
- srcdir = os.path.dirname(os.path.realpath(__file__))
- srcdir += "/../../.."
- cmd = 'python %s/tools/spinel-cli/spinel-cli.py -p %s/examples/apps/ncp/ot-ncp -n' % (srcdir, builddir)
- else:
- cmd = './ot-ncp'
- cmd += ' %d' % nodeid
- print ("%s" % cmd)
-
- self.pexpect = pexpect.spawn(cmd, timeout=4)
- time.sleep(0.2)
- self.pexpect.expect('spinel-cli >')
- self.debug(int(os.getenv('DEBUG', '0')))
-
- def __init_soc(self, nodeid):
- """ Initialize a System-on-a-chip node connected via UART. """
- import fdpexpect
- serialPort = '/dev/ttyUSB%d' % ((nodeid-1)*2)
- self.pexpect = fdpexpect.fdspawn(os.open(serialPort, os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY))
+ self.interface.clear_whitelist()
+ self.interface.disable_whitelist()
+ self.interface.set_timeout(100)
def __del__(self):
- if self.pexpect.isalive():
- self.send_command('exit')
- self.pexpect.expect(pexpect.EOF)
- self.pexpect.terminate()
- self.pexpect.close(force=True)
+ del self.interface
- def send_command(self, cmd):
- print ("%d: %s" % (self.nodeid, cmd))
- self.pexpect.sendline(cmd)
-
- def get_commands(self):
- self.send_command('?')
- self.pexpect.expect('Commands:')
- commands = []
- while True:
- i = self.pexpect.expect(['Done', '(\S+)'])
- if i != 0:
- commands.append(self.pexpect.match.groups()[0])
- else:
- break
- return commands
-
- def set_mode(self, mode):
- cmd = 'mode ' + mode
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ def set_mode(self, mode):
+ self.interface.set_mode(mode)
def debug(self, level):
- self.send_command('debug '+str(level))
+ self.interface.debug(level)
def interface_up(self):
- self.send_command('ifconfig up')
- self.pexpect.expect('Done')
+ self.interface.interface_up()
def interface_down(self):
- self.send_command('ifconfig down')
- self.pexpect.expect('Done')
+ self.interface.interface_down()
def thread_start(self):
- self.send_command('thread start')
- self.pexpect.expect('Done')
+ self.interface.thread_start()
def thread_stop(self):
- self.send_command('thread stop')
- self.pexpect.expect('Done')
-
+ self.interface.thread_stop()
+
def commissioner_start(self):
- cmd = 'commissioner start'
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.commissioner_start()
def commissioner_add_joiner(self, addr, psk):
- cmd = 'commissioner joiner add ' + addr + ' ' + psk
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.commissioner_add_joiner(addr, psk)
def joiner_start(self, pskd='', provisioning_url=''):
- cmd = 'joiner start ' + pskd + ' ' + provisioning_url
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.joiner_start(pskd, provisioning_url)
def start(self):
- self.interface_up()
- self.thread_start()
+ self.interface.interface_up()
+ self.interface.thread_start()
def stop(self):
- self.thread_stop()
- self.interface_down()
+ self.interface.thread_stop()
+ self.interface.interface_down()
def clear_whitelist(self):
- self.send_command('whitelist clear')
- self.pexpect.expect('Done')
+ self.interface.clear_whitelist()
def enable_whitelist(self):
- self.send_command('whitelist enable')
- self.pexpect.expect('Done')
+ self.interface.enable_whitelist()
def disable_whitelist(self):
- self.send_command('whitelist disable')
- self.pexpect.expect('Done')
+ self.interface.disable_whitelist()
def add_whitelist(self, addr, rssi=None):
- cmd = 'whitelist add ' + addr
- if rssi != None:
- cmd += ' ' + str(rssi)
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.add_whitelist(addr, rssi)
def remove_whitelist(self, addr):
- cmd = 'whitelist remove ' + addr
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.remove_whitelist(addr)
def get_addr16(self):
- self.send_command('rloc16')
- i = self.pexpect.expect('([0-9a-fA-F]{4})')
- if i == 0:
- addr16 = int(self.pexpect.match.groups()[0], 16)
- self.pexpect.expect('Done')
- return addr16
+ return self.interface.get_addr16()
def get_addr64(self):
- self.send_command('extaddr')
- i = self.pexpect.expect('([0-9a-fA-F]{16})')
- if i == 0:
- addr64 = self.pexpect.match.groups()[0].decode("utf-8")
- self.pexpect.expect('Done')
- return addr64
+ return self.interface.get_addr64()
def get_hashmacaddr(self):
- self.send_command('hashmacaddr')
- i = self.pexpect.expect('([0-9a-fA-F]{16})')
- if i == 0:
- addr = self.pexpect.match.groups()[0].decode("utf-8")
- self.pexpect.expect('Done')
- return addr
+ return self.interface.get_hashmacaddr()
def get_channel(self):
- self.send_command('channel')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- channel = int(self.pexpect.match.groups()[0])
- self.pexpect.expect('Done')
- return channel
+ return self.interface.get_channel()
def set_channel(self, channel):
- cmd = 'channel %d' % channel
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_channel(channel)
def get_masterkey(self):
- self.send_command('masterkey')
- i = self.pexpect.expect('([0-9a-fA-F]{32})')
- if i == 0:
- masterkey = self.pexpect.match.groups()[0].decode("utf-8")
- self.pexpect.expect('Done')
- return masterkey
+ return self.interface.get_masterkey()
def set_masterkey(self, masterkey):
- cmd = 'masterkey ' + masterkey
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_masterkey(masterkey)
def get_key_sequence_counter(self):
- self.send_command('keysequence counter')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- key_sequence_counter = int(self.pexpect.match.groups()[0])
- self.pexpect.expect('Done')
- return key_sequence_counter
+ return self.interface.get_key_sequence_counter()
def set_key_sequence_counter(self, key_sequence_counter):
- cmd = 'keysequence counter %d' % key_sequence_counter
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_key_sequence_counter(key_sequence_counter)
def set_key_switch_guardtime(self, key_switch_guardtime):
- cmd = 'keysequence guardtime %d' % key_switch_guardtime
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_key_switch_guardtime(key_switch_guardtime)
def set_network_id_timeout(self, network_id_timeout):
- cmd = 'networkidtimeout %d' % network_id_timeout
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_network_id_timeout(network_id_timeout)
def get_network_name(self):
- self.send_command('networkname')
- while True:
- i = self.pexpect.expect(['Done', '(\S+)'])
- if i != 0:
- network_name = self.pexpect.match.groups()[0].decode('utf-8')
- else:
- break
- return network_name
+ return self.interface.get_network_name()
def set_network_name(self, network_name):
- cmd = 'networkname ' + network_name
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_network_name(network_name)
def get_panid(self):
- self.send_command('panid')
- i = self.pexpect.expect('([0-9a-fA-F]{4})')
- if i == 0:
- panid = int(self.pexpect.match.groups()[0], 16)
- self.pexpect.expect('Done')
- return panid
+ return self.interface.get_panid()
def set_panid(self, panid):
- cmd = 'panid %d' % panid
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_panid(panid)
def get_partition_id(self):
- self.send_command('leaderpartitionid')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- weight = self.pexpect.match.groups()[0]
- self.pexpect.expect('Done')
- return weight
+ return self.interface.get_partition_id()
def set_partition_id(self, partition_id):
- cmd = 'leaderpartitionid %d' % partition_id
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_partition_id(partition_id)
def set_router_upgrade_threshold(self, threshold):
- cmd = 'routerupgradethreshold %d' % threshold
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_router_upgrade_threshold(threshold)
def set_router_downgrade_threshold(self, threshold):
- cmd = 'routerdowngradethreshold %d' % threshold
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_router_downgrade_threshold(threshold)
def release_router_id(self, router_id):
- cmd = 'releaserouterid %d' % router_id
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.release_router_id(router_id)
def get_state(self):
- states = ['detached', 'child', 'router', 'leader']
- self.send_command('state')
- match = self.pexpect.expect(states)
- self.pexpect.expect('Done')
- return states[match]
+ return self.interface.get_state()
def set_state(self, state):
- cmd = 'state ' + state
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_state(state)
def get_timeout(self):
- self.send_command('childtimeout')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- timeout = self.pexpect.match.groups()[0]
- self.pexpect.expect('Done')
- return timeout
+ return self.interface.get_timeout()
def set_timeout(self, timeout):
- cmd = 'childtimeout %d' % timeout
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_timeout(timeout)
def set_max_children(self, number):
- cmd = 'childmax %d' % number
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_max_children(number)
def get_weight(self):
- self.send_command('leaderweight')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- weight = self.pexpect.match.groups()[0]
- self.pexpect.expect('Done')
- return weight
+ return self.interface.get_weight()
def set_weight(self, weight):
- cmd = 'leaderweight %d' % weight
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_weight(weight)
def add_ipaddr(self, ipaddr):
- cmd = 'ipaddr add ' + ipaddr
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.add_ipaddr(ipaddr)
def get_addrs(self):
- addrs = []
- self.send_command('ipaddr')
-
- while True:
- i = self.pexpect.expect(['(\S+:\S+)\r\n', 'Done'])
- if i == 0:
- addrs.append(self.pexpect.match.groups()[0].decode("utf-8"))
- elif i == 1:
- break
-
- return addrs
+ return self.interface.get_addrs()
def get_context_reuse_delay(self):
- self.send_command('contextreusedelay')
- i = self.pexpect.expect('(\d+)\r\n')
- if i == 0:
- timeout = self.pexpect.match.groups()[0]
- self.pexpect.expect('Done')
- return timeout
+ return self.interface.get_context_reuse_delay()
def set_context_reuse_delay(self, delay):
- cmd = 'contextreusedelay %d' % delay
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_context_reuse_delay(delay)
def add_prefix(self, prefix, flags, prf = 'med'):
- cmd = 'prefix add ' + prefix + ' ' + flags + ' ' + prf
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.add_prefix(prefix, flags, prf)
def remove_prefix(self, prefix):
- cmd = ' prefix remove ' + prefix
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.remove_prefix(prefix)
def add_route(self, prefix, prf = 'med'):
- cmd = 'route add ' + prefix + ' ' + prf
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.add_route(prefix, prf)
def remove_route(self, prefix):
- cmd = 'route remove ' + prefix
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.remove_route(prefix)
def register_netdata(self):
- self.send_command('netdataregister')
- self.pexpect.expect('Done')
+ self.interface.register_netdata()
def energy_scan(self, mask, count, period, scan_duration, ipaddr):
- cmd = 'commissioner energy ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + str(scan_duration) + ' ' + ipaddr
- self.send_command(cmd)
- self.pexpect.expect('Energy:', timeout=8)
+ self.interface.energy_scan(mask, count, period, scan_duration, ipaddr)
def panid_query(self, panid, mask, ipaddr):
- cmd = 'commissioner panid ' + str(panid) + ' ' + str(mask) + ' ' + ipaddr
- self.send_command(cmd)
- self.pexpect.expect('Conflict:', timeout=8)
+ self.interface.panid_query(panid, mask, ipaddr)
def scan(self):
- self.send_command('scan')
-
- results = []
- while True:
- i = self.pexpect.expect(['\|\s(\S+)\s+\|\s(\S+)\s+\|\s([0-9a-fA-F]{4})\s\|\s([0-9a-fA-F]{16})\s\|\s(\d+)\r\n',
- 'Done'])
- if i == 0:
- results.append(self.pexpect.match.groups())
- else:
- break
-
- return results
+ return self.interface.scan()
def ping(self, ipaddr, num_responses=1, size=None):
- cmd = 'ping ' + ipaddr
- if size != None:
- cmd += ' ' + str(size)
-
- self.send_command(cmd)
-
- result = True
- try:
- responders = {}
- while len(responders) < num_responses:
- i = self.pexpect.expect(['from (\S+):'])
- if i == 0:
- responders[self.pexpect.match.groups()[0]] = 1
- self.pexpect.expect('\n')
- except pexpect.TIMEOUT:
- result = False
-
- return result
+ return self.interface.ping(ipaddr, num_responses, size)
def set_router_selection_jitter(self, jitter):
- cmd = 'routerselectionjitter %d' % jitter
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.set_router_selection_jitter(jitter)
def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None):
- self.send_command('dataset clear')
- self.pexpect.expect('Done')
-
- cmd = 'dataset activetimestamp %d' % timestamp
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if panid != None:
- cmd = 'dataset panid %d' % panid
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if channel != None:
- cmd = 'dataset channel %d' % channel
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if channel_mask != None:
- cmd = 'dataset channelmask %d' % channel_mask
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if master_key != None:
- cmd = 'dataset masterkey ' + master_key
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- self.send_command('dataset commit active')
- self.pexpect.expect('Done')
+ self.interface.set_active_dataset(timestamp, panid, channel, channel_mask, master_key)
def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None):
- self.send_command('dataset clear')
- self.pexpect.expect('Done')
-
- cmd = 'dataset pendingtimestamp %d' % pendingtimestamp
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- cmd = 'dataset activetimestamp %d' % activetimestamp
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if panid != None:
- cmd = 'dataset panid %d' % panid
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- if channel != None:
- cmd = 'dataset channel %d' % channel
- self.send_command(cmd)
- self.pexpect.expect('Done')
-
- self.send_command('dataset commit pending')
- self.pexpect.expect('Done')
+ self.interface.set_pending_dataset(pendingtimestamp, activetimestamp, panid, channel)
def announce_begin(self, mask, count, period, ipaddr):
- cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.announce_begin(mask, count, period, ipaddr)
def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None,
panid=None, master_key=None, mesh_local=None, network_name=None, binary=None):
- cmd = 'dataset mgmtsetcommand active '
-
- if active_timestamp != None:
- cmd += 'activetimestamp %d ' % active_timestamp
-
- if channel != None:
- cmd += 'channel %d ' % channel
-
- if channel_mask != None:
- cmd += 'channelmask %d ' % channel_mask
-
- if extended_panid != None:
- cmd += 'extpanid ' + extended_panid + ' '
-
- if panid != None:
- cmd += 'panid %d ' % panid
-
- if master_key != None:
- cmd += 'masterkey ' + master_key + ' '
-
- if mesh_local != None:
- cmd += 'localprefix ' + mesh_local + ' '
-
- if network_name != None:
- cmd += 'networkname ' + network_name + ' '
-
- if binary != None:
- cmd += 'binary ' + binary + ' '
-
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.send_mgmt_active_set(active_timestamp, channel, channel_mask, extended_panid, panid,
+ master_key, mesh_local, network_name, binary)
def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None,
panid=None, master_key=None, mesh_local=None, network_name=None):
- cmd = 'dataset mgmtsetcommand pending '
-
- if pending_timestamp != None:
- cmd += 'pendingtimestamp %d ' % pending_timestamp
-
- if active_timestamp != None:
- cmd += 'activetimestamp %d ' % active_timestamp
-
- if delay_timer != None:
- cmd += 'delaytimer %d ' % delay_timer
-
- if channel != None:
- cmd += 'channel %d ' % channel
-
- if panid != None:
- cmd += 'panid %d ' % panid
-
- if master_key != None:
- cmd += 'masterkey ' + master_key + ' '
-
- if mesh_local != None:
- cmd += 'localprefix ' + mesh_local + ' '
-
- if network_name != None:
- cmd += 'networkname ' + network_name + ' '
-
- self.send_command(cmd)
- self.pexpect.expect('Done')
+ self.interface.send_mgmt_pending_set(pending_timestamp, active_timestamp, delay_timer, channel, panid,
+ master_key, mesh_local, network_name)
if __name__ == '__main__':
unittest.main()
diff --git a/tests/scripts/thread-cert/node_api.py b/tests/scripts/thread-cert/node_api.py
new file mode 100644
index 000000000..298403725
--- /dev/null
+++ b/tests/scripts/thread-cert/node_api.py
@@ -0,0 +1,588 @@
+#!/usr/bin/python
+#
+# 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.
+#
+
+import os
+import sys
+import time
+import ctypes
+
+class otApi:
+ def __init__(self, nodeid):
+ self.verbose = int(float(os.getenv('VERBOSE', 0)))
+ self.__init_dll(nodeid)
+
+ def __del__(self):
+ self.Api.otNodeFinalize(self.otNode)
+
+ def set_mode(self, mode):
+ if self.Api.otNodeSetMode(self.otNode, mode.encode('utf-8')) != 0:
+ raise OSError("otNodeSetMode failed!")
+
+ def interface_up(self):
+ if self.Api.otNodeInterfaceUp(self.otNode) != 0:
+ raise OSError("otNodeInterfaceUp failed!")
+
+ def interface_down(self):
+ if self.Api.otNodeInterfaceDown(self.otNode) != 0:
+ raise OSError("otNodeInterfaceDown failed!")
+
+ def thread_start(self):
+ if self.Api.otNodeThreadStart(self.otNode) != 0:
+ raise OSError("otNodeThreadStart failed!")
+
+ def thread_stop(self):
+ if self.Api.otNodeThreadStop(self.otNode) != 0:
+ raise OSError("otNodeThreadStop failed!")
+
+ def commissioner_start(self):
+ if self.Api.otNodeCommissionerStart(self.otNode) != 0:
+ raise OSError("otNodeCommissionerStart failed!")
+
+ def commissioner_add_joiner(self, addr, psk):
+ if self.Api.otNodeCommissionerJoinerAdd(self.otNode, addr.encode('utf-8'), psk.encode('utf-8')) != 0:
+ raise OSError("otNodeCommissionerJoinerAdd failed!")
+
+ def joiner_start(self, pskd='', provisioning_url=''):
+ if self.Api.otNodeJoinerStart(self.otNode, pskd.encode('utf-8'), provisioning_url.encode('utf-8')) != 0:
+ raise OSError("otNodeJoinerStart failed!")
+
+ def clear_whitelist(self):
+ if self.Api.otNodeClearWhitelist(self.otNode) != 0:
+ raise OSError("otNodeClearWhitelist failed!")
+
+ def enable_whitelist(self):
+ if self.Api.otNodeEnableWhitelist(self.otNode) != 0:
+ raise OSError("otNodeEnableWhitelist failed!")
+
+ def disable_whitelist(self):
+ if self.Api.otNodeDisableWhitelist(self.otNode) != 0:
+ raise OSError("otNodeDisableWhitelist failed!")
+
+ def add_whitelist(self, addr, rssi=None):
+ if rssi == None:
+ rssi = 0
+ if self.Api.otNodeAddWhitelist(self.otNode, addr.encode('utf-8'), ctypes.c_byte(rssi)) != 0:
+ raise OSError("otNodeAddWhitelist failed!")
+
+ def remove_whitelist(self, addr):
+ if self.Api.otNodeRemoveWhitelist(self.otNode, addr.encode('utf-8')) != 0:
+ raise OSError("otNodeRemoveWhitelist failed!")
+
+ def get_addr16(self):
+ return self.Api.otNodeGetAddr16(self.otNode)
+
+ def get_addr64(self):
+ return self.Api.otNodeGetAddr64(self.otNode).decode('utf-8')
+
+ def get_hashmacaddr(self):
+ return self.Api.otNodeGetHashMacAddress(self.otNode).decode('utf-8')
+
+ def get_channel(self):
+ return self.Api.otNodeGetChannel(self.otNode)
+
+ def set_channel(self, channel):
+ if self.Api.otNodeSetChannel(self.otNode, ctypes.c_ubyte(channel)) != 0:
+ raise OSError("otNodeSetChannel failed!")
+
+ def get_masterkey(self):
+ return self.Api.otNodeGetMasterkey(self.otNode).decode("utf-8")
+
+ def set_masterkey(self, masterkey):
+ if self.Api.otNodeSetMasterkey(self.otNode, masterkey.encode('utf-8')) != 0:
+ raise OSError("otNodeSetMasterkey failed!")
+
+ def get_key_sequence_counter(self):
+ return self.Api.otNodeGetKeySequenceCounter(self.otNode)
+
+ def set_key_sequence_counter(self, key_sequence_counter):
+ if self.Api.otNodeSetKeySequenceCounter(self.otNode, ctypes.c_uint(key_sequence_counter)) != 0:
+ raise OSError("otNodeSetKeySequenceCounter failed!")
+
+ def set_key_switch_guardtime(self, key_switch_guardtime):
+ if self.Api.otNodeSetKeySwitchGuardTime(self.otNode, ctypes.c_uint(key_switch_guardtime)) != 0:
+ raise OSError("otNodeSetKeySwitchGuardTime failed!")
+
+ def set_network_id_timeout(self, network_id_timeout):
+ if self.Api.otNodeSetNetworkIdTimeout(self.otNode, ctypes.c_ubyte(network_id_timeout)) != 0:
+ raise OSError("otNodeSetNetworkIdTimeout failed!")
+
+ def get_network_name(self):
+ return self.Api.otNodeGetNetworkName(self.otNode).decode("utf-8")
+
+ def set_network_name(self, network_name):
+ if self.Api.otNodeSetNetworkName(self.otNode, network_name.encode('utf-8')) != 0:
+ raise OSError("otNodeSetNetworkName failed!")
+
+ def get_panid(self):
+ return int(self.Api.otNodeGetPanId(self.otNode))
+
+ def set_panid(self, panid):
+ if self.Api.otNodeSetPanId(self.otNode, ctypes.c_ushort(panid)) != 0:
+ raise OSError("otNodeSetPanId failed!")
+
+ def get_partition_id(self):
+ return int(self.Api.otNodeGetPartitionId(self.otNode))
+
+ def set_partition_id(self, partition_id):
+ if self.Api.otNodeSetPartitionId(self.otNode, ctypes.c_uint(partition_id)) != 0:
+ raise OSError("otNodeSetPartitionId failed!")
+
+ def set_router_upgrade_threshold(self, threshold):
+ if self.Api.otNodeSetRouterUpgradeThreshold(self.otNode, ctypes.c_ubyte(threshold)) != 0:
+ raise OSError("otNodeSetRouterUpgradeThreshold failed!")
+
+ def set_router_downgrade_threshold(self, threshold):
+ if self.Api.otNodeSetRouterDowngradeThreshold(self.otNode, ctypes.c_ubyte(threshold)) != 0:
+ raise OSError("otNodeSetRouterDowngradeThreshold failed!")
+
+ def release_router_id(self, router_id):
+ if self.Api.otNodeReleaseRouterId(self.otNode, ctypes.c_ubyte(router_id)) != 0:
+ raise OSError("otNodeReleaseRouterId failed!")
+
+ def get_state(self):
+ return self.Api.otNodeGetState(self.otNode).decode('utf-8')
+
+ def set_state(self, state):
+ if self.Api.otNodeSetState(self.otNode, state.encode('utf-8')) != 0:
+ raise OSError("otNodeSetState failed!")
+
+ def get_timeout(self):
+ return int(self.Api.otNodeGetTimeout(self.otNode))
+
+ def set_timeout(self, timeout):
+ if self.Api.otNodeSetTimeout(self.otNode, ctypes.c_uint(timeout)) != 0:
+ raise OSError("otNodeSetTimeout failed!")
+
+ def set_max_children(self, number):
+ if self.Api.otNodeSetMaxChildren(self.otNode, ctypes.c_ubyte(number)) != 0:
+ raise OSError("otNodeSetMaxChildren failed!")
+
+ def get_weight(self):
+ return int(self.Api.otNodeGetWeight(self.otNode))
+
+ def set_weight(self, weight):
+ if self.Api.otNodeSetWeight(self.otNode, ctypes.c_ubyte(weight)) != 0:
+ raise OSError("otNodeSetWeight failed!")
+
+ def add_ipaddr(self, ipaddr):
+ if self.Api.otNodeAddIpAddr(self.otNode, ipaddr.encode('utf-8')) != 0:
+ raise OSError("otNodeAddIpAddr failed!")
+
+ def get_addrs(self):
+ return self.Api.otNodeGetAddrs(self.otNode).decode("utf-8").split("\n")
+
+ def get_context_reuse_delay(self):
+ return int(self.Api.otNodeGetContextReuseDelay(self.otNode))
+
+ def set_context_reuse_delay(self, delay):
+ if self.Api.otNodeSetContextReuseDelay(self.otNode, ctypes.c_uint(delay)) != 0:
+ raise OSError("otNodeSetContextReuseDelay failed!")
+
+ def add_prefix(self, prefix, flags, prf = 'med'):
+ if self.Api.otNodeAddPrefix(self.otNode, prefix.encode('utf-8'), flags.encode('utf-8'), prf.encode('utf-8')) != 0:
+ raise OSError("otNodeAddPrefix failed!")
+
+ def remove_prefix(self, prefix):
+ if self.Api.otNodeRemovePrefix(self.otNode, prefix.encode('utf-8')) != 0:
+ raise OSError("otNodeRemovePrefix failed!")
+
+ def add_route(self, prefix, prf = 'med'):
+ if self.Api.otNodeAddRoute(self.otNode, prefix.encode('utf-8'), prf.encode('utf-8')) != 0:
+ raise OSError("otNodeAddRoute failed!")
+
+ def remove_route(self, prefix):
+ if self.Api.otNodeRemoveRoute(self.otNode, prefix.encode('utf-8')) != 0:
+ raise OSError("otNodeRemovePrefix failed!")
+
+ def register_netdata(self):
+ if self.Api.otNodeRegisterNetdata(self.otNode) != 0:
+ raise OSError("otNodeRegisterNetdata failed!")
+
+ def energy_scan(self, mask, count, period, scan_duration, ipaddr):
+ if self.Api.otNodeEnergyScan(self.otNode, ctypes.c_uint(mask), ctypes.c_ubyte(count), ctypes.c_ushort(period), ctypes.c_ushort(scan_duration), ipaddr.encode('utf-8')) != 0:
+ raise OSError("otNodeEnergyScan failed!")
+
+ def panid_query(self, panid, mask, ipaddr):
+ if self.Api.otNodePanIdQuery(self.otNode, ctypes.c_ushort(panid), ctypes.c_uint(mask), ipaddr.encode('utf-8')) != 0:
+ raise OSError("otNodePanIdQuery failed!")
+
+ def scan(self):
+ return self.Api.otNodeScan(self.otNode).decode("utf-8").split("\n")
+
+ def ping(self, ipaddr, num_responses=1, size=None):
+ if size == None:
+ size = 100
+ numberOfResponders = self.Api.otNodePing(self.otNode, ipaddr.encode('utf-8'), ctypes.c_ushort(size), ctypes.c_uint(num_responses))
+ return numberOfResponders >= num_responses
+
+ def set_router_selection_jitter(self, jitter):
+ if self.Api.otNodeSetRouterSelectionJitter(self.otNode, ctypes.c_ubyte(jitter)) != 0:
+ raise OSError("otNodeSetRouterSelectionJitter failed!")
+
+ def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None):
+ if panid == None:
+ panid = 0
+ if channel == None:
+ channel = 0
+ if channel_mask == None:
+ channel_mask = 0
+ if master_key == None:
+ master_key = ""
+ if self.Api.otNodeSetActiveDataset(
+ self.otNode,
+ ctypes.c_ulonglong(timestamp),
+ ctypes.c_ushort(panid),
+ ctypes.c_ushort(channel),
+ ctypes.c_uint(channel_mask),
+ master_key.encode('utf-8')
+ ) != 0:
+ raise OSError("otNodeSetActiveDataset failed!")
+
+ def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None):
+ if pendingtimestamp == None:
+ pendingtimestamp = 0
+ if activetimestamp == None:
+ activetimestamp = 0
+ if panid == None:
+ panid = 0
+ if channel == None:
+ channel = 0
+ if self.Api.otNodeSetPendingDataset(
+ self.otNode,
+ ctypes.c_ulonglong(activetimestamp),
+ ctypes.c_ulonglong(pendingtimestamp),
+ ctypes.c_ushort(panid),
+ ctypes.c_ushort(channel)
+ ) != 0:
+ raise OSError("otNodeSetPendingDataset failed!")
+
+ def announce_begin(self, mask, count, period, ipaddr):
+ if self.Api.otNodeCommissionerAnnounceBegin(self.otNode, ctypes.c_uint(mask), ctypes.c_ubyte(count), ctypes.c_ushort(period), ipaddr.encode('utf-8')) != 0:
+ raise OSError("otNodeCommissionerAnnounceBegin failed!")
+
+ def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None,
+ panid=None, master_key=None, mesh_local=None, network_name=None, binary=None):
+ if active_timestamp == None:
+ active_timestamp = 0
+ if panid == None:
+ panid = 0
+ if channel == None:
+ channel = 0
+ if channel_mask == None:
+ channel_mask = 0
+ if extended_panid == None:
+ extended_panid = ""
+ if master_key == None:
+ master_key = ""
+ if mesh_local == None:
+ mesh_local = ""
+ if network_name == None:
+ network_name = ""
+ if binary == None:
+ binary = ""
+ if self.Api.otNodeSendActiveSet(
+ self.otNode,
+ ctypes.c_ulonglong(active_timestamp),
+ ctypes.c_ushort(panid),
+ ctypes.c_ushort(channel),
+ ctypes.c_uint(channel_mask),
+ extended_panid.encode('utf-8'),
+ master_key.encode('utf-8'),
+ mesh_local.encode('utf-8'),
+ network_name.encode('utf-8'),
+ binary.encode('utf-8')
+ ) != 0:
+ raise OSError("otNodeSendActiveSet failed!")
+
+ def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None,
+ panid=None, master_key=None, mesh_local=None, network_name=None):
+ if pending_timestamp == None:
+ pending_timestamp = 0
+ if active_timestamp == None:
+ active_timestamp = 0
+ if delay_timer == None:
+ delay_timer = 0
+ if panid == None:
+ panid = 0
+ if channel == None:
+ channel = 0
+ if master_key == None:
+ master_key = ""
+ if mesh_local == None:
+ mesh_local = ""
+ if network_name == None:
+ network_name = ""
+ if self.Api.otNodeSendPendingSet(
+ self.otNode,
+ ctypes.c_ulonglong(active_timestamp),
+ ctypes.c_ulonglong(pending_timestamp),
+ ctypes.c_uint(delay_timer),
+ ctypes.c_ushort(panid),
+ ctypes.c_ushort(channel),
+ master_key.encode('utf-8'),
+ mesh_local.encode('utf-8'),
+ network_name.encode('utf-8')
+ ) != 0:
+ raise OSError("otNodeSendPendingSet failed!")
+
+ def log(self, message):
+ self.Api.otNodeLog(message)
+
+ def __init_dll(self, nodeid):
+ """ Initialize the API from a Windows DLL. """
+
+ # Load the DLL
+ self.Api = ctypes.WinDLL("otnodeapi.dll")
+ if self.Api == None:
+ raise OSError("Failed to load otnodeapi.dll!")
+
+ # Define the functions
+ self.Api.otNodeLog.argtypes = [ctypes.c_char_p]
+
+ self.Api.otNodeInit.argtypes = [ctypes.c_uint]
+ self.Api.otNodeInit.restype = ctypes.c_void_p
+
+ self.Api.otNodeFinalize.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeSetMode.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeInterfaceUp.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeInterfaceDown.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeThreadStart.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeThreadStop.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeCommissionerStart.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeCommissionerJoinerAdd.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeCommissionerStop.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeJoinerStart.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeJoinerStop.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeClearWhitelist.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeEnableWhitelist.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeDisableWhitelist.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeAddWhitelist.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_byte]
+
+ self.Api.otNodeRemoveWhitelist.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeGetAddr16.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetAddr16.restype = ctypes.c_ushort
+
+ self.Api.otNodeGetAddr64.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetAddr64.restype = ctypes.c_char_p
+
+ self.Api.otNodeGetHashMacAddress.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetHashMacAddress.restype = ctypes.c_char_p
+
+ self.Api.otNodeSetChannel.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeGetChannel.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetChannel.restype = ctypes.c_ubyte
+
+ self.Api.otNodeSetMasterkey.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeGetMasterkey.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetMasterkey.restype = ctypes.c_char_p
+
+ self.Api.otNodeGetKeySequenceCounter.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetKeySequenceCounter.restype = ctypes.c_uint
+
+ self.Api.otNodeSetKeySequenceCounter.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint]
+
+ self.Api.otNodeSetKeySwitchGuardTime.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint]
+
+ self.Api.otNodeSetNetworkIdTimeout.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeGetNetworkName.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetNetworkName.restype = ctypes.c_char_p
+
+ self.Api.otNodeSetNetworkName.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeGetPanId.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetPanId.restype = ctypes.c_ushort
+
+ self.Api.otNodeSetPanId.argtypes = [ctypes.c_void_p,
+ ctypes.c_ushort]
+
+ self.Api.otNodeGetPartitionId.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetPartitionId.restype = ctypes.c_uint
+
+ self.Api.otNodeSetPartitionId.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint]
+
+ self.Api.otNodeSetRouterUpgradeThreshold.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeSetRouterDowngradeThreshold.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeReleaseRouterId.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeGetState.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetState.restype = ctypes.c_char_p
+
+ self.Api.otNodeSetState.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeGetTimeout.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetTimeout.restype = ctypes.c_uint
+
+ self.Api.otNodeSetTimeout.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint]
+
+ self.Api.otNodeGetWeight.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetWeight.restype = ctypes.c_ubyte
+
+ self.Api.otNodeSetWeight.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeAddIpAddr.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeGetAddrs.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetAddrs.restype = ctypes.c_char_p
+
+ self.Api.otNodeGetContextReuseDelay.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeGetContextReuseDelay.restype = ctypes.c_uint
+
+ self.Api.otNodeSetContextReuseDelay.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint]
+
+ self.Api.otNodeAddPrefix.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeRemovePrefix.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeAddRoute.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeRemoveRoute.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeRegisterNetdata.argtypes = [ctypes.c_void_p]
+
+ self.Api.otNodeEnergyScan.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint,
+ ctypes.c_ubyte,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_char_p]
+
+ self.Api.otNodePanIdQuery.argtypes = [ctypes.c_void_p,
+ ctypes.c_ushort,
+ ctypes.c_uint,
+ ctypes.c_char_p]
+
+ self.Api.otNodeScan.argtypes = [ctypes.c_void_p]
+ self.Api.otNodeScan.restype = ctypes.c_char_p
+
+ self.Api.otNodePing.argtypes = [ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_ushort,
+ ctypes.c_uint]
+ self.Api.otNodePing.restype = ctypes.c_uint
+
+ self.Api.otNodeSetRouterSelectionJitter.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ self.Api.otNodeCommissionerAnnounceBegin.argtypes = [ctypes.c_void_p,
+ ctypes.c_uint,
+ ctypes.c_ubyte,
+ ctypes.c_ushort,
+ ctypes.c_char_p]
+
+ self.Api.otNodeSetActiveDataset.argtypes = [ctypes.c_void_p,
+ ctypes.c_ulonglong,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_uint,
+ ctypes.c_char_p]
+
+ self.Api.otNodeSetPendingDataset.argtypes = [ctypes.c_void_p,
+ ctypes.c_ulonglong,
+ ctypes.c_ulonglong,
+ ctypes.c_ushort,
+ ctypes.c_ushort]
+
+ self.Api.otNodeSendPendingSet.argtypes = [ctypes.c_void_p,
+ ctypes.c_ulonglong,
+ ctypes.c_ulonglong,
+ ctypes.c_uint,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeSendActiveSet.argtypes = [ctypes.c_void_p,
+ ctypes.c_ulonglong,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_uint,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p]
+
+ self.Api.otNodeSetMaxChildren.argtypes = [ctypes.c_void_p,
+ ctypes.c_ubyte]
+
+ # Initialize a new node
+ self.otNode = self.Api.otNodeInit(ctypes.c_uint(nodeid))
+ if self.otNode == None:
+ raise OSError("otNodeInit failed!")
diff --git a/tests/scripts/thread-cert/node_cli.py b/tests/scripts/thread-cert/node_cli.py
new file mode 100644
index 000000000..126c9f59f
--- /dev/null
+++ b/tests/scripts/thread-cert/node_cli.py
@@ -0,0 +1,581 @@
+#!/usr/bin/python
+#
+# 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.
+#
+
+import os
+import sys
+import time
+import pexpect
+
+class otCli:
+ def __init__(self, nodeid):
+ self.nodeid = nodeid
+ self.verbose = int(float(os.getenv('VERBOSE', 0)))
+ self.node_type = os.getenv('NODE_TYPE', 'sim')
+
+ if self.node_type == 'soc':
+ self.__init_soc(nodeid)
+ elif self.node_type == 'ncp-sim':
+ self.__init_ncp_sim(nodeid)
+ else:
+ self.__init_sim(nodeid)
+
+ if self.verbose:
+ self.pexpect.logfile_read = sys.stdout
+
+ def __init_sim(self, nodeid):
+ """ Initialize a simulation node. """
+ if "OT_CLI_PATH" in os.environ.keys():
+ cmd = os.environ['OT_CLI_PATH']
+ elif "top_builddir" in os.environ.keys():
+ srcdir = os.environ['top_builddir']
+ cmd = '%s/examples/apps/cli/ot-cli-ftd' % srcdir
+ else:
+ cmd = './ot-cli-ftd'
+ cmd += ' %d' % nodeid
+ print ("%s" % cmd)
+
+ self.pexpect = pexpect.spawn(cmd, timeout=4)
+
+ # Add delay to ensure that the process is ready to receive commands.
+ time.sleep(0.2)
+
+
+ def __init_ncp_sim(self, nodeid):
+ """ Initialize an NCP simulation node. """
+ if "top_builddir" in os.environ.keys():
+ builddir = os.environ['top_builddir']
+ if "top_srcdir" in os.environ.keys():
+ srcdir = os.environ['top_srcdir']
+ else:
+ srcdir = os.path.dirname(os.path.realpath(__file__))
+ srcdir += "/../../.."
+ cmd = 'python %s/tools/spinel-cli/spinel-cli.py -p %s/examples/apps/ncp/ot-ncp -n' % (srcdir, builddir)
+ else:
+ cmd = './ot-ncp'
+ cmd += ' %d' % nodeid
+ print ("%s" % cmd)
+
+ self.pexpect = pexpect.spawn(cmd, timeout=4)
+ time.sleep(0.2)
+ self.pexpect.expect('spinel-cli >')
+ self.debug(int(os.getenv('DEBUG', '0')))
+
+ def __init_soc(self, nodeid):
+ """ Initialize a System-on-a-chip node connected via UART. """
+ import fdpexpect
+ serialPort = '/dev/ttyUSB%d' % ((nodeid-1)*2)
+ self.pexpect = fdpexpect.fdspawn(os.open(serialPort, os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY))
+
+ def __del__(self):
+ if self.pexpect.isalive():
+ self.send_command('exit')
+ self.pexpect.expect(pexpect.EOF)
+ self.pexpect.terminate()
+ self.pexpect.close(force=True)
+
+ def send_command(self, cmd):
+ print ("%d: %s" % (self.nodeid, cmd))
+ self.pexpect.sendline(cmd)
+
+ def get_commands(self):
+ self.send_command('?')
+ self.pexpect.expect('Commands:')
+ commands = []
+ while True:
+ i = self.pexpect.expect(['Done', '(\S+)'])
+ if i != 0:
+ commands.append(self.pexpect.match.groups()[0])
+ else:
+ break
+ return commands
+
+ def set_mode(self, mode):
+ cmd = 'mode ' + mode
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def debug(self, level):
+ self.send_command('debug '+str(level))
+
+ def interface_up(self):
+ self.send_command('ifconfig up')
+ self.pexpect.expect('Done')
+
+ def interface_down(self):
+ self.send_command('ifconfig down')
+ self.pexpect.expect('Done')
+
+ def thread_start(self):
+ self.send_command('thread start')
+ self.pexpect.expect('Done')
+
+ def thread_stop(self):
+ self.send_command('thread stop')
+ self.pexpect.expect('Done')
+
+ def commissioner_start(self):
+ cmd = 'commissioner start'
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def commissioner_add_joiner(self, addr, psk):
+ cmd = 'commissioner joiner add ' + addr + ' ' + psk
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def joiner_start(self, pskd='', provisioning_url=''):
+ cmd = 'joiner start ' + pskd + ' ' + provisioning_url
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def clear_whitelist(self):
+ self.send_command('whitelist clear')
+ self.pexpect.expect('Done')
+
+ def enable_whitelist(self):
+ self.send_command('whitelist enable')
+ self.pexpect.expect('Done')
+
+ def disable_whitelist(self):
+ self.send_command('whitelist disable')
+ self.pexpect.expect('Done')
+
+ def add_whitelist(self, addr, rssi=None):
+ cmd = 'whitelist add ' + addr
+ if rssi != None:
+ cmd += ' ' + str(rssi)
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def remove_whitelist(self, addr):
+ cmd = 'whitelist remove ' + addr
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_addr16(self):
+ self.send_command('rloc16')
+ i = self.pexpect.expect('([0-9a-fA-F]{4})')
+ if i == 0:
+ addr16 = int(self.pexpect.match.groups()[0], 16)
+ self.pexpect.expect('Done')
+ return addr16
+
+ def get_addr64(self):
+ self.send_command('extaddr')
+ i = self.pexpect.expect('([0-9a-fA-F]{16})')
+ if i == 0:
+ addr64 = self.pexpect.match.groups()[0].decode("utf-8")
+ self.pexpect.expect('Done')
+ return addr64
+
+ def get_hashmacaddr(self):
+ self.send_command('hashmacaddr')
+ i = self.pexpect.expect('([0-9a-fA-F]{16})')
+ if i == 0:
+ addr = self.pexpect.match.groups()[0].decode("utf-8")
+ self.pexpect.expect('Done')
+ return addr
+
+ def get_channel(self):
+ self.send_command('channel')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ channel = int(self.pexpect.match.groups()[0])
+ self.pexpect.expect('Done')
+ return channel
+
+ def set_channel(self, channel):
+ cmd = 'channel %d' % channel
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_masterkey(self):
+ self.send_command('masterkey')
+ i = self.pexpect.expect('([0-9a-fA-F]{32})')
+ if i == 0:
+ masterkey = self.pexpect.match.groups()[0].decode("utf-8")
+ self.pexpect.expect('Done')
+ return masterkey
+
+ def set_masterkey(self, masterkey):
+ cmd = 'masterkey ' + masterkey
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_key_sequence_counter(self):
+ self.send_command('keysequence counter')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ key_sequence_counter = int(self.pexpect.match.groups()[0])
+ self.pexpect.expect('Done')
+ return key_sequence_counter
+
+ def set_key_sequence_counter(self, key_sequence_counter):
+ cmd = 'keysequence counter %d' % key_sequence_counter
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_key_switch_guardtime(self, key_switch_guardtime):
+ cmd = 'keysequence guardtime %d' % key_switch_guardtime
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_network_id_timeout(self, network_id_timeout):
+ cmd = 'networkidtimeout %d' % network_id_timeout
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_network_name(self):
+ self.send_command('networkname')
+ while True:
+ i = self.pexpect.expect(['Done', '(\S+)'])
+ if i != 0:
+ network_name = self.pexpect.match.groups()[0].decode('utf-8')
+ else:
+ break
+ return network_name
+
+ def set_network_name(self, network_name):
+ cmd = 'networkname ' + network_name
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_panid(self):
+ self.send_command('panid')
+ i = self.pexpect.expect('([0-9a-fA-F]{4})')
+ if i == 0:
+ panid = int(self.pexpect.match.groups()[0], 16)
+ self.pexpect.expect('Done')
+ return panid
+
+ def set_panid(self, panid):
+ cmd = 'panid %d' % panid
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_partition_id(self):
+ self.send_command('leaderpartitionid')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ weight = self.pexpect.match.groups()[0]
+ self.pexpect.expect('Done')
+ return weight
+
+ def set_partition_id(self, partition_id):
+ cmd = 'leaderpartitionid %d' % partition_id
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_router_upgrade_threshold(self, threshold):
+ cmd = 'routerupgradethreshold %d' % threshold
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_router_downgrade_threshold(self, threshold):
+ cmd = 'routerdowngradethreshold %d' % threshold
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def release_router_id(self, router_id):
+ cmd = 'releaserouterid %d' % router_id
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_state(self):
+ states = ['detached', 'child', 'router', 'leader']
+ self.send_command('state')
+ match = self.pexpect.expect(states)
+ self.pexpect.expect('Done')
+ return states[match]
+
+ def set_state(self, state):
+ cmd = 'state ' + state
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_timeout(self):
+ self.send_command('childtimeout')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ timeout = self.pexpect.match.groups()[0]
+ self.pexpect.expect('Done')
+ return timeout
+
+ def set_timeout(self, timeout):
+ cmd = 'childtimeout %d' % timeout
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_max_children(self, number):
+ cmd = 'childmax %d' % number
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_weight(self):
+ self.send_command('leaderweight')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ weight = self.pexpect.match.groups()[0]
+ self.pexpect.expect('Done')
+ return weight
+
+ def set_weight(self, weight):
+ cmd = 'leaderweight %d' % weight
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def add_ipaddr(self, ipaddr):
+ cmd = 'ipaddr add ' + ipaddr
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def get_addrs(self):
+ addrs = []
+ self.send_command('ipaddr')
+
+ while True:
+ i = self.pexpect.expect(['(\S+:\S+)\r\n', 'Done'])
+ if i == 0:
+ addrs.append(self.pexpect.match.groups()[0].decode("utf-8"))
+ elif i == 1:
+ break
+
+ return addrs
+
+ def get_context_reuse_delay(self):
+ self.send_command('contextreusedelay')
+ i = self.pexpect.expect('(\d+)\r\n')
+ if i == 0:
+ timeout = self.pexpect.match.groups()[0]
+ self.pexpect.expect('Done')
+ return timeout
+
+ def set_context_reuse_delay(self, delay):
+ cmd = 'contextreusedelay %d' % delay
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def add_prefix(self, prefix, flags, prf = 'med'):
+ cmd = 'prefix add ' + prefix + ' ' + flags + ' ' + prf
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def remove_prefix(self, prefix):
+ cmd = ' prefix remove ' + prefix
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def add_route(self, prefix, prf = 'med'):
+ cmd = 'route add ' + prefix + ' ' + prf
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def remove_route(self, prefix):
+ cmd = 'route remove ' + prefix
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def register_netdata(self):
+ self.send_command('netdataregister')
+ self.pexpect.expect('Done')
+
+ def energy_scan(self, mask, count, period, scan_duration, ipaddr):
+ cmd = 'commissioner energy ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + str(scan_duration) + ' ' + ipaddr
+ self.send_command(cmd)
+ self.pexpect.expect('Energy:', timeout=8)
+
+ def panid_query(self, panid, mask, ipaddr):
+ cmd = 'commissioner panid ' + str(panid) + ' ' + str(mask) + ' ' + ipaddr
+ self.send_command(cmd)
+ self.pexpect.expect('Conflict:', timeout=8)
+
+ def scan(self):
+ self.send_command('scan')
+
+ results = []
+ while True:
+ i = self.pexpect.expect(['\|\s(\S+)\s+\|\s(\S+)\s+\|\s([0-9a-fA-F]{4})\s\|\s([0-9a-fA-F]{16})\s\|\s(\d+)\r\n',
+ 'Done'])
+ if i == 0:
+ results.append(self.pexpect.match.groups())
+ else:
+ break
+
+ return results
+
+ def ping(self, ipaddr, num_responses=1, size=None):
+ cmd = 'ping ' + ipaddr
+ if size != None:
+ cmd += ' ' + str(size)
+
+ self.send_command(cmd)
+
+ result = True
+ try:
+ responders = {}
+ while len(responders) < num_responses:
+ i = self.pexpect.expect(['from (\S+):'])
+ if i == 0:
+ responders[self.pexpect.match.groups()[0]] = 1
+ self.pexpect.expect('\n')
+ except pexpect.TIMEOUT:
+ result = False
+
+ return result
+
+ def set_router_selection_jitter(self, jitter):
+ cmd = 'routerselectionjitter %d' % jitter
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None):
+ self.send_command('dataset clear')
+ self.pexpect.expect('Done')
+
+ cmd = 'dataset activetimestamp %d' % timestamp
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if panid != None:
+ cmd = 'dataset panid %d' % panid
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if channel != None:
+ cmd = 'dataset channel %d' % channel
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if channel_mask != None:
+ cmd = 'dataset channelmask %d' % channel_mask
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if master_key != None:
+ cmd = 'dataset masterkey ' + master_key
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ self.send_command('dataset commit active')
+ self.pexpect.expect('Done')
+
+ def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None):
+ self.send_command('dataset clear')
+ self.pexpect.expect('Done')
+
+ cmd = 'dataset pendingtimestamp %d' % pendingtimestamp
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ cmd = 'dataset activetimestamp %d' % activetimestamp
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if panid != None:
+ cmd = 'dataset panid %d' % panid
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ if channel != None:
+ cmd = 'dataset channel %d' % channel
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ self.send_command('dataset commit pending')
+ self.pexpect.expect('Done')
+
+ def announce_begin(self, mask, count, period, ipaddr):
+ cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None,
+ panid=None, master_key=None, mesh_local=None, network_name=None, binary=None):
+ cmd = 'dataset mgmtsetcommand active '
+
+ if active_timestamp != None:
+ cmd += 'activetimestamp %d ' % active_timestamp
+
+ if channel != None:
+ cmd += 'channel %d ' % channel
+
+ if channel_mask != None:
+ cmd += 'channelmask %d ' % channel_mask
+
+ if extended_panid != None:
+ cmd += 'extpanid ' + extended_panid + ' '
+
+ if panid != None:
+ cmd += 'panid %d ' % panid
+
+ if master_key != None:
+ cmd += 'masterkey ' + master_key + ' '
+
+ if mesh_local != None:
+ cmd += 'localprefix ' + mesh_local + ' '
+
+ if network_name != None:
+ cmd += 'networkname ' + network_name + ' '
+
+ if binary != None:
+ cmd += 'binary ' + binary + ' '
+
+ self.send_command(cmd)
+ self.pexpect.expect('Done')
+
+ def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None,
+ panid=None, master_key=None, mesh_local=None, network_name=None):
+ cmd = 'dataset mgmtsetcommand pending '
+
+ if pending_timestamp != None:
+ cmd += 'pendingtimestamp %d ' % pending_timestamp
+
+ if active_timestamp != None:
+ cmd += 'activetimestamp %d ' % active_timestamp
+
+ if delay_timer != None:
+ cmd += 'delaytimer %d ' % delay_timer
+
+ if channel != None:
+ cmd += 'channel %d ' % channel
+
+ if panid != None:
+ cmd += 'panid %d ' % panid
+
+ if master_key != None:
+ cmd += 'masterkey ' + master_key + ' '
+
+ if mesh_local != None:
+ cmd += 'localprefix ' + mesh_local + ' '
+
+ if network_name != None:
+ cmd += 'networkname ' + network_name + ' '
+
+ self.send_command(cmd)
+ self.pexpect.expect('Done')