blob: 3a0a001c8ea96f97bcaa479bec98bb4a4e007693 [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "otAdapter.h"
#include <collection.h>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
#define MAC8_FORMAT L"%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X"
#define MAC8_ARG(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
namespace ot
{
public delegate void otAdapterArrivalDelegate(otAdapter^ aAdapter);
// Helper class for OpenThread API
public ref class otApi sealed
{
private:
void *_apiInstance;
#define ApiInstance ((otApiInstance*)_apiInstance)
CRITICAL_SECTION _cs;
Vector<otAdapter^>^ _adapters;
public:
// Event for device availability changes
event otAdapterArrivalDelegate^ AdapterArrival;
property IntPtr RawHandle
{
IntPtr get() { return _apiInstance; }
}
// Constructor
otApi() :
_adapters(ref new Vector<otAdapter^>())
{
// Initialize the API handle
_apiInstance = otApiInit();
if (_apiInstance == nullptr)
{
throw Exception::CreateException(E_UNEXPECTED, L"otApiInit failed.");
}
InitializeCriticalSection(&_cs);
IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this);
// Register for device availability callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, pInspectable);
// Query list of devices
auto deviceList = otEnumerateDevices(ApiInstance);
if (deviceList)
{
EnterCriticalSection(&_cs);
// Add each adapter to our cache unless it already was inserted from a notification
for (DWORD i = 0; i < deviceList->aDevicesLength; i++)
{
if (GetAdapter(deviceList->aDevices[i]) == nullptr)
{
auto deviceInstance = otInstanceInit(ApiInstance, &deviceList->aDevices[i]);
if (deviceInstance)
{
_adapters->Append(ref new otAdapter(deviceInstance));
}
}
}
LeaveCriticalSection(&_cs);
otFreeMemory(deviceList);
}
}
// Destructor
virtual ~otApi()
{
// Clear registration for callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
DeleteCriticalSection(&_cs);
// Clean up api
otApiFinalize(ApiInstance);
_apiInstance = nullptr;
}
// Returns the entire list of adapters
IVectorView<otAdapter^>^ GetAdapters()
{
IVectorView<otAdapter^>^ adapters;
EnterCriticalSection(&_cs);
adapters = _adapters->GetView(); // TODO - Need to copy
LeaveCriticalSection(&_cs);
return adapters;
}
// Helper to get an adapter, given its device guid
otAdapter^ GetAdapter(Guid aDeviceGuid)
{
otAdapter^ ret = nullptr;
EnterCriticalSection(&_cs);
for (auto&& adapter : _adapters)
{
if (adapter->InterfaceGuid == aDeviceGuid)
{
ret = adapter;
break;
}
}
LeaveCriticalSection(&_cs);
return ret;
}
// Helper function to convert mac address to string
static String^ MacToString(uint64_t mac)
{
WCHAR szMac[64] = { 0 };
swprintf_s(szMac, 64, MAC8_FORMAT, MAC8_ARG(((UCHAR*)&mac)));
return ref new String(szMac);
}
// Helper function to convert RLOC16/PANID to string
static String^ Rloc16ToString(uint16_t rloc)
{
WCHAR szRloc[16] = { 0 };
swprintf_s(szRloc, 16, L"0x%X", rloc);
return ref new String(szRloc);
}
// Helper function to convert state to string
static String^ ThreadStateToString(otThreadState state)
{
switch (state)
{
default:
case otThreadState::Offline: return L"Offline";
case otThreadState::Disabled: return L"Disabled";
case otThreadState::Detached: return L"Disconnected";
case otThreadState::Child: return L"Connected - Child";
case otThreadState::Router: return L"Connected - Router";
case otThreadState::Leader: return L"Connected - Leader";
}
}
private:
// Callback from OpenThread indicating arrival or removal of interfaces
static void OTCALL
ThreadDeviceAvailabilityCallback(
bool aAdded,
const GUID* aDeviceGuid,
_In_ void* aContext
)
{
IInspectable* pInspectable = (IInspectable*)aContext;
otApi^ pThis = reinterpret_cast<otApi^>(pInspectable);
if (aAdded)
{
otAdapter^ adapter = nullptr;
// Add the device to the list, if it isn't already there
EnterCriticalSection(&pThis->_cs);
if (pThis->GetAdapter(*aDeviceGuid) == nullptr)
{
auto deviceInstance = otInstanceInit((otApiInstance*)pThis->_apiInstance, aDeviceGuid);
if (deviceInstance)
{
pThis->_adapters->Append(adapter = ref new otAdapter(deviceInstance));
}
}
LeaveCriticalSection(&pThis->_cs);
if (adapter)
{
// Send a notification of arrival
pThis->AdapterArrival(adapter);
}
}
else
{
otAdapter^ ret = nullptr;
Guid guid = *aDeviceGuid;
EnterCriticalSection(&pThis->_cs);
// Look up in the cached list of adapters to remove it
uint32_t i = 0;
for (auto&& adapter : pThis->_adapters)
{
if (adapter->InterfaceGuid == guid)
{
ret = adapter;
pThis->_adapters->RemoveAt(i);
break;
}
i++;
}
LeaveCriticalSection(&pThis->_cs);
if (ret)
{
ret->InvokeAdapterRemoval();
}
}
}
};
} // namespace ot