| /* |
| * 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 |
| |