| /* | |
| * Copyright (c) 2016, The OpenThread Authors. | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the name of the copyright holder nor the | |
| * names of its contributors may be used to endorse or promote products | |
| * derived from this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #pragma once | |
| #define OTDLL 1 | |
| #include <openthread/openthread.h> | |
| #include <openthread/border_router.h> | |
| #include <openthread/thread_ftd.h> | |
| #include <openthread/commissioner.h> | |
| #include <openthread/joiner.h> | |
| #include <wrl.h> | |
| #include <collection.h> | |
| using namespace Platform; | |
| using namespace Platform::Collections; | |
| using namespace Platform::Metadata; | |
| using namespace Windows::Foundation::Collections; | |
| using namespace Windows::Networking; | |
| namespace ot | |
| { | |
| ref class otAdapter; | |
| public delegate void otAdapterRemovalDelegate(otAdapter^ sender); | |
| public delegate void otIpAddressAddedDelegate(otAdapter^ sender); | |
| public delegate void otIpAddressRemovedDelegate(otAdapter^ sender); | |
| public delegate void otIpRlocAddedDelegate(otAdapter^ sender); | |
| public delegate void otIpRlocRemovedDelegate(otAdapter^ sender); | |
| public delegate void otIpLinkLocalAddresChangedDelegate(otAdapter^ sender); | |
| public delegate void otIpMeshLocalAddresChangedDelegate(otAdapter^ sender); | |
| public delegate void otNetRoleChangedDelegate(otAdapter^ sender); | |
| public delegate void otNetPartitionIdChangedDelegate(otAdapter^ sender); | |
| public delegate void otNetKeySequenceCounterChangedDelegate(otAdapter^ sender); | |
| public delegate void otThreadChildAddedDelegate(otAdapter^ sender); | |
| public delegate void otThreadChildRemovedDelegate(otAdapter^ sender); | |
| public delegate void otThreadNetDataUpdatedDelegate(otAdapter^ sender); | |
| [Flags] | |
| public enum class otLinkModeFlags : unsigned int | |
| { | |
| None = 0, | |
| RxOnWhenIdle = 0x1, /* 1, if the sender has its receiver on when not transmitting. 0, otherwise. */ | |
| SecureDataRequests = 0x2, /* 1, if the sender will use IEEE 802.15.4 to secure all data requests. 0, otherwise. */ | |
| DeviceType = 0x4, /* 1, if the sender is an FFD. 0, otherwise. */ | |
| NetworkData = 0x8 /* 1, if the sender requires the full Network Data. 0, otherwise. */ | |
| }; | |
| public enum class otThreadState | |
| { | |
| Offline, | |
| Disabled, | |
| Detached, | |
| Child, | |
| Router, | |
| Leader | |
| }; | |
| // Helper class for OpenThread Interface/Adapter specific APIs | |
| public ref class otAdapter sealed | |
| { | |
| private: | |
| void *_Instance; | |
| #define DeviceInstance ((otInstance*)_Instance) | |
| #define ThrowOnFailure(exp) \ | |
| do { \ | |
| auto res = exp; \ | |
| if (res != 0) \ | |
| throw Exception::CreateException(TheadErrorToHResult(res), #exp); \ | |
| } while (false) | |
| public: | |
| #pragma region Events | |
| event otAdapterRemovalDelegate^ AdapterRemoval; | |
| event otIpAddressAddedDelegate^ IpAddressAdded; | |
| event otIpAddressRemovedDelegate^ IpAddressRemoved; | |
| event otIpRlocAddedDelegate^ IpRlocAdded; | |
| event otIpRlocRemovedDelegate^ IpRlocRemoved; | |
| event otIpLinkLocalAddresChangedDelegate^ IpLinkLocalAddresChanged; | |
| event otIpMeshLocalAddresChangedDelegate^ IpMeshLocalAddresChanged; | |
| event otNetRoleChangedDelegate^ NetRoleChanged; | |
| event otNetPartitionIdChangedDelegate^ NetPartitionIdChanged; | |
| event otNetKeySequenceCounterChangedDelegate^ NetKeySequenceCounterChanged; | |
| event otThreadChildAddedDelegate^ ThreadChildAdded; | |
| event otThreadChildRemovedDelegate^ ThreadChildRemoved; | |
| event otThreadNetDataUpdatedDelegate^ ThreadNetDataUpdated; | |
| #pragma endregion | |
| #pragma region Properties | |
| property IntPtr RawHandle | |
| { | |
| IntPtr get() { return _Instance; } | |
| } | |
| property Guid InterfaceGuid | |
| { | |
| Guid get() { return otGetDeviceGuid(DeviceInstance); } | |
| } | |
| property uint32_t IfIndex | |
| { | |
| uint32_t get() { return otGetDeviceIfIndex(DeviceInstance); } | |
| } | |
| property uint32_t CompartmentId | |
| { | |
| uint32_t get() { return otGetCompartmentId(DeviceInstance); } | |
| } | |
| #pragma region Link Layer | |
| property signed int /*int8_t*/ MaxTransmitPower | |
| { | |
| signed int get() { return otLinkGetMaxTransmitPower(DeviceInstance); } | |
| void set(signed int value) | |
| { | |
| if (value > 127) throw Exception::CreateException(E_INVALIDARG); | |
| otLinkSetMaxTransmitPower(DeviceInstance, (int8_t)value); | |
| } | |
| } | |
| property uint32_t PollPeriod | |
| { | |
| uint32_t get() { return otLinkGetPollPeriod(DeviceInstance); } | |
| void set(uint32_t value) { otLinkSetPollPeriod(DeviceInstance, value); } | |
| } | |
| property uint8_t Channel | |
| { | |
| uint8_t get() { return otLinkGetChannel(DeviceInstance); } | |
| void set(uint8_t value) { ThrowOnFailure(otLinkSetChannel(DeviceInstance, value)); } | |
| } | |
| property uint16_t PanId | |
| { | |
| uint16_t get() { return otLinkGetPanId(DeviceInstance); } | |
| void set(uint16_t value) { ThrowOnFailure(otLinkSetPanId(DeviceInstance, value)); } | |
| } | |
| property uint16_t ShortAddress | |
| { | |
| uint16_t get() { return otLinkGetShortAddress(DeviceInstance); } | |
| } | |
| property uint64_t ExtendedAddress | |
| { | |
| uint64_t get() | |
| { | |
| auto addr = otLinkGetExtendedAddress(DeviceInstance); | |
| auto ret = *(uint64_t*)addr; | |
| otFreeMemory(addr); | |
| return ret; | |
| } | |
| void set(uint64_t value) | |
| { | |
| ThrowOnFailure(otLinkSetExtendedAddress(DeviceInstance, (otExtAddress*)&value)); | |
| } | |
| } | |
| property uint64_t FactoryAssignedIeeeEui64 | |
| { | |
| uint64_t get() | |
| { | |
| uint64_t addr; | |
| otLinkGetFactoryAssignedIeeeEui64(DeviceInstance, (otExtAddress*)&addr); | |
| return addr; | |
| } | |
| } | |
| property uint64_t JoinerId | |
| { | |
| uint64_t get() | |
| { | |
| uint64_t addr; | |
| otLinkGetJoinerId(DeviceInstance, (otExtAddress*)&addr); | |
| return addr; | |
| } | |
| } | |
| #pragma endregion | |
| #pragma region IP Layer | |
| property bool IpEnabled | |
| { | |
| bool get() { return otIp6IsEnabled(DeviceInstance); } | |
| void set(bool value) { ThrowOnFailure(otIp6SetEnabled(DeviceInstance, value)); } | |
| } | |
| #pragma endregion | |
| #pragma region Thread Layer | |
| property uint64_t ExtendedPanId | |
| { | |
| uint64_t get() | |
| { | |
| auto panid = otThreadGetExtendedPanId(DeviceInstance); | |
| auto ret = *(uint64_t*)panid; | |
| otFreeMemory(panid); | |
| return ret; | |
| } | |
| void set(uint64_t value) | |
| { | |
| otThreadSetExtendedPanId(DeviceInstance, (uint8_t*)&value); | |
| } | |
| } | |
| property otLinkModeFlags LinkMode | |
| { | |
| otLinkModeFlags get() | |
| { | |
| auto linkmode = otThreadGetLinkMode(DeviceInstance); | |
| otLinkModeFlags flags = otLinkModeFlags::None; | |
| if (linkmode.mRxOnWhenIdle) flags = flags | otLinkModeFlags::RxOnWhenIdle; | |
| if (linkmode.mSecureDataRequests) flags = flags | otLinkModeFlags::SecureDataRequests; | |
| if (linkmode.mDeviceType) flags = flags | otLinkModeFlags::DeviceType; | |
| if (linkmode.mNetworkData) flags = flags | otLinkModeFlags::NetworkData; | |
| return flags; | |
| } | |
| void set(otLinkModeFlags value) | |
| { | |
| otLinkModeConfig linkmode = { 0 }; | |
| if ((value & otLinkModeFlags::RxOnWhenIdle) != otLinkModeFlags::None) | |
| linkmode.mRxOnWhenIdle = true; | |
| if ((value & otLinkModeFlags::SecureDataRequests) != otLinkModeFlags::None) | |
| linkmode.mSecureDataRequests = true; | |
| if ((value & otLinkModeFlags::DeviceType) != otLinkModeFlags::None) | |
| linkmode.mDeviceType = true; | |
| if ((value & otLinkModeFlags::NetworkData) != otLinkModeFlags::None) | |
| linkmode.mNetworkData = true; | |
| ThrowOnFailure(otThreadSetLinkMode(DeviceInstance, linkmode)); | |
| } | |
| } | |
| static uint32_t charToValue(wchar_t c) | |
| { | |
| if (c >= L'a' && c <= L'f') | |
| { | |
| return c - L'a'; | |
| } | |
| else if (c >= L'A' && c <= L'F') | |
| { | |
| return c - L'A'; | |
| } | |
| else if (c >= L'0' && c <= L'9') | |
| { | |
| return c - L'0'; | |
| } | |
| else | |
| { | |
| throw Exception::CreateException(E_INVALIDARG); | |
| } | |
| } | |
| property String^ MasterKey | |
| { | |
| String^ get() | |
| { | |
| constexpr char hexmap[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | |
| auto key = otThreadGetMasterKey(DeviceInstance); | |
| WCHAR szKey[OT_MASTER_KEY_SIZE * 2 + 1] = { 0 }; | |
| for (uint8_t i = 0; i < OT_MASTER_KEY_SIZE; i++) | |
| { | |
| szKey[2 * i] = hexmap[(key->m8[i] & 0xF0) >> 4]; | |
| szKey[2 * i + 1] = hexmap[key->m8[i] & 0x0F]; | |
| } | |
| otFreeMemory(key); | |
| return ref new String(szKey); | |
| } | |
| void set(String^ value) | |
| { | |
| otMasterKey key; | |
| uint8_t keyLen = 0; | |
| for (uint32_t i = 0; i < value->Length() - 1; i+=2) | |
| { | |
| key.m8[keyLen++] = (uint8_t)((charToValue(value->Data()[i]) << 4) | | |
| charToValue(value->Data()[i + 1])); | |
| } | |
| if (keyLen * 2 == value->Length() - 1) | |
| { | |
| key.m8[keyLen++] = (uint8_t)(charToValue(value->Data()[value->Length()-1])) << 4; | |
| } | |
| memset(key.m8 + keyLen, 0, sizeof(key) - keyLen); | |
| ThrowOnFailure(otThreadSetMasterKey(DeviceInstance, &key)); | |
| } | |
| } | |
| property String^ NetworkName | |
| { | |
| String^ get() | |
| { | |
| auto _name = otThreadGetNetworkName(DeviceInstance); | |
| WCHAR name[OT_NETWORK_NAME_MAX_SIZE + 1]; | |
| MultiByteToWideChar(CP_UTF8, 0, _name, -1, name, ARRAYSIZE(name)); | |
| otFreeMemory(_name); | |
| return ref new String(name); | |
| } | |
| void set(String^ value) | |
| { | |
| char name[OT_NETWORK_NAME_MAX_SIZE + 1]; | |
| auto len = WideCharToMultiByte(CP_UTF8, 0, value->Data(), -1, name, ARRAYSIZE(name), nullptr, nullptr); | |
| if (len <= 0) throw Exception::CreateException(E_INVALIDARG); | |
| ThrowOnFailure(otThreadSetNetworkName(DeviceInstance, name)); | |
| } | |
| } | |
| property uint8_t MaxAllowedChildren | |
| { | |
| uint8_t get() { return otThreadGetMaxAllowedChildren(DeviceInstance); } | |
| void set(uint8_t value) { ThrowOnFailure(otThreadSetMaxAllowedChildren(DeviceInstance, value)); } | |
| } | |
| property uint32_t ChildTimeout | |
| { | |
| uint32_t get() { return otThreadGetChildTimeout(DeviceInstance); } | |
| void set(uint32_t value) { otThreadSetChildTimeout(DeviceInstance, value); } | |
| } | |
| property bool ThreadEnabled | |
| { | |
| //bool get() { return otIsThreadStarted(DeviceInstance); } | |
| void set(bool value) { ThrowOnFailure(otThreadSetEnabled(DeviceInstance, value)); } | |
| } | |
| property bool AutoStart | |
| { | |
| bool get() { return otThreadGetAutoStart(DeviceInstance); } | |
| void set(bool value) { ThrowOnFailure(otThreadSetAutoStart(DeviceInstance, value)); } | |
| } | |
| property bool Singleton | |
| { | |
| bool get() { return otThreadIsSingleton(DeviceInstance); } | |
| } | |
| property bool RouterRoleEnabled | |
| { | |
| bool get() { return otThreadIsRouterRoleEnabled(DeviceInstance); } | |
| void set(bool value) { otThreadSetRouterRoleEnabled(DeviceInstance, value); } | |
| } | |
| property uint8_t PreferredRouterId | |
| { | |
| void set(uint8_t value) { ThrowOnFailure(otThreadSetPreferredRouterId(DeviceInstance, value)); } | |
| } | |
| property HostName^ MeshLocalEid | |
| { | |
| HostName^ get() | |
| { | |
| auto addr = otThreadGetMeshLocalEid(DeviceInstance); | |
| WCHAR szAddr[46]; | |
| RtlIpv6AddressToString((IN6_ADDR*)addr, szAddr); | |
| otFreeMemory(addr); | |
| return ref new HostName(ref new String(szAddr)); | |
| } | |
| } | |
| property HostName^ LeaderRloc | |
| { | |
| HostName^ get() | |
| { | |
| IN6_ADDR addr; | |
| ThrowOnFailure(otThreadGetLeaderRloc(DeviceInstance, (otIp6Address*)&addr)); | |
| WCHAR szAddr[46]; | |
| RtlIpv6AddressToString(&addr, szAddr); | |
| return ref new HostName(ref new String(szAddr)); | |
| } | |
| } | |
| property uint8_t LocalLeaderWeight | |
| { | |
| uint8_t get() { return otThreadGetLocalLeaderWeight(DeviceInstance); } | |
| void set(uint8_t value) { otThreadSetLocalLeaderWeight(DeviceInstance, value); } | |
| } | |
| property uint32_t LocalLeaderPartitionId | |
| { | |
| uint32_t get() { return otThreadGetLocalLeaderPartitionId(DeviceInstance); } | |
| void set(uint32_t value) { otThreadSetLocalLeaderPartitionId(DeviceInstance, value); } | |
| } | |
| property uint8_t LeaderWeight | |
| { | |
| uint8_t get() { return otThreadGetLeaderWeight(DeviceInstance); } | |
| } | |
| property uint32_t LeaderRouterId | |
| { | |
| uint32_t get() { return otThreadGetLeaderRouterId(DeviceInstance); } | |
| } | |
| property uint32_t PartitionId | |
| { | |
| uint32_t get() { return otThreadGetPartitionId(DeviceInstance); } | |
| } | |
| property uint16_t Rloc16 | |
| { | |
| uint16_t get() { return otThreadGetRloc16(DeviceInstance); } | |
| } | |
| property otThreadState State | |
| { | |
| otThreadState get() | |
| { | |
| return (otThreadState)otThreadGetDeviceRole(DeviceInstance); | |
| } | |
| } | |
| #pragma endregion | |
| #pragma endregion | |
| #pragma region Constructor/Destructor | |
| otAdapter(_In_ IntPtr /*otInstance**/ aInstance) | |
| { | |
| _Instance = (void*)aInstance; | |
| IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this); | |
| // Register for device availability callbacks | |
| otSetStateChangedCallback(DeviceInstance, ThreadStateChangeCallback, pInspectable); | |
| } | |
| virtual ~otAdapter() | |
| { | |
| // Unregister for callbacks for the device | |
| otSetStateChangedCallback(DeviceInstance, nullptr, nullptr); | |
| // Free the device | |
| otFreeMemory(DeviceInstance); | |
| } | |
| #pragma endregion | |
| #pragma region Functions | |
| void PlatformReset() | |
| { | |
| otInstanceReset(DeviceInstance); | |
| } | |
| void FactoryReset() | |
| { | |
| otInstanceFactoryReset(DeviceInstance); | |
| } | |
| void BecomeRouter() | |
| { | |
| ThrowOnFailure(otThreadBecomeRouter(DeviceInstance)); | |
| } | |
| void BecomeLeader() | |
| { | |
| ThrowOnFailure(otThreadBecomeLeader(DeviceInstance)); | |
| } | |
| #pragma endregion | |
| internal: | |
| void InvokeAdapterRemoval() | |
| { | |
| AdapterRemoval(this); | |
| } | |
| private: | |
| friend ref class otApi; | |
| static void OTCALL | |
| ThreadStateChangeCallback( | |
| uint32_t aFlags, | |
| _In_ void* aContext | |
| ) | |
| { | |
| IInspectable* pInspectable = (IInspectable*)aContext; | |
| otAdapter^ pThis = reinterpret_cast<otAdapter^>(pInspectable); | |
| if (aFlags & OT_CHANGED_IP6_ADDRESS_ADDED) | |
| { | |
| pThis->IpAddressAdded(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) | |
| { | |
| pThis->IpAddressRemoved(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_RLOC_ADDED) | |
| { | |
| pThis->IpRlocAdded(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_RLOC_REMOVED) | |
| { | |
| pThis->IpRlocRemoved(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_LL_ADDR) | |
| { | |
| pThis->IpLinkLocalAddresChanged(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_ML_ADDR) | |
| { | |
| pThis->IpMeshLocalAddresChanged(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_ROLE) | |
| { | |
| pThis->NetRoleChanged(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_PARTITION_ID) | |
| { | |
| pThis->NetPartitionIdChanged(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER) | |
| { | |
| pThis->NetKeySequenceCounterChanged(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_CHILD_ADDED) | |
| { | |
| pThis->ThreadChildAdded(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_CHILD_REMOVED) | |
| { | |
| pThis->ThreadChildRemoved(pThis); | |
| } | |
| if (aFlags & OT_CHANGED_THREAD_NETDATA) | |
| { | |
| pThis->ThreadNetDataUpdated(pThis); | |
| } | |
| } | |
| static HRESULT | |
| TheadErrorToHResult( | |
| int /* otError */ error | |
| ) | |
| { | |
| switch (error) | |
| { | |
| case OT_ERROR_NO_BUFS: return E_OUTOFMEMORY; | |
| case OT_ERROR_DROP: | |
| case OT_ERROR_NO_ROUTE: return HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE); | |
| case OT_ERROR_INVALID_ARGS: return E_INVALIDARG; | |
| case OT_ERROR_SECURITY: return E_ACCESSDENIED; | |
| case OT_ERROR_NOT_CAPABLE: | |
| case OT_ERROR_NOT_IMPLEMENTED: return E_NOTIMPL; | |
| case OT_ERROR_INVALID_STATE: return E_NOT_VALID_STATE; | |
| case OT_ERROR_NOT_FOUND: return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| case OT_ERROR_ALREADY: return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); | |
| case OT_ERROR_RESPONSE_TIMEOUT: return HRESULT_FROM_WIN32(ERROR_TIMEOUT); | |
| default: return E_FAIL; | |
| } | |
| } | |
| }; | |
| } // namespace ot | |