"${chip_root}/examples/chip-tool",
"${chip_root}/examples/minimal-mdns:minimal-mdns-client",
"${chip_root}/examples/minimal-mdns:minimal-mdns-server",
+ "${chip_root}/examples/minimal-mdns:mdns-advertiser",
]
}
*
*/
+/* TIZEN_CHIP_MODIFY */
+
#include "Commands.h"
#include "Command.h"
err = chip::Platform::MemoryInit();
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Memory failure: %s", chip::ErrorStr(err)));
-#if CHIP_DEVICE_LAYER_TARGET_LINUX && CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+#if (CHIP_DEVICE_LAYER_TARGET_LINUX && CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE) || \
+ (CHIP_DEVICE_LAYER_TARGET_TIZEN && CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE)
// By default, Linux device is configured as a BLE peripheral while the controller needs a BLE central.
SuccessOrExit(err = chip::DeviceLayer::Internal::BLEMgrImpl().ConfigureBle(/* BLE adapter ID */ 0, /* BLE central */ true));
#endif
BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(gio-unix-2.0)
-BuildRequires: pkgconfig(avahi-client)
+BuildRequires: pkgconfig(dns_sd)
BuildRequires: python3
BuildRequires: gn
cp out/host/chip-shell %{buildroot}%{_bindir}/
cp out/host/minimal-mdns-client %{buildroot}%{_bindir}/
cp out/host/minimal-mdns-server %{buildroot}%{_bindir}/
+cp out/host/mdns-advertiser %{buildroot}%{_bindir}/
%post
%{_bindir}/chip-shell
%{_bindir}/minimal-mdns-client
%{_bindir}/minimal-mdns-server
+%{_bindir}/mdns-advertiser
echo -e "\e[31m********** C A U T I O N **********\e[0m"
echo -e "\e[31m***********************************\e[0m"
echo -e "\e[31m This script is a program that downloads the code of CHIP upstream and enables tizen gbs build.\e[0m"
- echo -e "\e[31m This script contains the contents of deleting all files from CHIP folder (except packaging, git folders)! We ask for safety in use.\e[0m"
+ echo -e "\e[31m This script contains the contents of deleting all files from CHIP folder (except packaging, git, tizen specific folders)! We ask for safety in use.\e[0m"
+ echo -e "\e[31m Also, files with the TIZEN_CHIP_MODIFY keyword will not be deleted.\e[0m"
echo -e "\e[31m Please do not change the location of this script.\e[0m"
echo -e "\e[31m Please check if the path of CHIP folder below is correct. \e[0m"
echo -e "\e[32m CHIP folder path : $PWD\e[0m"
done
echo -e "\e[33m1. copy tizen modify files to temp folder\e[0m"
-mkdir packaging/tizen_fix/temp/
+mkdir -p packaging/tizen_fix/temp/src/platform/
+
+echo "copy src/platform/Tizen folder to packaging/tizen_fix/temp/src/platform/Tizen"
+cp -rf src/platform/Tizen packaging/tizen_fix/temp/src/platform/
+
for MODIFY_FILE in $(grep -lr 'TIZEN_CHIP_MODIFY' | grep -v 'tizen_fix.sh')
do
MODIFY_FILE_PATH=$(dirname "$MODIFY_FILE")
# See the License for the specific language governing permissions and
# limitations under the License.
+# TIZEN_CHIP_MODIFY
+
import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("//build_overrides/nlio.gni")
if (chip_enable_openthread) {
import("//build_overrides/openthread.gni")
- if (chip_device_platform == "linux" || chip_device_platform == "Darwin") {
+ if (chip_device_platform == "linux" || chip_device_platform == "Darwin" || chip_device_platform == "tizen") {
import("//build_overrides/ot_br_posix.gni")
}
}
}
}
+if (chip_device_platform == "tizen" && chip_mdns != "none") {
+ pkg_config("dns_sd") {
+ packages = [ "dns_sd" ]
+ }
+}
+
if (chip_device_platform != "none") {
declare_args() {
# Extra header to include in CHIPDeviceConfig.h for project.
chip_enable_additional_data_advertising = true
# Enable adding rotating device id to the additional data.
- chip_enable_rotating_device_id = true
+ chip_enable_rotating_device_id = false
}
buildconfig_header("platform_buildconfig") {
"OPENTHREAD_CONFIG_ENABLE_TOBLE=false",
]
- if (chip_device_platform == "linux" || chip_device_platform == "darwin") {
+ if (chip_device_platform == "linux" || chip_device_platform == "darwin" || chip_device_platform == "tizen") {
defines += [ "CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE=${chip_enable_ble}" ]
}
"CHIP_DEVICE_LAYER_TARGET_LINUX=1",
"CHIP_DEVICE_LAYER_TARGET=Linux",
]
+ } else if (chip_device_platform == "tizen") {
+ defines += [
+ "CHIP_DEVICE_LAYER_TARGET_TIZEN=1",
+ "CHIP_DEVICE_LAYER_TARGET=Tizen",
+ ]
} else if (chip_device_platform == "nrfconnect") {
defines += [
"CHIP_DEVICE_LAYER_TARGET_NRFCONNECT=1",
}
public_deps += [ "${chip_root}/third_party/inipp" ]
+ } else if (chip_device_platform == "tizen") {
+ sources += [
+ "Tizen/BLEManagerImpl.cpp",
+ "Tizen/BLEManagerImpl.h",
+ "Tizen/BlePlatformConfig.h",
+ "Tizen/CHIPDevicePlatformConfig.h",
+ "Tizen/CHIPDevicePlatformEvent.h",
+ "Tizen/CHIPLinuxStorage.cpp",
+ "Tizen/CHIPLinuxStorage.h",
+ "Tizen/CHIPLinuxStorageIni.cpp",
+ "Tizen/CHIPLinuxStorageIni.h",
+ "Tizen/CHIPPlatformConfig.h",
+ "Tizen/ConfigurationManagerImpl.cpp",
+ "Tizen/ConfigurationManagerImpl.h",
+ "Tizen/ConnectivityManagerImpl.cpp",
+ "Tizen/ConnectivityManagerImpl.h",
+ "Tizen/DeviceNetworkProvisioningDelegateImpl.cpp",
+ "Tizen/DeviceNetworkProvisioningDelegateImpl.h",
+ "Tizen/InetPlatformConfig.h",
+ "Tizen/KeyValueStoreManagerImpl.cpp",
+ "Tizen/KeyValueStoreManagerImpl.h",
+ "Tizen/Logging.cpp",
+ "Tizen/PlatformManagerImpl.cpp",
+ "Tizen/PlatformManagerImpl.h",
+ "Tizen/PosixConfig.cpp",
+ "Tizen/PosixConfig.h",
+ "Tizen/SystemPlatformConfig.h",
+ "Tizen/SystemTimeSupport.cpp",
+ "Tizen/bluez/AdapterIterator.cpp",
+ "Tizen/bluez/AdapterIterator.h",
+ "Tizen/bluez/ChipDeviceScanner.cpp",
+ "Tizen/bluez/ChipDeviceScanner.h",
+ "Tizen/bluez/Helper.cpp",
+ "Tizen/bluez/Helper.h",
+ "Tizen/bluez/MainLoop.cpp",
+ "Tizen/bluez/MainLoop.h",
+ "Tizen/bluez/Types.h",
+ ]
+
+ if (chip_mdns != "none") {
+ sources += [
+ "Tizen/MdnsImpl.cpp",
+ "Tizen/MdnsImpl.h",
+ "Tizen/MdnsError.cpp",
+ "Tizen/MdnsError.h",
+ ]
+
+ public_configs += [ ":dns_sd" ]
+ }
+
+ if (chip_enable_openthread) {
+ sources += [
+ "Tizen/ThreadStackManagerImpl.cpp",
+ "Tizen/ThreadStackManagerImpl.h",
+ ]
+ public_deps += [ "${ot_br_posix_root}:ot_br_client" ]
+ }
+
+ if (chip_enable_wifi) {
+ public_deps += [ "Tizen/dbus/wpa" ]
+ }
+
+ if (chip_enable_ble) {
+ public_deps += [ "Tizen/dbus/bluez" ]
+ }
+
+ public_deps += [ "${chip_root}/third_party/inipp" ]
} else if (chip_device_platform == "nrfconnect") {
sources += [
"Zephyr/BLEManagerImpl.cpp",
}
if (chip_enable_openthread && chip_mdns == "platform" &&
- chip_device_platform != "linux") {
+ chip_device_platform != "linux" && chip_device_platform != "tizen") {
sources += [ "OpenThread/MdnsImpl.cpp" ]
}
* limitations under the License.
*/
-/* TIZEN_CHIP_MODIFY */
-
#include "MdnsImpl.h"
#include <algorithm>
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ * Copyright (c) 2018 Nest Labs, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the BLEManager singleton object
+ * for Linux platforms.
+ */
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <ble/CHIPBleServiceData.h>
+#include <new>
+#include <platform/internal/BLEManager.h>
+#include <support/CodeUtils.h>
+#include <support/SafeInt.h>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include "bluez/Helper.h"
+
+using namespace ::nl;
+using namespace ::chip::Ble;
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+namespace {
+
+static constexpr unsigned kNewConnectionScanTimeoutMs = 10000;
+static constexpr unsigned kConnectTimeoutMs = 5000;
+
+const ChipBleUUID ChipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
+ 0x9D, 0x11 } };
+const ChipBleUUID ChipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
+ 0x9D, 0x12 } };
+
+void HandleConnectTimeout(chip::System::Layer *, void * apEndpoint, chip::System::Error)
+{
+ assert(apEndpoint != nullptr);
+
+ CancelConnect(static_cast<BluezEndpoint *>(apEndpoint));
+ BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_TIMEOUT);
+}
+
+} // namespace
+
+BLEManagerImpl BLEManagerImpl::sInstance;
+
+void HandleIncomingBleConnection(BLEEndPoint * bleEP)
+{
+ ChipLogProgress(DeviceLayer, "CHIPoBluez con rcvd");
+}
+
+CHIP_ERROR BLEManagerImpl::_Init()
+{
+ CHIP_ERROR err;
+
+ err = BleLayer::Init(this, this, this, &SystemLayer);
+ SuccessOrExit(err);
+
+ mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled;
+ mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART && !mIsCentral);
+ mFlags.Set(Flags::kFastAdvertisingEnabled, true);
+ mAppState = nullptr;
+
+ memset(mDeviceName, 0, sizeof(mDeviceName));
+
+ OnChipBleConnectReceived = HandleIncomingBleConnection;
+
+ PlatformMgr().ScheduleWork(DriveBLEState, 0);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR BLEManagerImpl::_SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrExit(val != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrExit(mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
+
+ if (val != mServiceMode)
+ {
+ mServiceMode = val;
+ PlatformMgr().ScheduleWork(DriveBLEState, 0);
+ }
+
+exit:
+ return err;
+}
+
+CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ if (mFlags.Has(Flags::kAdvertisingEnabled) != val)
+ {
+ mFlags.Set(Flags::kAdvertisingEnabled, val);
+ }
+
+ PlatformMgr().ScheduleWork(DriveBLEState, 0);
+
+ return err;
+}
+
+CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode)
+{
+ switch (mode)
+ {
+ case BLEAdvertisingMode::kFastAdvertising:
+ mFlags.Set(Flags::kFastAdvertisingEnabled, true);
+ break;
+ case BLEAdvertisingMode::kSlowAdvertising:
+ mFlags.Set(Flags::kFastAdvertisingEnabled, false);
+ break;
+ default:
+ return CHIP_ERROR_INVALID_ARGUMENT;
+ }
+ mFlags.Set(Flags::kAdvertisingRefreshNeeded);
+ PlatformMgr().ScheduleWork(DriveBLEState, 0);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize)
+{
+ if (strlen(mDeviceName) >= bufSize)
+ {
+ return CHIP_ERROR_BUFFER_TOO_SMALL;
+ }
+ strcpy(buf, mDeviceName);
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * deviceName)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrExit(mServiceMode != ConnectivityManager::kCHIPoBLEServiceMode_NotSupported, err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
+
+ if (deviceName != nullptr && deviceName[0] != 0)
+ {
+ VerifyOrExit(strlen(deviceName) < kMaxDeviceNameLength, err = CHIP_ERROR_INVALID_ARGUMENT);
+ strcpy(mDeviceName, deviceName);
+ mFlags.Set(Flags::kUseCustomDeviceName);
+ }
+ else
+ {
+ uint16_t discriminator;
+ SuccessOrExit(err = ConfigurationMgr().GetSetupDiscriminator(discriminator));
+ snprintf(mDeviceName, sizeof(mDeviceName), "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator);
+ mDeviceName[kMaxDeviceNameLength] = 0;
+ mFlags.Clear(Flags::kUseCustomDeviceName);
+ }
+
+exit:
+ return err;
+}
+
+uint16_t BLEManagerImpl::_NumConnections()
+{
+ uint16_t numCons = 0;
+ return numCons;
+}
+
+CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ mBLEAdvConfig.mpBleName = mDeviceName;
+ mBLEAdvConfig.mAdapterId = aAdapterId;
+ mBLEAdvConfig.mMajor = 1;
+ mBLEAdvConfig.mMinor = 1;
+ mBLEAdvConfig.mVendorId = 1;
+ mBLEAdvConfig.mProductId = 1;
+ mBLEAdvConfig.mDeviceId = 1;
+ mBLEAdvConfig.mDuration = 2;
+ mBLEAdvConfig.mPairingStatus = 0;
+ mBLEAdvConfig.mType = ChipAdvType::BLUEZ_ADV_TYPE_UNDIRECTED_CONNECTABLE_SCANNABLE;
+ mBLEAdvConfig.mpAdvertisingUUID = "0xFEAF";
+
+ mIsCentral = aIsCentral;
+
+ return err;
+}
+
+CHIP_ERROR BLEManagerImpl::StartBLEAdvertising()
+{
+ return StartBluezAdv(mpEndpoint);
+}
+
+CHIP_ERROR BLEManagerImpl::StopBLEAdvertising()
+{
+ return StopBluezAdv(mpEndpoint);
+}
+
+void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event)
+{
+ switch (event->Type)
+ {
+ case DeviceEventType::kCHIPoBLESubscribe:
+ HandleSubscribeReceived(event->CHIPoBLESubscribe.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX);
+ {
+ ChipDeviceEvent connectionEvent;
+ connectionEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished;
+ PlatformMgr().PostEvent(&connectionEvent);
+ }
+ break;
+
+ case DeviceEventType::kCHIPoBLEUnsubscribe:
+ HandleUnsubscribeReceived(event->CHIPoBLEUnsubscribe.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX);
+ break;
+
+ case DeviceEventType::kCHIPoBLEWriteReceived:
+ HandleWriteReceived(event->CHIPoBLEWriteReceived.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX,
+ PacketBufferHandle::Adopt(event->CHIPoBLEWriteReceived.Data));
+ break;
+
+ case DeviceEventType::kCHIPoBLEIndicateConfirm:
+ HandleIndicationConfirmation(event->CHIPoBLEIndicateConfirm.ConId, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX);
+ break;
+
+ case DeviceEventType::kCHIPoBLEConnectionError:
+ HandleConnectionError(event->CHIPoBLEConnectionError.ConId, event->CHIPoBLEConnectionError.Reason);
+ break;
+ case DeviceEventType::kFabricMembershipChange:
+ case DeviceEventType::kServiceProvisioningChange:
+ case DeviceEventType::kAccountPairingChange:
+
+ // If CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled, and there is a change to the
+ // device's provisioning state, then automatically disable CHIPoBLE advertising if the device
+ // is now fully provisioned.
+#if CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED
+ if (ConfigurationMgr().IsFullyProvisioned())
+ {
+ mFlags.Clear(Flags::kAdvertisingEnabled);
+ ChipLogProgress(DeviceLayer, "CHIPoBLE advertising disabled because device is fully provisioned");
+ }
+#endif // CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED
+
+ // Force the advertising configuration to be refreshed to reflect new provisioning state.
+ mFlags.Clear(Flags::kAdvertisingConfigured);
+
+ DriveBLEState();
+ break;
+ default:
+ HandlePlatformSpecificBLEEvent(event);
+ break;
+ }
+}
+
+void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ bool controlOpComplete = false;
+ ChipLogProgress(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type);
+ switch (apEvent->Type)
+ {
+ case DeviceEventType::kPlatformLinuxBLECentralConnected:
+ if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting)
+ {
+ OnConnectionComplete(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnected.mConnection);
+ CleanScanConfig();
+ }
+ break;
+ case DeviceEventType::kPlatformLinuxBLECentralConnectFailed:
+ if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting)
+ {
+ OnConnectionError(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnectFailed.mError);
+ CleanScanConfig();
+ }
+ break;
+ case DeviceEventType::kPlatformLinuxBLEWriteComplete:
+ HandleWriteConfirmation(apEvent->Platform.BLEWriteComplete.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX);
+ break;
+ case DeviceEventType::kPlatformLinuxBLESubscribeOpComplete:
+ if (apEvent->Platform.BLESubscribeOpComplete.mIsSubscribed)
+ HandleSubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID,
+ &ChipUUID_CHIPoBLEChar_TX);
+ else
+ HandleUnsubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID,
+ &ChipUUID_CHIPoBLEChar_TX);
+ break;
+ case DeviceEventType::kPlatformLinuxBLEIndicationReceived:
+ HandleIndicationReceived(apEvent->Platform.BLEIndicationReceived.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX,
+ PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData));
+ break;
+ case DeviceEventType::kPlatformLinuxBLEPeripheralAdvConfiguredComplete:
+ VerifyOrExit(apEvent->Platform.BLEPeripheralAdvConfiguredComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+ sInstance.mFlags.Set(Flags::kAdvertisingConfigured).Clear(Flags::kControlOpInProgress);
+ controlOpComplete = true;
+ ChipLogProgress(DeviceLayer, "CHIPoBLE advertising config complete");
+ break;
+ case DeviceEventType::kPlatformLinuxBLEPeripheralAdvStartComplete:
+ VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStartComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+ sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded);
+
+ if (!sInstance.mFlags.Has(Flags::kAdvertising))
+ {
+ sInstance.mFlags.Set(Flags::kAdvertising);
+ }
+
+ break;
+ case DeviceEventType::kPlatformLinuxBLEPeripheralAdvStopComplete:
+ VerifyOrExit(apEvent->Platform.BLEPeripheralAdvStopComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+
+ sInstance.mFlags.Clear(Flags::kControlOpInProgress).Clear(Flags::kAdvertisingRefreshNeeded);
+
+ // Transition to the not Advertising state...
+ if (sInstance.mFlags.Has(Flags::kAdvertising))
+ {
+ sInstance.mFlags.Clear(Flags::kAdvertising);
+ ChipLogProgress(DeviceLayer, "CHIPoBLE advertising stopped");
+ }
+ break;
+ case DeviceEventType::kPlatformLinuxBLEPeripheralRegisterAppComplete:
+ VerifyOrExit(apEvent->Platform.BLEPeripheralRegisterAppComplete.mIsSuccess, err = CHIP_ERROR_INCORRECT_STATE);
+ mFlags.Set(Flags::kAppRegistered);
+ controlOpComplete = true;
+ break;
+ default:
+ break;
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err));
+ mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
+ sInstance.mFlags.Clear(Flags::kControlOpInProgress);
+ }
+
+ if (controlOpComplete)
+ {
+ mFlags.Clear(Flags::kControlOpInProgress);
+ DriveBLEState();
+ }
+}
+
+uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const
+{
+ BluezConnection * connection = static_cast<BluezConnection *>(conId);
+ return (connection != nullptr) ? connection->mMtu : 0;
+}
+
+bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
+{
+ bool result = false;
+
+ VerifyOrExit(Ble::UUIDsMatch(svcId, &CHIP_BLE_SVC_ID),
+ ChipLogError(DeviceLayer, "SubscribeCharacteristic() called with invalid service ID"));
+ VerifyOrExit(Ble::UUIDsMatch(charId, &ChipUUID_CHIPoBLEChar_TX),
+ ChipLogError(DeviceLayer, "SubscribeCharacteristic() called with invalid characteristic ID"));
+
+ result = BluezSubscribeCharacteristic(conId);
+exit:
+ return result;
+}
+
+bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
+{
+ bool result = false;
+
+ VerifyOrExit(Ble::UUIDsMatch(svcId, &CHIP_BLE_SVC_ID),
+ ChipLogError(DeviceLayer, "UnsubscribeCharacteristic() called with invalid service ID"));
+ VerifyOrExit(Ble::UUIDsMatch(charId, &ChipUUID_CHIPoBLEChar_TX),
+ ChipLogError(DeviceLayer, "UnsubscribeCharacteristic() called with invalid characteristic ID"));
+
+ result = BluezUnsubscribeCharacteristic(conId);
+exit:
+ return result;
+}
+
+bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId)
+{
+ ChipLogProgress(DeviceLayer, "Closing BLE GATT connection (con %p)", conId);
+ return CloseBluezConnection(conId);
+}
+
+bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ chip::System::PacketBufferHandle pBuf)
+{
+ return SendBluezIndication(conId, std::move(pBuf));
+}
+
+bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ chip::System::PacketBufferHandle pBuf)
+{
+ bool result = false;
+
+ VerifyOrExit(Ble::UUIDsMatch(svcId, &CHIP_BLE_SVC_ID),
+ ChipLogError(DeviceLayer, "SendWriteRequest() called with invalid service ID"));
+ VerifyOrExit(Ble::UUIDsMatch(charId, &ChipUUID_CHIPoBLEChar_RX),
+ ChipLogError(DeviceLayer, "SendWriteRequest() called with invalid characteristic ID"));
+
+ result = BluezSendWriteRequest(conId, std::move(pBuf));
+exit:
+ return result;
+}
+
+bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ chip::System::PacketBufferHandle pBuf)
+{
+ ChipLogError(Ble, "SendReadRequest: Not implemented");
+ return true;
+}
+
+bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext,
+ const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId)
+{
+ ChipLogError(Ble, "SendReadRBluezonse: Not implemented");
+ return true;
+}
+
+void BLEManagerImpl::HandleNewConnection(BLE_CONNECTION_OBJECT conId)
+{
+ if (sInstance.mIsCentral)
+ {
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLECentralConnected;
+ event.Platform.BLECentralConnected.mConnection = conId;
+ PlatformMgr().PostEvent(&event);
+ }
+}
+
+void BLEManagerImpl::HandleConnectFailed(CHIP_ERROR error)
+{
+ if (sInstance.mIsCentral)
+ {
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLECentralConnectFailed;
+ event.Platform.BLECentralConnectFailed.mError = error;
+ PlatformMgr().PostEvent(&event);
+ }
+}
+
+void BLEManagerImpl::HandleWriteComplete(BLE_CONNECTION_OBJECT conId)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEWriteComplete;
+ event.Platform.BLEWriteComplete.mConnection = conId;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::HandleSubscribeOpComplete(BLE_CONNECTION_OBJECT conId, bool subscribed)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLESubscribeOpComplete;
+ event.Platform.BLESubscribeOpComplete.mConnection = conId;
+ event.Platform.BLESubscribeOpComplete.mIsSubscribed = subscribed;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::HandleTXCharChanged(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ System::PacketBufferHandle buf = System::PacketBufferHandle::NewWithData(value, len);
+
+ ChipLogProgress(DeviceLayer, "Indication received, conn = %p", conId);
+
+ VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
+
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEIndicationReceived;
+ event.Platform.BLEIndicationReceived.mConnection = conId;
+ event.Platform.BLEIndicationReceived.mData = std::move(buf).UnsafeRelease();
+ PlatformMgr().PostEvent(&event);
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ ChipLogError(DeviceLayer, "HandleTXCharChanged() failed: %s", ErrorStr(err));
+}
+
+void BLEManagerImpl::HandleRXCharWrite(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ System::PacketBufferHandle buf;
+
+ // Copy the data to a packet buffer.
+ buf = System::PacketBufferHandle::NewWithData(value, len);
+ VerifyOrExit(!buf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
+
+ // Post an event to the Chip queue to deliver the data into the Chip stack.
+ {
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kCHIPoBLEWriteReceived;
+ ChipLogProgress(Ble, "Write request received debug %p", conId);
+ event.CHIPoBLEWriteReceived.ConId = conId;
+ event.CHIPoBLEWriteReceived.Data = std::move(buf).UnsafeRelease();
+ PlatformMgr().PostEvent(&event);
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "HandleRXCharWrite() failed: %s", ErrorStr(err));
+ }
+}
+
+void BLEManagerImpl::CHIPoBluez_ConnectionClosed(BLE_CONNECTION_OBJECT conId)
+{
+ ChipLogProgress(DeviceLayer, "Bluez notify CHIPoBluez connection disconnected");
+
+ // If this was a CHIPoBLE connection, post an event to deliver a connection error to the CHIPoBLE layer.
+ {
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kCHIPoBLEConnectionError;
+ event.CHIPoBLEConnectionError.ConId = conId;
+ event.CHIPoBLEConnectionError.Reason = BLE_ERROR_REMOTE_DEVICE_DISCONNECTED;
+ PlatformMgr().PostEvent(&event);
+ }
+}
+
+void BLEManagerImpl::HandleTXCharCCCDWrite(BLE_CONNECTION_OBJECT conId)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ BluezConnection * connection = static_cast<BluezConnection *>(conId);
+
+ VerifyOrExit(connection != nullptr, ChipLogError(DeviceLayer, "Connection is NULL in HandleTXCharCCCDWrite"));
+ VerifyOrExit(connection->mpC2 != nullptr, ChipLogError(DeviceLayer, "C2 is NULL in HandleTXCharCCCDWrite"));
+
+ // Post an event to the Chip queue to process either a CHIPoBLE Subscribe or Unsubscribe based on
+ // whether the client is enabling or disabling indications.
+ {
+ ChipDeviceEvent event;
+ event.Type = connection->mIsNotify ? DeviceEventType::kCHIPoBLESubscribe : DeviceEventType::kCHIPoBLEUnsubscribe;
+ event.CHIPoBLESubscribe.ConId = connection;
+ PlatformMgr().PostEvent(&event);
+ }
+
+ ChipLogProgress(DeviceLayer, "CHIPoBLE %s received", connection->mIsNotify ? "subscribe" : "unsubscribe");
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "HandleTXCharCCCDWrite() failed: %s", ErrorStr(err));
+ // TODO: fail connection
+ }
+}
+
+void BLEManagerImpl::HandleTXComplete(BLE_CONNECTION_OBJECT conId)
+{
+ // Post an event to the Chip queue to process the indicate confirmation.
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kCHIPoBLEIndicateConfirm;
+ event.CHIPoBLEIndicateConfirm.ConId = conId;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::DriveBLEState()
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ // Perform any initialization actions that must occur after the Chip task is running.
+ if (!mFlags.Has(Flags::kAsyncInitCompleted))
+ {
+ mFlags.Set(Flags::kAsyncInitCompleted);
+
+ // If CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled,
+ // disable CHIPoBLE advertising if the device is fully provisioned.
+#if CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED
+ if (ConfigurationMgr().IsFullyProvisioned())
+ {
+ mFlags.Clear(Flags::kAdvertisingEnabled);
+ ChipLogProgress(DeviceLayer, "CHIPoBLE advertising disabled because device is fully provisioned");
+ }
+#endif // CHIP_DEVICE_CONFIG_CHIPOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED
+ ExitNow();
+ }
+
+ // If there's already a control operation in progress, wait until it completes.
+ VerifyOrExit(!mFlags.Has(Flags::kControlOpInProgress), /* */);
+
+ // Initializes the Bluez BLE layer if needed.
+ if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && !mFlags.Has(Flags::kBluezBLELayerInitialized))
+ {
+ err = InitBluezBleLayer(mIsCentral, nullptr, mBLEAdvConfig, mpEndpoint);
+ SuccessOrExit(err);
+ mFlags.Set(Flags::kBluezBLELayerInitialized);
+ }
+
+ // Register the CHIPoBLE application with the Bluez BLE layer if needed.
+ if (!mIsCentral && mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && !mFlags.Has(Flags::kAppRegistered))
+ {
+ err = BluezGattsAppRegister(mpEndpoint);
+ mFlags.Set(Flags::kControlOpInProgress);
+ ExitNow();
+ }
+
+ // If the application has enabled CHIPoBLE and BLE advertising...
+ if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_Enabled && mFlags.Has(Flags::kAdvertisingEnabled))
+ {
+ // Start/re-start advertising if not already advertising, or if the advertising state of the
+ // Bluez BLE layer needs to be refreshed.
+ if (!mFlags.Has(Flags::kAdvertising) || mFlags.Has(Flags::kAdvertisingRefreshNeeded))
+ {
+ mFlags.Clear(Flags::kAdvertisingRefreshNeeded);
+
+ // Configure advertising data if it hasn't been done yet. This is an asynchronous step which
+ // must complete before advertising can be started. When that happens, this method will
+ // be called again, and execution will proceed to the code below.
+ if (!mFlags.Has(Flags::kAdvertisingConfigured))
+ {
+ err = BluezAdvertisementSetup(mpEndpoint);
+ ExitNow();
+ }
+
+ // Start advertising. This is also an asynchronous step.
+ err = StartBLEAdvertising();
+ SuccessOrExit(err);
+
+ sInstance.mFlags.Set(Flags::kAdvertising);
+ ExitNow();
+ }
+ }
+
+ // Otherwise stop advertising if needed...
+ else
+ {
+ if (mFlags.Has(Flags::kAdvertising))
+ {
+ err = StopBLEAdvertising();
+ SuccessOrExit(err);
+ mFlags.Set(Flags::kControlOpInProgress);
+
+ ExitNow();
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err));
+ mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled;
+ }
+}
+
+void BLEManagerImpl::DriveBLEState(intptr_t arg)
+{
+ sInstance.DriveBLEState();
+}
+
+void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId)
+{
+ ChipLogProgress(Ble, "Got notification regarding chip connection closure");
+}
+
+void BLEManagerImpl::InitiateScan(BleScanState scanType)
+{
+ DriveBLEState();
+
+ if (scanType == BleScanState::kNotScanning)
+ {
+ OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE);
+ ChipLogError(Ble, "Invalid scan type requested");
+ return;
+ }
+
+ if (mpEndpoint == nullptr)
+ {
+ OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE);
+ ChipLogError(Ble, "BLE Layer is not yet initialized");
+ return;
+ }
+
+ if (mpEndpoint->mpAdapter == nullptr)
+ {
+ OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE);
+ ChipLogError(Ble, "No adapter available for new connection establishment");
+ return;
+ }
+
+ mDeviceScanner = Internal::ChipDeviceScanner::Create(mpEndpoint->mpAdapter, this);
+ mBLEScanConfig.mBleScanState = scanType;
+
+ if (!mDeviceScanner)
+ {
+ mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
+ OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INTERNAL);
+ ChipLogError(Ble, "Failed to create a BLE device scanner");
+ return;
+ }
+
+ CHIP_ERROR err = mDeviceScanner->StartScan(kNewConnectionScanTimeoutMs);
+ if (err != CHIP_NO_ERROR)
+ {
+ mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
+ ChipLogError(Ble, "Failed to start a BLE can: %s", chip::ErrorStr(err));
+ OnConnectionError(mBLEScanConfig.mAppState, err);
+ return;
+ }
+}
+
+void BLEManagerImpl::CleanScanConfig()
+{
+ if (mBLEScanConfig.mBleScanState == BleScanState::kConnecting)
+ DeviceLayer::SystemLayer.CancelTimer(HandleConnectTimeout, mpEndpoint);
+
+ mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
+}
+
+void BLEManagerImpl::InitiateScan(intptr_t arg)
+{
+ sInstance.InitiateScan(static_cast<BleScanState>(arg));
+}
+
+void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const uint16_t connDiscriminator)
+{
+ mBLEScanConfig.mDiscriminator = connDiscriminator;
+ mBLEScanConfig.mAppState = appState;
+
+ // Scan initiation performed async, to ensure that the BLE subsystem is initialized.
+ PlatformMgr().ScheduleWork(InitiateScan, static_cast<intptr_t>(BleScanState::kScanForDiscriminator));
+}
+
+BLE_ERROR BLEManagerImpl::CancelConnection()
+{
+ return BLE_ERROR_NOT_IMPLEMENTED;
+}
+
+void BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(bool aIsSuccess, void * apAppstate)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEPeripheralRegisterAppComplete;
+ event.Platform.BLEPeripheralRegisterAppComplete.mIsSuccess = aIsSuccess;
+ event.Platform.BLEPeripheralRegisterAppComplete.mpAppstate = apAppstate;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::NotifyBLEPeripheralAdvConfiguredComplete(bool aIsSuccess, void * apAppstate)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEPeripheralAdvConfiguredComplete;
+ event.Platform.BLEPeripheralAdvConfiguredComplete.mIsSuccess = aIsSuccess;
+ event.Platform.BLEPeripheralAdvConfiguredComplete.mpAppstate = apAppstate;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess, void * apAppstate)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStartComplete;
+ event.Platform.BLEPeripheralAdvStartComplete.mIsSuccess = aIsSuccess;
+ event.Platform.BLEPeripheralAdvStartComplete.mpAppstate = apAppstate;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess, void * apAppstate)
+{
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kPlatformLinuxBLEPeripheralAdvStopComplete;
+ event.Platform.BLEPeripheralAdvStopComplete.mIsSuccess = aIsSuccess;
+ event.Platform.BLEPeripheralAdvStopComplete.mpAppstate = apAppstate;
+ PlatformMgr().PostEvent(&event);
+}
+
+void BLEManagerImpl::OnDeviceScanned(BluezDevice1 * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info)
+{
+ ChipLogProgress(Ble, "New device scanned: %s", bluez_device1_get_address(device));
+
+ if (mBLEScanConfig.mBleScanState == BleScanState::kScanForDiscriminator)
+ {
+ if (info.GetDeviceDiscriminator() != mBLEScanConfig.mDiscriminator)
+ {
+ return;
+ }
+ ChipLogProgress(Ble, "Device discriminator match. Attempting to connect.");
+ }
+ else if (mBLEScanConfig.mBleScanState == BleScanState::kScanForAddress)
+ {
+ if (strcmp(bluez_device1_get_address(device), mBLEScanConfig.mAddress.c_str()) != 0)
+ {
+ return;
+ }
+ ChipLogProgress(Ble, "Device address match. Attempting to connect.");
+ }
+ else
+ {
+ // Internal consistency eerror
+ ChipLogError(Ble, "Unknown discovery type. Ignoring scanned device.");
+ return;
+ }
+
+ mBLEScanConfig.mBleScanState = BleScanState::kConnecting;
+ DeviceLayer::SystemLayer.StartTimer(kConnectTimeoutMs, HandleConnectTimeout, mpEndpoint);
+ mDeviceScanner->StopScan();
+
+ ConnectDevice(device, mpEndpoint);
+}
+
+void BLEManagerImpl::OnScanComplete()
+{
+ if (mBLEScanConfig.mBleScanState != BleScanState::kScanForDiscriminator &&
+ mBLEScanConfig.mBleScanState != BleScanState::kScanForAddress)
+ {
+ ChipLogProgress(Ble, "Scan complete notification without an active scan.");
+ return;
+ }
+
+ OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_TIMEOUT);
+ mBLEScanConfig.mBleScanState = BleScanState::kNotScanning;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the BLEManager singleton object
+ * for the Linux platforms.
+ */
+
+#pragma once
+
+#include <ble/BleLayer.h>
+#include <platform/internal/BLEManager.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include "bluez/ChipDeviceScanner.h"
+#include "bluez/Types.h"
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+void HandleIncomingBleConnection(Ble::BLEEndPoint * bleEP);
+
+struct BLEAdvConfig
+{
+ char * mpBleName;
+ uint32_t mAdapterId;
+ uint8_t mMajor;
+ uint8_t mMinor;
+ uint16_t mVendorId;
+ uint16_t mProductId;
+ uint64_t mDeviceId;
+ uint8_t mPairingStatus;
+ ChipAdvType mType;
+ uint16_t mDuration;
+ const char * mpAdvertisingUUID;
+};
+
+enum class BleScanState : uint8_t
+{
+ kNotScanning,
+ kScanForDiscriminator,
+ kScanForAddress,
+ kConnecting,
+};
+
+struct BLEScanConfig
+{
+ // If an active scan for connection is being performed
+ BleScanState mBleScanState = BleScanState::kNotScanning;
+
+ // If scanning by discriminator, what are we scanning for
+ uint16_t mDiscriminator = 0;
+
+ // If scanning by address, what address are we searching for
+ std::string mAddress;
+
+ // Optional argument to be passed to callback functions provided by the BLE scan/connect requestor
+ void * mAppState = nullptr;
+};
+
+/**
+ * Concrete implementation of the BLEManagerImpl singleton object for the Linux platforms.
+ */
+class BLEManagerImpl final : public BLEManager,
+ private Ble::BleLayer,
+ private Ble::BlePlatformDelegate,
+ private Ble::BleApplicationDelegate,
+ private Ble::BleConnectionDelegate,
+ private ChipDeviceScannerDelegate
+{
+ // Allow the BLEManager interface class to delegate method calls to
+ // the implementation methods provided by this class.
+ friend BLEManager;
+
+public:
+ CHIP_ERROR ConfigureBle(uint32_t aAdapterId, bool aIsCentral);
+
+ // Driven by BlueZ IO
+ static void HandleNewConnection(BLE_CONNECTION_OBJECT conId);
+ static void HandleConnectFailed(CHIP_ERROR error);
+ static void HandleWriteComplete(BLE_CONNECTION_OBJECT conId);
+ static void HandleSubscribeOpComplete(BLE_CONNECTION_OBJECT conId, bool subscribed);
+ static void HandleTXCharChanged(BLE_CONNECTION_OBJECT conId, const uint8_t * value, size_t len);
+ static void HandleRXCharWrite(BLE_CONNECTION_OBJECT user_data, const uint8_t * value, size_t len);
+ static void CHIPoBluez_ConnectionClosed(BLE_CONNECTION_OBJECT user_data);
+ static void HandleTXCharCCCDWrite(BLE_CONNECTION_OBJECT user_data);
+ static void HandleTXComplete(BLE_CONNECTION_OBJECT user_data);
+
+ static void NotifyBLEPeripheralRegisterAppComplete(bool aIsSuccess, void * apAppstate);
+ static void NotifyBLEPeripheralAdvConfiguredComplete(bool aIsSuccess, void * apAppstate);
+ static void NotifyBLEPeripheralAdvStartComplete(bool aIsSuccess, void * apAppstate);
+ static void NotifyBLEPeripheralAdvStopComplete(bool aIsSuccess, void * apAppstate);
+
+private:
+ // ===== Members that implement the BLEManager internal interface.
+
+ CHIP_ERROR _Init();
+ CHIPoBLEServiceMode _GetCHIPoBLEServiceMode();
+ CHIP_ERROR _SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val);
+ bool _IsAdvertisingEnabled();
+ CHIP_ERROR _SetAdvertisingEnabled(bool val);
+ bool _IsAdvertising();
+ CHIP_ERROR _SetAdvertisingMode(BLEAdvertisingMode mode);
+ CHIP_ERROR _GetDeviceName(char * buf, size_t bufSize);
+ CHIP_ERROR _SetDeviceName(const char * deviceName);
+ uint16_t _NumConnections();
+
+ void _OnPlatformEvent(const ChipDeviceEvent * event);
+ void HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * event);
+ BleLayer * _GetBleLayer();
+
+ // ===== Members that implement virtual methods on BlePlatformDelegate.
+
+ bool SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId,
+ const Ble::ChipBleUUID * charId) override;
+ bool UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId,
+ const Ble::ChipBleUUID * charId) override;
+ bool CloseConnection(BLE_CONNECTION_OBJECT conId) override;
+ uint16_t GetMTU(BLE_CONNECTION_OBJECT conId) const override;
+ bool SendIndication(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ System::PacketBufferHandle pBuf) override;
+ bool SendWriteRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ System::PacketBufferHandle pBuf) override;
+ bool SendReadRequest(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId,
+ System::PacketBufferHandle pBuf) override;
+ bool SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext, const Ble::ChipBleUUID * svcId,
+ const Ble::ChipBleUUID * charId) override;
+
+ // ===== Members that implement virtual methods on BleApplicationDelegate.
+
+ void NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) override;
+
+ // ===== Members that implement virtual methods on BleConnectionDelegate.
+
+ void NewConnection(BleLayer * bleLayer, void * appState, uint16_t connDiscriminator) override;
+ BLE_ERROR CancelConnection() override;
+
+ // ===== Members that implement virtual methods on ChipDeviceScannerDelegate
+ void OnDeviceScanned(BluezDevice1 * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) override;
+ void OnScanComplete() override;
+
+ // ===== Members for internal use by the following friends.
+
+ friend BLEManager & BLEMgr();
+ friend BLEManagerImpl & BLEMgrImpl();
+
+ static BLEManagerImpl sInstance;
+
+ // ===== Private members reserved for use by this class only.
+ enum class Flags : uint16_t
+ {
+ kAsyncInitCompleted = 0x0001, /**< One-time asynchronous initialization actions have been performed. */
+ kBluezBLELayerInitialized = 0x0002, /**< The Bluez layer has been initialized. */
+ kAppRegistered = 0x0004, /**< The CHIPoBLE application has been registered with the Bluez layer. */
+ kAdvertisingConfigured = 0x0008, /**< CHIPoBLE advertising has been configured in the Bluez layer. */
+ kAdvertising = 0x0010, /**< The system is currently CHIPoBLE advertising. */
+ kControlOpInProgress = 0x0020, /**< An async control operation has been issued to the ESP BLE layer. */
+ kAdvertisingEnabled = 0x0040, /**< The application has enabled CHIPoBLE advertising. */
+ kFastAdvertisingEnabled = 0x0080, /**< The application has enabled fast advertising. */
+ kUseCustomDeviceName = 0x0100, /**< The application has configured a custom BLE device name. */
+ kAdvertisingRefreshNeeded = 0x0200, /**< The advertising configuration/state in BLE layer needs to be updated. */
+ };
+
+ enum
+ {
+ kMaxConnections = 1, // TODO: right max connection
+ kMaxDeviceNameLength = 20, // TODO: right-size this
+ kMaxAdvertismentDataSetSize = 31 // TODO: verify this
+ };
+
+ CHIP_ERROR StartBLEAdvertising();
+ CHIP_ERROR StopBLEAdvertising();
+
+ void DriveBLEState();
+ static void DriveBLEState(intptr_t arg);
+
+ void InitiateScan(BleScanState scanType);
+ static void InitiateScan(intptr_t arg);
+ void CleanScanConfig();
+
+ CHIPoBLEServiceMode mServiceMode;
+ BLEAdvConfig mBLEAdvConfig;
+ BLEScanConfig mBLEScanConfig;
+ BitFlags<Flags> mFlags;
+ char mDeviceName[kMaxDeviceNameLength + 1];
+ bool mIsCentral = false;
+ BluezEndpoint * mpEndpoint = nullptr;
+ std::unique_ptr<ChipDeviceScanner> mDeviceScanner;
+};
+
+/**
+ * Returns a reference to the public interface of the BLEManager singleton object.
+ *
+ * Internal components should use this to access features of the BLEManager object
+ * that are common to all platforms.
+ */
+inline BLEManager & BLEMgr()
+{
+ return BLEManagerImpl::sInstance;
+}
+
+/**
+ * Returns the platform-specific implementation of the BLEManager singleton object.
+ *
+ * Internal components can use this to gain access to features of the BLEManager
+ * that are specific to the Linux platforms.
+ */
+inline BLEManagerImpl & BLEMgrImpl()
+{
+ return BLEManagerImpl::sInstance;
+}
+
+inline Ble::BleLayer * BLEManagerImpl::_GetBleLayer()
+{
+ return this;
+}
+
+inline BLEManager::CHIPoBLEServiceMode BLEManagerImpl::_GetCHIPoBLEServiceMode()
+{
+ return mServiceMode;
+}
+
+inline bool BLEManagerImpl::_IsAdvertisingEnabled()
+{
+ return mFlags.Has(Flags::kAdvertisingEnabled);
+}
+
+inline bool BLEManagerImpl::_IsAdvertising()
+{
+ return mFlags.Has(Flags::kAdvertising);
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific configuration overrides for the CHIP BLE
+ * Layer on Linux platforms.
+ *
+ */
+
+#pragma once
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+struct BluezConnection;
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+// ==================== Platform Adaptations ====================
+#define BLE_CONNECTION_UNINITIALIZED nullptr
+// ========== Platform-specific Configuration Overrides =========
+
+/* none so far */
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific configuration overrides for the chip Device Layer
+ * on Linux platforms.
+ */
+
+#pragma once
+
+// ==================== Platform Adaptations ====================
+
+#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 0
+#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0
+
+#ifndef CHIP_DEVICE_CONFIG_ENABLE_THREAD
+#define CHIP_DEVICE_CONFIG_ENABLE_THREAD CHIP_ENABLE_OPENTHREAD
+#endif
+
+#ifndef CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+#define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 0
+#endif
+
+#define CHIP_DEVICE_CONFIG_ENABLE_CHIP_TIME_SERVICE_TIME_SYNC 0
+
+#define CHIP_DEVICE_CONFIG_PERSISTED_STORAGE_CRIT_EIDC_KEY 2
+#define CHIP_DEVICE_CONFIG_PERSISTED_STORAGE_PROD_EIDC_KEY 3
+#define CHIP_DEVICE_CONFIG_PERSISTED_STORAGE_INFO_EIDC_KEY 4
+#define CHIP_DEVICE_CONFIG_PERSISTED_STORAGE_DEBUG_EIDC_KEY 5
+
+// ========== Platform-specific Configuration =========
+
+// These are configuration options that are unique to Linux platforms.
+// These can be overridden by the application as needed.
+
+/**
+ * @def CHIP_DEVICE_LAYER_BLE_OBSERVER_PRIORITY
+ *
+ * The priority of the SoftDevice observer event handler registered by the
+ * CHIP BleLayer.
+ */
+#ifndef CHIP_DEVICE_LAYER_BLE_OBSERVER_PRIORITY
+#define CHIP_DEVICE_LAYER_BLE_OBSERVER_PRIORITY 3
+#endif // CHIP_DEVICE_LAYER_BLE_OBSERVER_PRIORITY
+
+/**
+ * @def CHIP_DEVICE_LAYER_BLE_CONN_CFG_TAG
+ *
+ * The SoftDevice BLE connection configuration tag used by the CHIP
+ * BleLayer.
+ */
+#ifndef CHIP_DEVICE_LAYER_BLE_CONN_CFG_TAG
+#define CHIP_DEVICE_LAYER_BLE_CONN_CFG_TAG 1
+#endif // CHIP_DEVICE_LAYER_BLE_CONN_CFG_TAG
+
+// ========== Platform-specific Configuration Overrides =========
+
+#ifndef CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE
+#define CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE 8192
+#endif // CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE
+
+#ifndef CHIP_DEVICE_CONFIG_THREAD_TASK_STACK_SIZE
+#define CHIP_DEVICE_CONFIG_THREAD_TASK_STACK_SIZE 8192
+#endif // CHIP_DEVICE_CONFIG_THREAD_TASK_STACK_SIZE
+
+#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_TELEMETRY 0
+#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_TELEMETRY 0
+#define CHIP_DEVICE_CONFIG_ENABLE_THREAD_TELEMETRY_FULL 0
+
+// TODO: CHIP has redesigned the crypto interface, pending on the final version of CHIP HASH APIs
+#define CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH 0
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Defines platform-specific event types and data for the chip
+ * Device Layer on Linux platforms.
+ */
+
+#pragma once
+
+#include <platform/CHIPDeviceEvent.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+namespace DeviceEventType {
+
+/**
+ * Enumerates Linux platform-specific event types that are visible to the application.
+ */
+enum PublicPlatformSpecificEventTypes
+{
+ /* None currently defined */
+};
+
+/**
+ * Enumerates Linux platform-specific event types that are internal to the chip Device Layer.
+ */
+enum InternalPlatformSpecificEventTypes
+{
+ kPlatformLinuxEvent = kRange_InternalPlatformSpecific,
+ kPlatformLinuxBLECentralConnected,
+ kPlatformLinuxBLECentralConnectFailed,
+ kPlatformLinuxBLEWriteComplete,
+ kPlatformLinuxBLESubscribeOpComplete,
+ kPlatformLinuxBLEIndicationReceived,
+ kPlatformLinuxBLEC1WriteEvent,
+ kPlatformLinuxBLEOutOfBuffersEvent,
+ kPlatformLinuxBLEPeripheralRegisterAppComplete,
+ kPlatformLinuxBLEPeripheralAdvConfiguredComplete,
+ kPlatformLinuxBLEPeripheralAdvStartComplete,
+ kPlatformLinuxBLEPeripheralAdvStopComplete
+};
+
+} // namespace DeviceEventType
+
+/**
+ * Represents platform-specific event information for Linux platforms.
+ */
+struct ChipDevicePlatformEvent
+{
+ union
+ {
+ struct
+ {
+ BLE_CONNECTION_OBJECT mConnection;
+ } BLECentralConnected;
+ struct
+ {
+ CHIP_ERROR mError;
+ } BLECentralConnectFailed;
+ struct
+ {
+ BLE_CONNECTION_OBJECT mConnection;
+ } BLEWriteComplete;
+ struct
+ {
+ BLE_CONNECTION_OBJECT mConnection;
+ bool mIsSubscribed;
+ } BLESubscribeOpComplete;
+ struct
+ {
+ BLE_CONNECTION_OBJECT mConnection;
+ chip::System::PacketBuffer * mData;
+ } BLEIndicationReceived;
+ struct
+ {
+ bool mIsSuccess;
+ void * mpAppstate;
+ } BLEPeripheralRegisterAppComplete;
+ struct
+ {
+ bool mIsSuccess;
+ void * mpAppstate;
+ } BLEPeripheralAdvConfiguredComplete;
+ struct
+ {
+ bool mIsSuccess;
+ void * mpAppstate;
+ } BLEPeripheralAdvStartComplete;
+ struct
+ {
+ bool mIsSuccess;
+ void * mpAppstate;
+ } BLEPeripheralAdvStopComplete;
+ };
+};
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * This file implements a class for managing client application
+ * user-editable settings on Linux platform.
+ *
+ */
+
+#include <errno.h>
+#include <fstream>
+#include <inttypes.h>
+#include <libgen.h>
+#include <string>
+#include <unistd.h>
+
+#include <platform/Linux/CHIPLinuxStorage.h>
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+#include <support/Base64.h>
+#include <support/CHIPMem.h>
+#include <support/CodeUtils.h>
+#include <support/ScopedBuffer.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+ChipLinuxStorage::ChipLinuxStorage()
+{
+ mDirty = false;
+}
+
+ChipLinuxStorage::~ChipLinuxStorage() {}
+
+CHIP_ERROR ChipLinuxStorage::Init(const char * configFile)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mConfigPath.assign(configFile);
+ retval = ChipLinuxStorageIni::Init();
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ std::ifstream ifs;
+
+ ifs.open(configFile, std::ifstream::in);
+
+ // Create default setting file if not exist.
+ if (!ifs.good())
+ {
+ mDirty = true;
+ retval = Commit();
+ mDirty = false;
+ }
+ }
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ retval = ChipLinuxStorageIni::AddConfig(mConfigPath);
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, bool & val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ uint32_t result;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::GetUIntValue(key, result);
+ val = (result != 0);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint32_t & val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::GetUIntValue(key, val);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint64_t & val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::GetUInt64Value(key, val);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ReadValueStr(const char * key, char * buf, size_t bufSize, size_t & outLen)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::GetStringValue(key, buf, bufSize, outLen);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ReadValueBin(const char * key, uint8_t * buf, size_t bufSize, size_t & outLen)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::GetBinaryBlobValue(key, buf, bufSize, outLen);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, bool val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ if (val)
+ {
+ retval = WriteValue(key, static_cast<uint32_t>(1));
+ }
+ else
+ {
+ retval = WriteValue(key, static_cast<uint32_t>(0));
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint32_t val)
+{
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%d", val);
+
+ return WriteValueStr(key, buf);
+}
+
+CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint64_t val)
+{
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%" PRIu64, val);
+
+ return WriteValueStr(key, buf);
+}
+
+CHIP_ERROR ChipLinuxStorage::WriteValueStr(const char * key, const char * val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::AddEntry(key, val);
+
+ mDirty = true;
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::WriteValueBin(const char * key, const uint8_t * data, size_t dataLen)
+{
+ static const size_t kMaxBlobSize = 5 * 1024;
+
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ chip::Platform::ScopedMemoryBuffer<char> encodedData;
+ size_t encodedDataLen = 0;
+ size_t expectedEncodedLen = ((dataLen + 3) * 4) / 3;
+
+ // We only support encoding blobs up to 5kb
+ if (dataLen > kMaxBlobSize)
+ {
+ retval = CHIP_ERROR_INVALID_ARGUMENT;
+ }
+
+ // Compute our expectedEncodedLen
+ // Allocate just enough space for the encoded data, and the NULL terminator
+ if (retval == CHIP_NO_ERROR)
+ {
+ if (!encodedData.Alloc(expectedEncodedLen + 1))
+ {
+ retval = CHIP_ERROR_NO_MEMORY;
+ }
+ }
+
+ // Encode it
+ if (retval == CHIP_NO_ERROR)
+ {
+ // We tested above that dataLen is no more than kMaxBlobSize.
+ static_assert(kMaxBlobSize < UINT16_MAX, "dataLen won't fit");
+ encodedDataLen = Base64Encode(data, static_cast<uint16_t>(dataLen), encodedData.Get());
+ encodedData[encodedDataLen] = 0;
+ }
+
+ // Store it
+ if (retval == CHIP_NO_ERROR)
+ {
+ WriteValueStr(key, encodedData.Get());
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ClearValue(const char * key)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::RemoveEntry(key);
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ mDirty = true;
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::ClearAll()
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::RemoveAll();
+
+ mLock.unlock();
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ mDirty = true;
+ retval = Commit();
+ }
+ else
+ {
+ retval = CHIP_ERROR_WRITE_FAILED;
+ }
+
+ return retval;
+}
+
+bool ChipLinuxStorage::HasValue(const char * key)
+{
+ bool retval;
+
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::HasValue(key);
+
+ mLock.unlock();
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorage::Commit()
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ if (mDirty && !mConfigPath.empty())
+ {
+ mLock.lock();
+
+ retval = ChipLinuxStorageIni::CommitConfig(mConfigPath);
+
+ mLock.unlock();
+ }
+ else
+ {
+ retval = CHIP_ERROR_WRITE_FAILED;
+ }
+
+ return retval;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * This file defines a class for managing client application
+ * user-editable settings. CHIP settings are partitioned into two
+ * distinct areas:
+ *
+ * 1. immutable / durable: factory parameters (CHIP_DEFAULT_FACTORY_PATH)
+ * 2. mutable / ephemeral: user parameters (CHIP_DEFAULT_CONFIG_PATH/CHIP_DEFAULT_DATA_PATH)
+ *
+ * The ephemeral partition should be erased during factory reset.
+ *
+ * ChipLinuxStorage wraps the storage class ChipLinuxStorageIni with mutex.
+ *
+ */
+
+#pragma once
+
+#include <mutex>
+#include <platform/Linux/CHIPLinuxStorageIni.h>
+
+#ifndef FATCONFDIR
+#define FATCONFDIR "/tmp"
+#endif
+
+#ifndef SYSCONFDIR
+#define SYSCONFDIR "/tmp"
+#endif
+
+#ifndef LOCALSTATEDIR
+#define LOCALSTATEDIR "/tmp"
+#endif
+
+#define CHIP_DEFAULT_FACTORY_PATH \
+ FATCONFDIR "/" \
+ "chip_factory.ini"
+#define CHIP_DEFAULT_CONFIG_PATH \
+ SYSCONFDIR "/" \
+ "chip_config.ini"
+#define CHIP_DEFAULT_DATA_PATH \
+ LOCALSTATEDIR "/" \
+ "chip_counters.ini"
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+class ChipLinuxStorage : private ChipLinuxStorageIni
+{
+public:
+ ChipLinuxStorage();
+ ~ChipLinuxStorage();
+
+ CHIP_ERROR Init(const char * configFile);
+ CHIP_ERROR ReadValue(const char * key, bool & val);
+ CHIP_ERROR ReadValue(const char * key, uint32_t & val);
+ CHIP_ERROR ReadValue(const char * key, uint64_t & val);
+ CHIP_ERROR ReadValueStr(const char * key, char * buf, size_t bufSize, size_t & outLen);
+ CHIP_ERROR ReadValueBin(const char * key, uint8_t * buf, size_t bufSize, size_t & outLen);
+ CHIP_ERROR WriteValue(const char * key, bool val);
+ CHIP_ERROR WriteValue(const char * key, uint32_t val);
+ CHIP_ERROR WriteValue(const char * key, uint64_t val);
+ CHIP_ERROR WriteValueStr(const char * key, const char * val);
+ CHIP_ERROR WriteValueBin(const char * key, const uint8_t * data, size_t dataLen);
+ CHIP_ERROR ClearValue(const char * key);
+ CHIP_ERROR ClearAll();
+ CHIP_ERROR Commit();
+ bool HasValue(const char * key);
+
+private:
+ std::mutex mLock;
+ bool mDirty;
+ std::string mConfigPath;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the Configuration key-value store object
+ * using IniPP on Linux platform.
+ *
+ */
+
+#include <fstream>
+#include <string>
+#include <unistd.h>
+
+#include <platform/Linux/CHIPLinuxStorageIni.h>
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+#include <support/Base64.h>
+#include <support/CHIPMem.h>
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+CHIP_ERROR ChipLinuxStorageIni::Init()
+{
+ return RemoveAll();
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetDefaultSection(std::map<std::string, std::string> & section)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ auto it = mConfigStore.sections.find("DEFAULT");
+
+ if (it != mConfigStore.sections.end())
+ {
+ section = mConfigStore.sections["DEFAULT"];
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::AddConfig(const std::string & configFile)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ std::ifstream ifs;
+
+ ifs.open(configFile, std::ifstream::in);
+
+ if (ifs.is_open())
+ {
+ mConfigStore.parse(ifs);
+ ifs.close();
+ }
+ else
+ {
+ ChipLogError(DeviceLayer, "Failed to open config file: %s", configFile.c_str());
+ retval = CHIP_ERROR_OPEN_FAILED;
+ }
+
+ return retval;
+}
+
+// Updating a file atomically and durably on Linux requires:
+// 1. Writing to a temporary file
+// 2. Sync'ing the temp file to commit updated data
+// 3. Using rename() to overwrite the existing file
+CHIP_ERROR ChipLinuxStorageIni::CommitConfig(const std::string & configFile)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ std::ofstream ofs;
+ std::string tmpPath = configFile;
+
+ tmpPath.append(".tmp");
+
+ ofs.open(tmpPath, std::ofstream::out | std::ofstream::trunc);
+
+ if (ofs.is_open())
+ {
+ ChipLogProgress(DeviceLayer, "writing settings to file (%s)", tmpPath.c_str());
+
+ mConfigStore.generate(ofs);
+ ofs.close();
+
+ if (rename(tmpPath.c_str(), configFile.c_str()) == 0)
+ {
+ ChipLogError(DeviceLayer, "renamed tmp file to file (%s)", configFile.c_str());
+ }
+ else
+ {
+ ChipLogError(DeviceLayer, "failed to rename (%s), %s (%d)", tmpPath.c_str(), strerror(errno), errno);
+ retval = CHIP_ERROR_WRITE_FAILED;
+ }
+ }
+ else
+ {
+ ChipLogError(DeviceLayer, "failed to open file (%s) for writing", tmpPath.c_str());
+ retval = CHIP_ERROR_OPEN_FAILED;
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetUIntValue(const char * key, uint32_t & val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ std::map<std::string, std::string> section;
+
+ retval = GetDefaultSection(section);
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ auto it = section.find(key);
+
+ if (it != section.end())
+ {
+ if (!inipp::extract(section[key], val))
+ {
+ retval = CHIP_ERROR_INVALID_ARGUMENT;
+ }
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetUInt64Value(const char * key, uint64_t & val)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ std::map<std::string, std::string> section;
+
+ retval = GetDefaultSection(section);
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ auto it = section.find(key);
+
+ if (it != section.end())
+ {
+ if (!inipp::extract(section[key], val))
+ {
+ retval = CHIP_ERROR_INVALID_ARGUMENT;
+ }
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetStringValue(const char * key, char * buf, size_t bufSize, size_t & outLen)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ std::map<std::string, std::string> section;
+
+ retval = GetDefaultSection(section);
+
+ if (retval == CHIP_NO_ERROR)
+ {
+ auto it = section.find(key);
+
+ if (it != section.end())
+ {
+ std::string value;
+ if (inipp::extract(section[key], value))
+ {
+ size_t len = value.size();
+
+ if (len > bufSize - 1)
+ {
+ outLen = len;
+ retval = CHIP_ERROR_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ outLen = value.copy(buf, len);
+ buf[outLen] = '\0';
+ }
+ }
+ else
+ {
+ retval = CHIP_ERROR_INVALID_ARGUMENT;
+ }
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobDataAndLengths(const char * key,
+ chip::Platform::ScopedMemoryBuffer<char> & encodedData,
+ size_t & encodedDataLen, size_t & decodedDataLen)
+{
+ size_t encodedDataPaddingLen = 0;
+ std::map<std::string, std::string> section;
+ CHIP_ERROR err = GetDefaultSection(section);
+ if (err != CHIP_NO_ERROR)
+ {
+ return err;
+ }
+
+ auto it = section.find(key);
+ if (it == section.end())
+ {
+ return CHIP_ERROR_KEY_NOT_FOUND;
+ }
+
+ std::string value;
+
+ // Compute the expectedDecodedLen
+ if (!inipp::extract(section[key], value))
+ {
+ return CHIP_ERROR_INVALID_ARGUMENT;
+ }
+
+ size_t len = value.size();
+ if (!encodedData.Alloc(len + 1))
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+ encodedDataLen = value.copy(encodedData.Get(), len);
+ encodedData[encodedDataLen] = '\0';
+
+ // Check if encoded data was padded. Only "=" or "==" padding combinations are allowed.
+ if ((encodedDataLen > 0) && (encodedData[encodedDataLen - 1] == '='))
+ {
+ encodedDataPaddingLen++;
+ if ((encodedDataLen > 1) && (encodedData[encodedDataLen - 2] == '='))
+ encodedDataPaddingLen++;
+ }
+
+ decodedDataLen = ((encodedDataLen - encodedDataPaddingLen) * 3) / 4;
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobValue(const char * key, uint8_t * decodedData, size_t bufSize, size_t & decodedDataLen)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+ chip::Platform::ScopedMemoryBuffer<char> encodedData;
+ size_t encodedDataLen;
+ size_t expectedDecodedLen = 0;
+
+ retval = GetBinaryBlobDataAndLengths(key, encodedData, encodedDataLen, expectedDecodedLen);
+
+ // Check the size
+ if (retval != CHIP_NO_ERROR)
+ {
+ return retval;
+ }
+
+ if (expectedDecodedLen > bufSize)
+ {
+ decodedDataLen = expectedDecodedLen;
+ return CHIP_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (encodedDataLen > UINT16_MAX)
+ {
+ // We can't even pass this length into Base64Decode.
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ // Decode it
+ // Cast is safe because we checked encodedDataLen above.
+ decodedDataLen = Base64Decode(encodedData.Get(), static_cast<uint16_t>(encodedDataLen), decodedData);
+ if (decodedDataLen == UINT16_MAX || decodedDataLen > expectedDecodedLen)
+ {
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+bool ChipLinuxStorageIni::HasValue(const char * key)
+{
+ std::map<std::string, std::string> section;
+
+ if (GetDefaultSection(section) != CHIP_NO_ERROR)
+ return false;
+
+ auto it = section.find(key);
+
+ return it != section.end();
+}
+
+CHIP_ERROR ChipLinuxStorageIni::AddEntry(const char * key, const char * value)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ if ((key != nullptr) && (value != nullptr))
+ {
+ std::map<std::string, std::string> & section = mConfigStore.sections["DEFAULT"];
+ section[key] = std::string(value);
+ }
+ else
+ {
+ ChipLogError(DeviceLayer, "Invalid input argument, failed to add entry");
+ retval = CHIP_ERROR_INVALID_ARGUMENT;
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::RemoveEntry(const char * key)
+{
+ CHIP_ERROR retval = CHIP_NO_ERROR;
+
+ std::map<std::string, std::string> & section = mConfigStore.sections["DEFAULT"];
+
+ auto it = section.find(key);
+
+ if (it != section.end())
+ {
+ section.erase(it);
+ }
+ else
+ {
+ retval = CHIP_ERROR_KEY_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+CHIP_ERROR ChipLinuxStorageIni::RemoveAll()
+{
+ mConfigStore.clear();
+
+ return CHIP_NO_ERROR;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the Configuration key-value store interface
+ * using IniPP.
+ *
+ */
+
+#pragma once
+
+#include <inipp/inipp.h>
+#include <platform/PersistedStorage.h>
+#include <support/ScopedBuffer.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+class ChipLinuxStorageIni
+{
+public:
+ CHIP_ERROR Init();
+ CHIP_ERROR AddConfig(const std::string & configFile);
+ CHIP_ERROR CommitConfig(const std::string & configFile);
+ CHIP_ERROR GetUIntValue(const char * key, uint32_t & val);
+ CHIP_ERROR GetUInt64Value(const char * key, uint64_t & val);
+ CHIP_ERROR GetStringValue(const char * key, char * buf, size_t bufSize, size_t & outLen);
+ CHIP_ERROR GetBinaryBlobValue(const char * key, uint8_t * decodedData, size_t bufSize, size_t & decodedDataLen);
+ bool HasValue(const char * key);
+
+protected:
+ CHIP_ERROR AddEntry(const char * key, const char * value);
+ CHIP_ERROR RemoveEntry(const char * key);
+ CHIP_ERROR RemoveAll();
+
+private:
+ CHIP_ERROR GetDefaultSection(std::map<std::string, std::string> & section);
+ CHIP_ERROR GetBinaryBlobDataAndLengths(const char * key, chip::Platform::ScopedMemoryBuffer<char> & encodedData,
+ size_t & encodedDataLen, size_t & decodedDataLen);
+ inipp::Ini<char> mConfigStore;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific configuration overrides for CHIP on
+ * Linux platforms.
+ */
+
+#pragma once
+
+// ==================== General Platform Adaptations ====================
+
+#define ChipDie() abort()
+
+// TODO:(#756) Add FabricState support
+#define CHIP_CONFIG_ENABLE_FABRIC_STATE 0
+
+using CHIP_CONFIG_PERSISTED_STORAGE_KEY_TYPE = const char *;
+#define CHIP_CONFIG_PERSISTED_STORAGE_MAX_KEY_LENGTH 16
+
+#define CHIP_CONFIG_LIFETIIME_PERSISTED_COUNTER_KEY "life-count"
+
+#define CHIP_CONFIG_TIME_ENABLE_CLIENT 1
+#define CHIP_CONFIG_TIME_ENABLE_SERVER 0
+
+// ==================== Security Adaptations ====================
+
+#define CHIP_CONFIG_USE_OPENSSL_ECC 0
+#define CHIP_CONFIG_USE_MICRO_ECC 0
+
+#define CHIP_CONFIG_HASH_IMPLEMENTATION_OPENSSL 0
+#define CHIP_CONFIG_HASH_IMPLEMENTATION_MINCRYPT 1
+#define CHIP_CONFIG_HASH_IMPLEMENTATION_MBEDTLS 0
+#define CHIP_CONFIG_HASH_IMPLEMENTATION_PLATFORM 0
+
+#define CHIP_CONFIG_AES_IMPLEMENTATION_OPENSSL 0
+#define CHIP_CONFIG_AES_IMPLEMENTATION_AESNI 0
+#define CHIP_CONFIG_AES_IMPLEMENTATION_MBEDTLS 1
+#define CHIP_CONFIG_AES_IMPLEMENTATION_PLATFORM 0
+
+#define CHIP_CONFIG_RNG_IMPLEMENTATION_OPENSSL 0
+#define CHIP_CONFIG_RNG_IMPLEMENTATION_CHIPDRBG 1
+#define CHIP_CONFIG_RNG_IMPLEMENTATION_PLATFORM 0
+
+#define CHIP_CONFIG_ENABLE_PASE_INITIATOR 0
+#define CHIP_CONFIG_ENABLE_PASE_RESPONDER 1
+#define CHIP_CONFIG_ENABLE_CASE_INITIATOR 1
+
+#define CHIP_CONFIG_SUPPORT_PASE_CONFIG0 0
+#define CHIP_CONFIG_SUPPORT_PASE_CONFIG1 0
+#define CHIP_CONFIG_SUPPORT_PASE_CONFIG2 0
+#define CHIP_CONFIG_SUPPORT_PASE_CONFIG3 0
+#define CHIP_CONFIG_SUPPORT_PASE_CONFIG4 1
+
+#define CHIP_CONFIG_ENABLE_KEY_EXPORT_INITIATOR 0
+
+#define CHIP_CONFIG_ENABLE_PROVISIONING_BUNDLE_SUPPORT 0
+
+// ==================== General Configuration Overrides ====================
+
+#ifndef CHIP_CONFIG_MAX_PEER_NODES
+#define CHIP_CONFIG_MAX_PEER_NODES 16
+#endif // CHIP_CONFIG_MAX_PEER_NODES
+
+#ifndef CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS
+#define CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS 16
+#endif // CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS
+
+#ifndef CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS
+#define CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS 8
+#endif // CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS
+
+#ifndef CHIP_CONFIG_RMP_TIMER_DEFAULT_PERIOD_SHIFT
+#define CHIP_CONFIG_RMP_TIMER_DEFAULT_PERIOD_SHIFT 6
+#endif // CHIP_CONFIG_RMP_TIMER_DEFAULT_PERIOD_SHIFT
+
+#ifndef CHIP_LOG_FILTERING
+#define CHIP_LOG_FILTERING 0
+#endif // CHIP_LOG_FILTERING
+
+#ifndef CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS
+#define CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS 1
+#endif // CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS
+
+// ==================== Security Configuration Overrides ====================
+
+#ifndef CHIP_CONFIG_MAX_APPLICATION_GROUPS
+#define CHIP_CONFIG_MAX_APPLICATION_GROUPS 4
+#endif // CHIP_CONFIG_MAX_APPLICATION_GROUPS
+
+#ifndef CHIP_CONFIG_DEBUG_CERT_VALIDATION
+#define CHIP_CONFIG_DEBUG_CERT_VALIDATION 0
+#endif // CHIP_CONFIG_DEBUG_CERT_VALIDATION
+
+#ifndef CHIP_CONFIG_ENABLE_CASE_RESPONDER
+#define CHIP_CONFIG_ENABLE_CASE_RESPONDER 1
+#endif // CHIP_CONFIG_ENABLE_CASE_RESPONDER
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * Copyright (c) 2018 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides the implementation of the Device Layer ConfigurationManager object
+ * for Linux platforms.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <ifaddrs.h>
+#include <netpacket/packet.h>
+
+#include <core/CHIPVendorIdentifiers.hpp>
+#include <platform/ConfigurationManager.h>
+#include <platform/Linux/PosixConfig.h>
+#include <platform/internal/GenericConfigurationManagerImpl.cpp>
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+using namespace ::chip::DeviceLayer::Internal;
+
+/** Singleton instance of the ConfigurationManager implementation object.
+ */
+ConfigurationManagerImpl ConfigurationManagerImpl::sInstance;
+
+CHIP_ERROR ConfigurationManagerImpl::_Init()
+{
+ CHIP_ERROR err;
+ bool failSafeArmed;
+
+ // Force initialization of NVS namespaces if they doesn't already exist.
+ err = EnsureNamespace(kConfigNamespace_ChipFactory);
+ SuccessOrExit(err);
+ err = EnsureNamespace(kConfigNamespace_ChipConfig);
+ SuccessOrExit(err);
+ err = EnsureNamespace(kConfigNamespace_ChipCounters);
+ SuccessOrExit(err);
+
+ // Initialize the generic implementation base class.
+ err = Internal::GenericConfigurationManagerImpl<ConfigurationManagerImpl>::_Init();
+ SuccessOrExit(err);
+
+ // If the fail-safe was armed when the device last shutdown, initiate a factory reset.
+ if (_GetFailSafeArmed(failSafeArmed) == CHIP_NO_ERROR && failSafeArmed)
+ {
+ ChipLogProgress(DeviceLayer, "Detected fail-safe armed on reboot; initiating factory reset");
+ _InitiateFactoryReset();
+ }
+
+ err = CHIP_NO_ERROR;
+
+exit:
+ return err;
+}
+
+CHIP_ERROR ConfigurationManagerImpl::_GetPrimaryWiFiMACAddress(uint8_t * buf)
+{
+ struct ifaddrs * addresses = NULL;
+ CHIP_ERROR error = CHIP_NO_ERROR;
+ bool found = false;
+
+ VerifyOrExit(getifaddrs(&addresses) == 0, error = CHIP_ERROR_INTERNAL);
+ for (auto addr = addresses; addr != NULL; addr = addr->ifa_next)
+ {
+ if ((addr->ifa_addr) && (addr->ifa_addr->sa_family == AF_PACKET) && strncmp(addr->ifa_name, "lo", IFNAMSIZ) != 0)
+ {
+ struct sockaddr_ll * mac = (struct sockaddr_ll *) addr->ifa_addr;
+ memcpy(buf, mac->sll_addr, mac->sll_halen);
+ found = true;
+ break;
+ }
+ }
+ freeifaddrs(addresses);
+ if (!found)
+ {
+ error = CHIP_ERROR_NO_ENDPOINT;
+ }
+
+exit:
+ return error;
+}
+
+bool ConfigurationManagerImpl::_CanFactoryReset()
+{
+ // TODO(#742): query the application to determine if factory reset is allowed.
+ return true;
+}
+
+void ConfigurationManagerImpl::_InitiateFactoryReset()
+{
+ PlatformMgr().ScheduleWork(DoFactoryReset);
+}
+
+CHIP_ERROR ConfigurationManagerImpl::_ReadPersistedStorageValue(::chip::Platform::PersistedStorage::Key key, uint32_t & value)
+{
+ PosixConfig::Key configKey{ kConfigNamespace_ChipCounters, key };
+
+ CHIP_ERROR err = ReadConfigValue(configKey, value);
+ if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND)
+ {
+ err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
+ }
+ return err;
+}
+
+CHIP_ERROR ConfigurationManagerImpl::_WritePersistedStorageValue(::chip::Platform::PersistedStorage::Key key, uint32_t value)
+{
+ PosixConfig::Key configKey{ kConfigNamespace_ChipCounters, key };
+ return WriteConfigValue(configKey, value);
+}
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
+CHIP_ERROR ConfigurationManagerImpl::GetWiFiStationSecurityType(Profiles::NetworkProvisioning::WiFiSecurityType & secType)
+{
+ CHIP_ERROR err;
+ uint32_t secTypeInt;
+
+ err = ReadConfigValue(kConfigKey_WiFiStationSecType, secTypeInt);
+ if (err == CHIP_NO_ERROR)
+ {
+ secType = (Profiles::NetworkProvisioning::WiFiSecurityType) secTypeInt;
+ }
+ return err;
+}
+
+CHIP_ERROR ConfigurationManagerImpl::UpdateWiFiStationSecurityType(Profiles::NetworkProvisioning::WiFiSecurityType secType)
+{
+ CHIP_ERROR err;
+ Profiles::NetworkProvisioning::WiFiSecurityType curSecType;
+
+ if (secType != Profiles::NetworkProvisioning::kWiFiSecurityType_NotSpecified)
+ {
+ err = GetWiFiStationSecurityType(curSecType);
+ if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || (err == CHIP_NO_ERROR && secType != curSecType))
+ {
+ uint32_t secTypeInt = secType;
+ err = WriteConfigValue(kConfigKey_WiFiStationSecType, secTypeInt);
+ }
+ SuccessOrExit(err);
+ }
+ else
+ {
+ err = ClearConfigValue(kConfigKey_WiFiStationSecType);
+ SuccessOrExit(err);
+ }
+
+exit:
+ return err;
+}
+#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
+
+void ConfigurationManagerImpl::DoFactoryReset(intptr_t arg)
+{
+ CHIP_ERROR err;
+
+ ChipLogProgress(DeviceLayer, "Performing factory reset");
+
+ err = FactoryResetConfig();
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "FactoryResetConfig() failed: %s", ErrorStr(err));
+ }
+
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+
+ ChipLogProgress(DeviceLayer, "Clearing Thread provision");
+ ThreadStackMgr().ErasePersistentInfo();
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD
+
+ // Restart the system.
+ ChipLogProgress(DeviceLayer, "System restarting (not implemented)");
+ // TODO(#742): restart CHIP exe
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the ConfigurationManager object
+ * for Linux platforms.
+ */
+
+#pragma once
+
+#include <platform/internal/GenericConfigurationManagerImpl.h>
+
+#include <platform/Linux/PosixConfig.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+/**
+ * Concrete implementation of the ConfigurationManager singleton object for the Linux platform.
+ */
+class ConfigurationManagerImpl final : public ConfigurationManager,
+ public Internal::GenericConfigurationManagerImpl<ConfigurationManagerImpl>,
+ private Internal::PosixConfig
+{
+ // Allow the ConfigurationManager interface class to delegate method calls to
+ // the implementation methods provided by this class.
+ friend class ConfigurationManager;
+
+ // Allow the GenericConfigurationManagerImpl base class to access helper methods and types
+ // defined on this class.
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ friend class Internal::GenericConfigurationManagerImpl<ConfigurationManagerImpl>;
+#endif
+
+private:
+ // ===== Members that implement the ConfigurationManager public interface.
+
+ CHIP_ERROR _Init();
+ CHIP_ERROR _GetPrimaryWiFiMACAddress(uint8_t * buf);
+ bool _CanFactoryReset();
+ void _InitiateFactoryReset();
+ CHIP_ERROR _ReadPersistedStorageValue(::chip::Platform::PersistedStorage::Key key, uint32_t & value);
+ CHIP_ERROR _WritePersistedStorageValue(::chip::Platform::PersistedStorage::Key key, uint32_t value);
+
+ // NOTE: Other public interface methods are implemented by GenericConfigurationManagerImpl<>.
+
+ // ===== Members for internal use by the following friends.
+
+ friend ConfigurationManager & ConfigurationMgr();
+ friend ConfigurationManagerImpl & ConfigurationMgrImpl();
+
+ static ConfigurationManagerImpl sInstance;
+
+ // ===== Private members reserved for use by this class only.
+
+ static void DoFactoryReset(intptr_t arg);
+};
+
+/**
+ * Returns the public interface of the ConfigurationManager singleton object.
+ *
+ * chip applications should use this to access features of the ConfigurationManager object
+ * that are common to all platforms.
+ */
+inline ConfigurationManager & ConfigurationMgr()
+{
+ return ConfigurationManagerImpl::sInstance;
+}
+
+/**
+ * Returns the platform-specific implementation of the ConfigurationManager singleton object.
+ *
+ * chip applications can use this to gain access to features of the ConfigurationManager
+ * that are specific to the ESP32 platform.
+ */
+inline ConfigurationManagerImpl & ConfigurationMgrImpl()
+{
+ return ConfigurationManagerImpl::sInstance;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ * Copyright (c) 2019 Nest Labs, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <platform/ConnectivityManager.h>
+#include <platform/internal/BLEManager.h>
+
+#include <cstdlib>
+#include <new>
+
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+#include <platform/internal/GenericConnectivityManagerImpl_BLE.cpp>
+#endif
+
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+#include <platform/internal/GenericConnectivityManagerImpl_Thread.cpp>
+#endif
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+#include <platform/internal/GenericConnectivityManagerImpl_WiFi.cpp>
+#endif
+
+using namespace ::chip;
+using namespace ::chip::TLV;
+using namespace ::chip::DeviceLayer::Internal;
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+namespace {
+const char kWpaSupplicantServiceName[] = "fi.w1.wpa_supplicant1";
+const char kWpaSupplicantObjectPath[] = "/fi/w1/wpa_supplicant1";
+
+constexpr uint16_t kWiFi_BAND_2_4_GHZ = 2400;
+constexpr uint16_t kWiFi_BAND_5_0_GHZ = 5000;
+
+uint16_t Map2400MHz(const uint8_t inChannel)
+{
+ uint16_t frequency = 0;
+
+ if (inChannel >= 1 && inChannel <= 13)
+ {
+ frequency = static_cast<uint16_t>(2412 + ((inChannel - 1) * 5));
+ }
+ else if (inChannel == 14)
+ {
+ frequency = 2484;
+ }
+
+ return frequency;
+}
+
+uint16_t Map5000MHz(const uint8_t inChannel)
+{
+ uint16_t frequency = 0;
+
+ switch (inChannel)
+ {
+
+ case 183:
+ frequency = 4915;
+ break;
+ case 184:
+ frequency = 4920;
+ break;
+ case 185:
+ frequency = 4925;
+ break;
+ case 187:
+ frequency = 4935;
+ break;
+ case 188:
+ frequency = 4940;
+ break;
+ case 189:
+ frequency = 4945;
+ break;
+ case 192:
+ frequency = 4960;
+ break;
+ case 196:
+ frequency = 4980;
+ break;
+ case 7:
+ frequency = 5035;
+ break;
+ case 8:
+ frequency = 5040;
+ break;
+ case 9:
+ frequency = 5045;
+ break;
+ case 11:
+ frequency = 5055;
+ break;
+ case 12:
+ frequency = 5060;
+ break;
+ case 16:
+ frequency = 5080;
+ break;
+ case 34:
+ frequency = 5170;
+ break;
+ case 36:
+ frequency = 5180;
+ break;
+ case 38:
+ frequency = 5190;
+ break;
+ case 40:
+ frequency = 5200;
+ break;
+ case 42:
+ frequency = 5210;
+ break;
+ case 44:
+ frequency = 5220;
+ break;
+ case 46:
+ frequency = 5230;
+ break;
+ case 48:
+ frequency = 5240;
+ break;
+ case 52:
+ frequency = 5260;
+ break;
+ case 56:
+ frequency = 5280;
+ break;
+ case 60:
+ frequency = 5300;
+ break;
+ case 64:
+ frequency = 5320;
+ break;
+ case 100:
+ frequency = 5500;
+ break;
+ case 104:
+ frequency = 5520;
+ break;
+ case 108:
+ frequency = 5540;
+ break;
+ case 112:
+ frequency = 5560;
+ break;
+ case 116:
+ frequency = 5580;
+ break;
+ case 120:
+ frequency = 5600;
+ break;
+ case 124:
+ frequency = 5620;
+ break;
+ case 128:
+ frequency = 5640;
+ break;
+ case 132:
+ frequency = 5660;
+ break;
+ case 136:
+ frequency = 5680;
+ break;
+ case 140:
+ frequency = 5700;
+ break;
+ case 149:
+ frequency = 5745;
+ break;
+ case 153:
+ frequency = 5765;
+ break;
+ case 157:
+ frequency = 5785;
+ break;
+ case 161:
+ frequency = 5805;
+ break;
+ case 165:
+ frequency = 5825;
+ break;
+ }
+
+ return frequency;
+}
+
+static uint16_t MapFrequency(const uint16_t inBand, const uint8_t inChannel)
+{
+ uint16_t frequency = 0;
+
+ if (inBand == kWiFi_BAND_2_4_GHZ)
+ {
+ frequency = Map2400MHz(inChannel);
+ }
+ else if (inBand == kWiFi_BAND_5_0_GHZ)
+ {
+ frequency = Map5000MHz(inChannel);
+ }
+
+ return frequency;
+}
+} // namespace
+#endif
+
+namespace chip {
+namespace DeviceLayer {
+
+ConnectivityManagerImpl ConnectivityManagerImpl::sInstance;
+
+CHIP_ERROR ConnectivityManagerImpl::_Init()
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ mWiFiStationMode = kWiFiStationMode_Disabled;
+ mWiFiStationReconnectIntervalMS = CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL;
+
+ // Initialize the generic base classes that require it.
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+ GenericConnectivityManagerImpl_Thread<ConnectivityManagerImpl>::_Init();
+#endif
+
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+void ConnectivityManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event)
+{
+ // Forward the event to the generic base classes as needed.
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+ GenericConnectivityManagerImpl_Thread<ConnectivityManagerImpl>::_OnPlatformEvent(event);
+#endif
+}
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+
+BitFlags<Internal::GenericConnectivityManagerImpl_WiFi<ConnectivityManagerImpl>::ConnectivityFlags>
+ ConnectivityManagerImpl::mConnectivityFlag;
+struct GDBusWpaSupplicant ConnectivityManagerImpl::mWpaSupplicant;
+
+bool ConnectivityManagerImpl::_HaveIPv4InternetConnectivity()
+{
+ return mConnectivityFlag.Has(ConnectivityFlags::kHaveIPv4InternetConnectivity);
+}
+
+bool ConnectivityManagerImpl::_HaveIPv6InternetConnectivity()
+{
+ return mConnectivityFlag.Has(ConnectivityFlags::kHaveIPv6InternetConnectivity);
+}
+
+ConnectivityManager::WiFiStationMode ConnectivityManagerImpl::_GetWiFiStationMode()
+{
+ if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled)
+ {
+ mWiFiStationMode = (mWpaSupplicant.iface != nullptr) ? kWiFiStationMode_Enabled : kWiFiStationMode_Disabled;
+ }
+
+ return mWiFiStationMode;
+}
+
+CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationMode(ConnectivityManager::WiFiStationMode val)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrExit(val != ConnectivityManager::kWiFiStationMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT);
+
+ if (mWiFiStationMode != val)
+ {
+ ChipLogProgress(DeviceLayer, "WiFi station mode change: %s -> %s", WiFiStationModeToStr(mWiFiStationMode),
+ WiFiStationModeToStr(val));
+ }
+
+ mWiFiStationMode = val;
+exit:
+ return err;
+}
+
+uint32_t ConnectivityManagerImpl::_GetWiFiStationReconnectIntervalMS()
+{
+ return mWiFiStationReconnectIntervalMS;
+}
+
+CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationReconnectIntervalMS(uint32_t val)
+{
+ mWiFiStationReconnectIntervalMS = val;
+
+ return CHIP_NO_ERROR;
+}
+
+bool ConnectivityManagerImpl::_IsWiFiStationEnabled()
+{
+ return GetWiFiStationMode() == kWiFiStationMode_Enabled;
+}
+
+bool ConnectivityManagerImpl::_IsWiFiStationConnected()
+{
+ bool ret = false;
+ const gchar * state = nullptr;
+
+ if (mWpaSupplicant.state != GDBusWpaSupplicant::WPA_INTERFACE_CONNECTED)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: _IsWiFiStationConnected: interface not connected");
+ return false;
+ }
+
+ state = wpa_fi_w1_wpa_supplicant1_interface_get_state(mWpaSupplicant.iface);
+ if (g_strcmp0(state, "completed") == 0)
+ {
+ mConnectivityFlag.Set(ConnectivityFlags::kHaveIPv4InternetConnectivity)
+ .Set(ConnectivityFlags::kHaveIPv6InternetConnectivity);
+ ret = true;
+ }
+
+ return ret;
+}
+
+bool ConnectivityManagerImpl::_IsWiFiStationApplicationControlled()
+{
+ return mWiFiStationMode == ConnectivityManager::kWiFiStationMode_ApplicationControlled;
+}
+
+bool ConnectivityManagerImpl::_IsWiFiStationProvisioned()
+{
+ bool ret = false;
+ const gchar * bss = nullptr;
+
+ if (mWpaSupplicant.state != GDBusWpaSupplicant::WPA_INTERFACE_CONNECTED)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: _IsWiFiStationProvisioned: interface not connected");
+ return false;
+ }
+
+ bss = wpa_fi_w1_wpa_supplicant1_interface_get_current_bss(mWpaSupplicant.iface);
+ if (g_str_match_string("BSSs", bss, true))
+ {
+ ret = true;
+ }
+
+ return ret;
+}
+
+void ConnectivityManagerImpl::_ClearWiFiStationProvision()
+{
+ if (mWpaSupplicant.state != GDBusWpaSupplicant::WPA_INTERFACE_CONNECTED)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: _ClearWiFiStationProvision: interface not connected");
+ return;
+ }
+
+ if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled)
+ {
+ GError * err = nullptr;
+ wpa_fi_w1_wpa_supplicant1_interface_call_remove_all_networks_sync(mWpaSupplicant.iface, nullptr, &err);
+
+ if (err != nullptr)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to remove all networks with error: %s",
+ err ? err->message : "unknown error");
+ g_error_free(err);
+ }
+ }
+}
+
+bool ConnectivityManagerImpl::_CanStartWiFiScan()
+{
+ return mWpaSupplicant.state == GDBusWpaSupplicant::WPA_INTERFACE_CONNECTED &&
+ mWpaSupplicant.scanState == GDBusWpaSupplicant::WIFI_SCANNING_IDLE;
+}
+
+CHIP_ERROR ConnectivityManagerImpl::_SetWiFiAPMode(WiFiAPMode val)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrExit(val != kWiFiAPMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT);
+
+ if (mWiFiAPMode != val)
+ {
+ ChipLogProgress(DeviceLayer, "WiFi AP mode change: %s -> %s", WiFiAPModeToStr(mWiFiAPMode), WiFiAPModeToStr(val));
+ mWiFiAPMode = val;
+
+ SystemLayer.ScheduleWork(DriveAPState, NULL);
+ }
+
+exit:
+ return err;
+}
+
+void ConnectivityManagerImpl::_DemandStartWiFiAP()
+{
+ if (mWiFiAPMode == kWiFiAPMode_OnDemand || mWiFiAPMode == kWiFiAPMode_OnDemand_NoStationProvision)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: Demand start WiFi AP");
+ mLastAPDemandTime = System::Layer::GetClock_MonotonicMS();
+ SystemLayer.ScheduleWork(DriveAPState, NULL);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: Demand start WiFi AP ignored, mode: %s", WiFiAPModeToStr(mWiFiAPMode));
+ }
+}
+
+void ConnectivityManagerImpl::_StopOnDemandWiFiAP()
+{
+ if (mWiFiAPMode == kWiFiAPMode_OnDemand || mWiFiAPMode == kWiFiAPMode_OnDemand_NoStationProvision)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: Demand stop WiFi AP");
+ mLastAPDemandTime = 0;
+ SystemLayer.ScheduleWork(DriveAPState, NULL);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: Demand stop WiFi AP ignored, mode: %s", WiFiAPModeToStr(mWiFiAPMode));
+ }
+}
+
+void ConnectivityManagerImpl::_MaintainOnDemandWiFiAP()
+{
+ if (mWiFiAPMode == kWiFiAPMode_OnDemand || mWiFiAPMode == kWiFiAPMode_OnDemand_NoStationProvision)
+ {
+ if (mWiFiAPState == kWiFiAPState_Active)
+ {
+ mLastAPDemandTime = System::Layer::GetClock_MonotonicMS();
+ }
+ }
+}
+
+void ConnectivityManagerImpl::_SetWiFiAPIdleTimeoutMS(uint32_t val)
+{
+ mWiFiAPIdleTimeoutMS = val;
+ SystemLayer.ScheduleWork(DriveAPState, NULL);
+}
+
+void ConnectivityManagerImpl::_OnWpaInterfaceProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data)
+{
+ GError * err = nullptr;
+ WpaFiW1Wpa_supplicant1Interface * iface = wpa_fi_w1_wpa_supplicant1_interface_proxy_new_for_bus_finish(res, &err);
+
+ if (mWpaSupplicant.iface)
+ {
+ g_object_unref(mWpaSupplicant.iface);
+ mWpaSupplicant.iface = nullptr;
+ }
+
+ if (iface != nullptr && err == nullptr)
+ {
+ mWpaSupplicant.iface = iface;
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_INTERFACE_CONNECTED;
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: connected to wpa_supplicant interface proxy");
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to create wpa_supplicant1 interface proxy %s: %s",
+ mWpaSupplicant.interfacePath, err ? err->message : "unknown error");
+
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_NOT_CONNECTED;
+ }
+
+ if (err != nullptr)
+ g_error_free(err);
+}
+
+void ConnectivityManagerImpl::_OnWpaInterfaceReady(GObject * source_object, GAsyncResult * res, gpointer user_data)
+{
+ GError * err = nullptr;
+
+ gboolean result =
+ wpa_fi_w1_wpa_supplicant1_call_get_interface_finish(mWpaSupplicant.proxy, &mWpaSupplicant.interfacePath, res, &err);
+ if (result)
+ {
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_GOT_INTERFACE_PATH;
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: WiFi interface: %s", mWpaSupplicant.interfacePath);
+
+ wpa_fi_w1_wpa_supplicant1_interface_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, kWpaSupplicantServiceName,
+ mWpaSupplicant.interfacePath, nullptr, _OnWpaInterfaceProxyReady,
+ nullptr);
+ }
+ else
+ {
+ GError * error = nullptr;
+ GVariant * args = nullptr;
+ GVariantBuilder builder;
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: can't find interface %s: %s", CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME,
+ err ? err->message : "unknown error");
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: try to create interface %s", CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME);
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "Ifname", g_variant_new_string(CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME));
+ args = g_variant_builder_end(&builder);
+
+ result = wpa_fi_w1_wpa_supplicant1_call_create_interface_sync(mWpaSupplicant.proxy, args, &mWpaSupplicant.interfacePath,
+ nullptr, &error);
+
+ if (result)
+ {
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_GOT_INTERFACE_PATH;
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: WiFi interface: %s", mWpaSupplicant.interfacePath);
+
+ wpa_fi_w1_wpa_supplicant1_interface_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
+ kWpaSupplicantServiceName, mWpaSupplicant.interfacePath, nullptr,
+ _OnWpaInterfaceProxyReady, nullptr);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to create interface %s: %s",
+ CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME, error ? error->message : "unknown error");
+
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_NO_INTERFACE_PATH;
+
+ if (mWpaSupplicant.interfacePath)
+ {
+ g_free(mWpaSupplicant.interfacePath);
+ mWpaSupplicant.interfacePath = nullptr;
+ }
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+ }
+
+ if (err != nullptr)
+ g_error_free(err);
+}
+
+void ConnectivityManagerImpl::_OnWpaInterfaceAdded(WpaFiW1Wpa_supplicant1 * proxy, const gchar * path, GVariant * properties,
+ gpointer user_data)
+{
+ if (mWpaSupplicant.interfacePath)
+ return;
+
+ mWpaSupplicant.interfacePath = const_cast<gchar *>(path);
+ if (mWpaSupplicant.interfacePath)
+ {
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_GOT_INTERFACE_PATH;
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: WiFi interface added: %s", mWpaSupplicant.interfacePath);
+
+ wpa_fi_w1_wpa_supplicant1_interface_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, kWpaSupplicantServiceName,
+ mWpaSupplicant.interfacePath, nullptr, _OnWpaInterfaceProxyReady,
+ nullptr);
+ }
+}
+
+void ConnectivityManagerImpl::_OnWpaInterfaceRemoved(WpaFiW1Wpa_supplicant1 * proxy, const gchar * path, GVariant * properties,
+ gpointer user_data)
+{
+ if (mWpaSupplicant.interfacePath == nullptr)
+ return;
+
+ if (g_strcmp0(mWpaSupplicant.interfacePath, path) == 0)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: WiFi interface removed: %s", path);
+
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_NO_INTERFACE_PATH;
+
+ if (mWpaSupplicant.interfacePath)
+ {
+ g_free(mWpaSupplicant.interfacePath);
+ mWpaSupplicant.interfacePath = nullptr;
+ }
+
+ if (mWpaSupplicant.iface)
+ {
+ g_object_unref(mWpaSupplicant.iface);
+ mWpaSupplicant.iface = nullptr;
+ }
+
+ mWpaSupplicant.scanState = GDBusWpaSupplicant::WIFI_SCANNING_IDLE;
+ }
+}
+
+void ConnectivityManagerImpl::_OnWpaProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data)
+{
+ GError * err = nullptr;
+
+ mWpaSupplicant.proxy = wpa_fi_w1_wpa_supplicant1_proxy_new_for_bus_finish(res, &err);
+ if (mWpaSupplicant.proxy != nullptr && err == nullptr)
+ {
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_CONNECTED;
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: connected to wpa_supplicant proxy");
+
+ g_signal_connect(mWpaSupplicant.proxy, "interface-added", G_CALLBACK(_OnWpaInterfaceAdded), NULL);
+
+ g_signal_connect(mWpaSupplicant.proxy, "interface-removed", G_CALLBACK(_OnWpaInterfaceRemoved), NULL);
+
+ wpa_fi_w1_wpa_supplicant1_call_get_interface(mWpaSupplicant.proxy, CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME, nullptr,
+ _OnWpaInterfaceReady, nullptr);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to create wpa_supplicant proxy %s",
+ err ? err->message : "unknown error");
+ mWpaSupplicant.state = GDBusWpaSupplicant::WPA_NOT_CONNECTED;
+ }
+
+ if (err != nullptr)
+ g_error_free(err);
+}
+
+void ConnectivityManagerImpl::StartWiFiManagement()
+{
+ mConnectivityFlag.ClearAll();
+ mWpaSupplicant.state = GDBusWpaSupplicant::INIT;
+ mWpaSupplicant.scanState = GDBusWpaSupplicant::WIFI_SCANNING_IDLE;
+ mWpaSupplicant.proxy = nullptr;
+ mWpaSupplicant.iface = nullptr;
+ mWpaSupplicant.interfacePath = nullptr;
+ mWpaSupplicant.networkPath = nullptr;
+
+ wpa_fi_w1_wpa_supplicant1_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, kWpaSupplicantServiceName,
+ kWpaSupplicantObjectPath, nullptr, _OnWpaProxyReady, nullptr);
+}
+
+void ConnectivityManagerImpl::DriveAPState()
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ WiFiAPState targetState;
+ uint64_t now;
+ uint32_t apTimeout;
+
+ // If the AP interface is not under application control...
+ if (mWiFiAPMode != kWiFiAPMode_ApplicationControlled)
+ {
+ // Determine the target (desired) state for AP interface...
+
+ // The target state is 'NotActive' if the application has expressly disabled the AP interface.
+ if (mWiFiAPMode == kWiFiAPMode_Disabled)
+ {
+ targetState = kWiFiAPState_NotActive;
+ }
+
+ // The target state is 'Active' if the application has expressly enabled the AP interface.
+ else if (mWiFiAPMode == kWiFiAPMode_Enabled)
+ {
+ targetState = kWiFiAPState_Active;
+ }
+
+ // The target state is 'Active' if the AP mode is 'On demand, when no station is available'
+ // and the station interface is not provisioned or the application has disabled the station
+ // interface.
+ else if (mWiFiAPMode == kWiFiAPMode_OnDemand_NoStationProvision &&
+ (!IsWiFiStationProvisioned() || GetWiFiStationMode() == kWiFiStationMode_Disabled))
+ {
+ targetState = kWiFiAPState_Active;
+ }
+
+ // The target state is 'Active' if the AP mode is one of the 'On demand' modes and there
+ // has been demand for the AP within the idle timeout period.
+ else if (mWiFiAPMode == kWiFiAPMode_OnDemand || mWiFiAPMode == kWiFiAPMode_OnDemand_NoStationProvision)
+ {
+ now = System::Layer::GetClock_MonotonicMS();
+
+ if (mLastAPDemandTime != 0 && now < (mLastAPDemandTime + mWiFiAPIdleTimeoutMS))
+ {
+ targetState = kWiFiAPState_Active;
+
+ // Compute the amount of idle time before the AP should be deactivated and
+ // arm a timer to fire at that time.
+ apTimeout = (uint32_t)((mLastAPDemandTime + mWiFiAPIdleTimeoutMS) - now);
+ err = SystemLayer.StartTimer(apTimeout, DriveAPState, NULL);
+ SuccessOrExit(err);
+ ChipLogProgress(DeviceLayer, "Next WiFi AP timeout in %" PRIu32 " s", apTimeout / 1000);
+ }
+ else
+ {
+ targetState = kWiFiAPState_NotActive;
+ }
+ }
+
+ // Otherwise the target state is 'NotActive'.
+ else
+ {
+ targetState = kWiFiAPState_NotActive;
+ }
+
+ // If the current AP state does not match the target state...
+ if (mWiFiAPState != targetState)
+ {
+ if (targetState == kWiFiAPState_Active)
+ {
+ err = ConfigureWiFiAP();
+ SuccessOrExit(err);
+
+ ChangeWiFiAPState(kWiFiAPState_Active);
+ }
+ else
+ {
+ if (mWpaSupplicant.networkPath)
+ {
+ GError * error = nullptr;
+
+ gboolean result = wpa_fi_w1_wpa_supplicant1_interface_call_remove_network_sync(
+ mWpaSupplicant.iface, mWpaSupplicant.networkPath, nullptr, &error);
+
+ if (result)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: removed network: %s", mWpaSupplicant.networkPath);
+ g_free(mWpaSupplicant.networkPath);
+ mWpaSupplicant.networkPath = nullptr;
+ ChangeWiFiAPState(kWiFiAPState_NotActive);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to stop AP mode with error: %s",
+ error ? error->message : "unknown error");
+ err = CHIP_ERROR_INTERNAL;
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+ }
+ }
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ SetWiFiAPMode(kWiFiAPMode_Disabled);
+ ChipLogError(DeviceLayer, "Drive AP state failed: %s", ErrorStr(err));
+ }
+}
+
+CHIP_ERROR ConnectivityManagerImpl::ConfigureWiFiAP()
+{
+ CHIP_ERROR ret = CHIP_NO_ERROR;
+ GError * err = nullptr;
+ GVariant * args = nullptr;
+ GVariantBuilder builder;
+
+ uint16_t channel = 1;
+ uint16_t discriminator = 0;
+ char ssid[32];
+
+ channel = MapFrequency(kWiFi_BAND_2_4_GHZ, CHIP_DEVICE_CONFIG_WIFI_AP_CHANNEL);
+
+ if (ConfigurationMgr().GetSetupDiscriminator(discriminator) != CHIP_NO_ERROR)
+ discriminator = 0;
+
+ snprintf(ssid, 32, "%s%04u", CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX, discriminator);
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: ConfigureWiFiAP, ssid: %s, channel: %d", ssid, channel);
+
+ // Clean up current network if exists
+ if (mWpaSupplicant.networkPath)
+ {
+ g_object_unref(mWpaSupplicant.networkPath);
+ mWpaSupplicant.networkPath = nullptr;
+ }
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssid));
+ g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("NONE"));
+ g_variant_builder_add(&builder, "{sv}", "mode", g_variant_new_int32(2));
+ g_variant_builder_add(&builder, "{sv}", "frequency", g_variant_new_int32(channel));
+ args = g_variant_builder_end(&builder);
+
+ gboolean result = wpa_fi_w1_wpa_supplicant1_interface_call_add_network_sync(mWpaSupplicant.iface, args,
+ &mWpaSupplicant.networkPath, nullptr, &err);
+
+ if (result)
+ {
+ GError * error = nullptr;
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: added network: SSID: %s: %s", ssid, mWpaSupplicant.networkPath);
+
+ result = wpa_fi_w1_wpa_supplicant1_interface_call_select_network_sync(mWpaSupplicant.iface, mWpaSupplicant.networkPath,
+ nullptr, &error);
+ if (result)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: succeeded to start softAP: SSID: %s", ssid);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to start softAP: SSID: %s: %s", ssid,
+ error ? error->message : "unknown error");
+
+ ret = CHIP_ERROR_INTERNAL;
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to add network: %s: %s", ssid, err ? err->message : "unknown error");
+
+ if (mWpaSupplicant.networkPath)
+ {
+ g_object_unref(mWpaSupplicant.networkPath);
+ mWpaSupplicant.networkPath = nullptr;
+ }
+
+ ret = CHIP_ERROR_INTERNAL;
+ }
+
+ if (err != nullptr)
+ g_error_free(err);
+
+ return ret;
+}
+
+void ConnectivityManagerImpl::ChangeWiFiAPState(WiFiAPState newState)
+{
+ if (mWiFiAPState != newState)
+ {
+ ChipLogProgress(DeviceLayer, "WiFi AP state change: %s -> %s", WiFiAPStateToStr(mWiFiAPState), WiFiAPStateToStr(newState));
+ mWiFiAPState = newState;
+ }
+}
+
+void ConnectivityManagerImpl::DriveAPState(::chip::System::Layer * aLayer, void * aAppState, ::chip::System::Error aError)
+{
+ sInstance.DriveAPState();
+}
+#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA
+
+CHIP_ERROR ConnectivityManagerImpl::ProvisionWiFiNetwork(const char * ssid, const char * key)
+{
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+ CHIP_ERROR ret = CHIP_NO_ERROR;
+ GError * err = nullptr;
+ GVariant * args = nullptr;
+ GVariantBuilder builder;
+ gboolean result;
+
+ // Clean up current network if exists
+ if (mWpaSupplicant.networkPath)
+ {
+ GError * error = nullptr;
+
+ result = wpa_fi_w1_wpa_supplicant1_interface_call_remove_network_sync(mWpaSupplicant.iface, mWpaSupplicant.networkPath,
+ nullptr, &error);
+
+ if (result)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: removed network: %s", mWpaSupplicant.networkPath);
+ g_free(mWpaSupplicant.networkPath);
+ mWpaSupplicant.networkPath = nullptr;
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to stop AP mode with error: %s",
+ error ? error->message : "unknown error");
+ ret = CHIP_ERROR_INTERNAL;
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+
+ SuccessOrExit(ret);
+ }
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&builder, "{sv}", "ssid", g_variant_new_string(ssid));
+ g_variant_builder_add(&builder, "{sv}", "psk", g_variant_new_string(key));
+ g_variant_builder_add(&builder, "{sv}", "key_mgmt", g_variant_new_string("WPA-PSK"));
+ args = g_variant_builder_end(&builder);
+
+ result = wpa_fi_w1_wpa_supplicant1_interface_call_add_network_sync(mWpaSupplicant.iface, args, &mWpaSupplicant.networkPath,
+ nullptr, &err);
+
+ if (result)
+ {
+ GError * error = nullptr;
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: added network: SSID: %s: %s", ssid, mWpaSupplicant.networkPath);
+
+ result = wpa_fi_w1_wpa_supplicant1_interface_call_select_network_sync(mWpaSupplicant.iface, mWpaSupplicant.networkPath,
+ nullptr, &error);
+ if (result)
+ {
+ GError * gerror = nullptr;
+
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: connected to network: SSID: %s", ssid);
+
+ result = wpa_fi_w1_wpa_supplicant1_interface_call_save_config_sync(mWpaSupplicant.iface, nullptr, &gerror);
+
+ if (result)
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: save config succeeded!");
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to save config: %s",
+ gerror ? gerror->message : "unknown error");
+ }
+
+ if (gerror != nullptr)
+ g_error_free(gerror);
+
+ // Iterate on the network interface to see if we already have beed assigned addresses.
+ // The temporary hack for getting IP address change on linux for network provisioning in the rendezvous session.
+ // This should be removed or find a better place once we depercate the rendezvous session.
+ for (chip::Inet::InterfaceAddressIterator it; it.HasCurrent(); it.Next())
+ {
+ char ifName[chip::Inet::InterfaceIterator::kMaxIfNameLength];
+ if (it.IsUp() && CHIP_NO_ERROR == it.GetInterfaceName(ifName, sizeof(ifName)) &&
+ strncmp(ifName, CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME, sizeof(ifName)) == 0)
+ {
+ chip::Inet::IPAddress addr = it.GetAddress();
+ if (addr.IsIPv4())
+ {
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kInternetConnectivityChange;
+ event.InternetConnectivityChange.IPv4 = kConnectivity_Established;
+ event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange;
+ addr.ToString(event.InternetConnectivityChange.address);
+
+ ChipLogDetail(DeviceLayer, "Got IP address on interface: %s IP: %s", ifName,
+ event.InternetConnectivityChange.address);
+
+ PlatformMgr().PostEvent(&event);
+ }
+ }
+ }
+
+ // Run dhclient for IP on WiFi.
+ // TODO: The wifi can be managed by networkmanager on linux so we don't have to care about this.
+ char cmdBuffer[128];
+ sprintf(cmdBuffer, "dhclient -nw %s", CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME);
+ int dhclientSystemRet = system(cmdBuffer);
+ if (dhclientSystemRet != 0)
+ {
+ ChipLogError(DeviceLayer, "Failed to run dhclient, system() returns %d", dhclientSystemRet);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "dhclient is running on the %s interface.", CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME);
+ }
+
+ // Return success as long as the device is connected to the network
+ ret = CHIP_NO_ERROR;
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to connect to network: SSID: %s: %s", ssid,
+ error ? error->message : "unknown error");
+
+ ret = CHIP_ERROR_INTERNAL;
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+ }
+ else
+ {
+ ChipLogProgress(DeviceLayer, "wpa_supplicant: failed to add network: %s: %s", ssid, err ? err->message : "unknown error");
+
+ if (mWpaSupplicant.networkPath)
+ {
+ g_object_unref(mWpaSupplicant.networkPath);
+ mWpaSupplicant.networkPath = nullptr;
+ }
+
+ ret = CHIP_ERROR_INTERNAL;
+ }
+
+exit:
+ if (err != nullptr)
+ g_error_free(err);
+
+ return ret;
+#else
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ * Copyright (c) 2018 Nest Labs, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <platform/ConnectivityManager.h>
+#include <platform/internal/GenericConnectivityManagerImpl.h>
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+#include <platform/internal/GenericConnectivityManagerImpl_BLE.h>
+#else
+#include <platform/internal/GenericConnectivityManagerImpl_NoBLE.h>
+#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+#include <platform/internal/GenericConnectivityManagerImpl_Thread.h>
+#else
+#include <platform/internal/GenericConnectivityManagerImpl_NoThread.h>
+#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+#include <platform/internal/GenericConnectivityManagerImpl_WiFi.h>
+#else
+#include <platform/internal/GenericConnectivityManagerImpl_NoWiFi.h>
+#endif
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+#include <platform/Linux/dbus/wpa/DBusWpa.h>
+#include <platform/Linux/dbus/wpa/DBusWpaInterface.h>
+#include <platform/Linux/dbus/wpa/DBusWpaNetwork.h>
+#endif
+
+namespace chip {
+namespace Inet {
+class IPAddress;
+} // namespace Inet
+} // namespace chip
+
+namespace chip {
+namespace DeviceLayer {
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+struct GDBusWpaSupplicant
+{
+ enum
+ {
+ INIT,
+ WPA_CONNECTING,
+ WPA_CONNECTED,
+ WPA_NOT_CONNECTED,
+ WPA_NO_INTERFACE_PATH,
+ WPA_GOT_INTERFACE_PATH,
+ WPA_INTERFACE_CONNECTED,
+ } state;
+
+ enum
+ {
+ WIFI_SCANNING_IDLE,
+ WIFI_SCANNING,
+ } scanState;
+
+ WpaFiW1Wpa_supplicant1 * proxy;
+ WpaFiW1Wpa_supplicant1Interface * iface;
+ gchar * interfacePath;
+ gchar * networkPath;
+};
+#endif
+
+/**
+ * Concrete implementation of the ConnectivityManager singleton object for Linux platforms.
+ */
+class ConnectivityManagerImpl final : public ConnectivityManager,
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+ public Internal::GenericConnectivityManagerImpl_BLE<ConnectivityManagerImpl>,
+#else
+ public Internal::GenericConnectivityManagerImpl_NoBLE<ConnectivityManagerImpl>,
+#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
+ public Internal::GenericConnectivityManagerImpl_Thread<ConnectivityManagerImpl>,
+#else
+ public Internal::GenericConnectivityManagerImpl_NoThread<ConnectivityManagerImpl>,
+#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+ public Internal::GenericConnectivityManagerImpl_WiFi<ConnectivityManagerImpl>,
+#else
+ public Internal::GenericConnectivityManagerImpl_NoWiFi<ConnectivityManagerImpl>,
+#endif
+ public Internal::GenericConnectivityManagerImpl<ConnectivityManagerImpl>
+{
+ // Allow the ConnectivityManager interface class to delegate method calls to
+ // the implementation methods provided by this class.
+ friend class ConnectivityManager;
+
+public:
+ CHIP_ERROR ProvisionWiFiNetwork(const char * ssid, const char * key);
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+ void StartWiFiManagement();
+#endif
+
+private:
+ // ===== Members that implement the ConnectivityManager abstract interface.
+
+ bool _HaveIPv4InternetConnectivity();
+ bool _HaveIPv6InternetConnectivity();
+ bool _HaveServiceConnectivity();
+ CHIP_ERROR _Init();
+ void _OnPlatformEvent(const ChipDeviceEvent * event);
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+ WiFiStationMode _GetWiFiStationMode();
+ CHIP_ERROR _SetWiFiStationMode(ConnectivityManager::WiFiStationMode val);
+ uint32_t _GetWiFiStationReconnectIntervalMS();
+ CHIP_ERROR _SetWiFiStationReconnectIntervalMS(uint32_t val);
+ bool _IsWiFiStationEnabled();
+ bool _IsWiFiStationConnected();
+ bool _IsWiFiStationApplicationControlled();
+ bool _IsWiFiStationProvisioned();
+ void _ClearWiFiStationProvision();
+ bool _CanStartWiFiScan();
+
+ WiFiAPMode _GetWiFiAPMode();
+ CHIP_ERROR _SetWiFiAPMode(WiFiAPMode val);
+ bool _IsWiFiAPActive();
+ bool _IsWiFiAPApplicationControlled();
+ void _DemandStartWiFiAP();
+ void _StopOnDemandWiFiAP();
+ void _MaintainOnDemandWiFiAP();
+ uint32_t _GetWiFiAPIdleTimeoutMS();
+ void _SetWiFiAPIdleTimeoutMS(uint32_t val);
+
+ static void _OnWpaProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data);
+ static void _OnWpaInterfaceRemoved(WpaFiW1Wpa_supplicant1 * proxy, const gchar * path, GVariant * properties,
+ gpointer user_data);
+ static void _OnWpaInterfaceAdded(WpaFiW1Wpa_supplicant1 * proxy, const gchar * path, GVariant * properties, gpointer user_data);
+ static void _OnWpaInterfaceReady(GObject * source_object, GAsyncResult * res, gpointer user_data);
+ static void _OnWpaInterfaceProxyReady(GObject * source_object, GAsyncResult * res, gpointer user_data);
+
+ static BitFlags<ConnectivityFlags> mConnectivityFlag;
+ static struct GDBusWpaSupplicant mWpaSupplicant;
+#endif
+
+ // ==================== ConnectivityManager Private Methods ====================
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+ void DriveAPState();
+ CHIP_ERROR ConfigureWiFiAP();
+ void ChangeWiFiAPState(WiFiAPState newState);
+ static void DriveAPState(::chip::System::Layer * aLayer, void * aAppState, ::chip::System::Error aError);
+#endif
+
+ // ===== Members for internal use by the following friends.
+
+ friend ConnectivityManager & ConnectivityMgr();
+ friend ConnectivityManagerImpl & ConnectivityMgrImpl();
+
+ static ConnectivityManagerImpl sInstance;
+
+ // ===== Private members reserved for use by this class only.
+
+ ConnectivityManager::WiFiStationMode mWiFiStationMode;
+ ConnectivityManager::WiFiAPMode mWiFiAPMode;
+ WiFiAPState mWiFiAPState;
+ uint64_t mLastAPDemandTime;
+ uint32_t mWiFiStationReconnectIntervalMS;
+ uint32_t mWiFiAPIdleTimeoutMS;
+};
+
+#if CHIP_DEVICE_CONFIG_ENABLE_WPA
+inline ConnectivityManager::WiFiAPMode ConnectivityManagerImpl::_GetWiFiAPMode()
+{
+ return mWiFiAPMode;
+}
+
+inline bool ConnectivityManagerImpl::_IsWiFiAPActive()
+{
+ return mWiFiAPState == kWiFiAPState_Active;
+}
+
+inline bool ConnectivityManagerImpl::_IsWiFiAPApplicationControlled()
+{
+ return mWiFiAPMode == kWiFiAPMode_ApplicationControlled;
+}
+
+inline uint32_t ConnectivityManagerImpl::_GetWiFiAPIdleTimeoutMS()
+{
+ return mWiFiAPIdleTimeoutMS;
+}
+
+inline bool ConnectivityManagerImpl::_HaveServiceConnectivity()
+{
+ return _HaveServiceConnectivityViaThread();
+}
+#endif
+
+/**
+ * Returns the public interface of the ConnectivityManager singleton object.
+ *
+ * chip applications should use this to access features of the ConnectivityManager object
+ * that are common to all platforms.
+ */
+inline ConnectivityManager & ConnectivityMgr()
+{
+ return ConnectivityManagerImpl::sInstance;
+}
+
+/**
+ * Returns the platform-specific implementation of the ConnectivityManager singleton object.
+ *
+ * chip applications can use this to gain access to features of the ConnectivityManager
+ * that are specific to the ESP32 platform.
+ */
+inline ConnectivityManagerImpl & ConnectivityMgrImpl()
+{
+ return ConnectivityManagerImpl::sInstance;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <support/ErrorStr.h>
+#include <support/logging/CHIPLogging.h>
+
+#include "DeviceNetworkProvisioningDelegateImpl.h"
+
+namespace chip {
+namespace DeviceLayer {
+
+CHIP_ERROR DeviceNetworkProvisioningDelegateImpl::_ProvisionWiFiNetwork(const char * ssid, const char * key)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ ChipLogProgress(NetworkProvisioning, "LinuxNetworkProvisioningDelegate: SSID: %s", ssid);
+
+ err = ConnectivityMgrImpl().ProvisionWiFiNetwork(ssid, key);
+
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %s", chip::ErrorStr(err));
+ }
+
+ return err;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <platform/internal/GenericDeviceNetworkProvisioningDelegateImpl.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+namespace Internal {
+
+template <class ImplClass>
+class GenericDeviceNetworkProvisioningDelegateImpl;
+
+} // namespace Internal
+
+class DeviceNetworkProvisioningDelegateImpl final
+ : public Internal::GenericDeviceNetworkProvisioningDelegateImpl<DeviceNetworkProvisioningDelegateImpl>
+{
+ friend class GenericDeviceNetworkProvisioningDelegateImpl<DeviceNetworkProvisioningDelegateImpl>;
+
+private:
+ CHIP_ERROR _ProvisionWiFiNetwork(const char * ssid, const char * passwd);
+ CHIP_ERROR _ProvisionThreadNetwork(DeviceLayer::Internal::DeviceNetworkInfo & threadData) { return CHIP_ERROR_NOT_IMPLEMENTED; }
+};
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * Copyright (c) 2019 Google LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides implementations for the chip entropy sourcing functions
+ * on the Linux platforms.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+#include <support/crypto/CHIPRNG.h>
+
+using namespace ::chip;
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+CHIP_ERROR InitEntropy()
+{
+ CHIP_ERROR err;
+ unsigned int seed;
+
+ // Initialize the source used by CHIP to get secure random data.
+ err = Platform::Security::InitSecureRandomDataSource(getentropy, 64, NULL, 0);
+ SuccessOrExit(err);
+
+ // Seed the standard rand() pseudo-random generator with data from the secure random source.
+ err = Platform::Security::GetSecureRandomData((uint8_t *) &seed, sizeof(seed));
+ SuccessOrExit(err);
+ srand(seed);
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Crypto, "InitEntropy() failed: %d" err);
+ }
+ return err;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific configuration overrides for the CHIP Inet
+ * Layer on Linux platforms.
+ *
+ */
+
+#pragma once
+
+// ==================== Platform Adaptations ====================
+
+#define INET_CONFIG_ENABLE_IPV4 1
+
+// ========== Platform-specific Configuration Overrides =========
+
+#ifndef INET_CONFIG_NUM_TCP_ENDPOINTS
+#define INET_CONFIG_NUM_TCP_ENDPOINTS 32
+#endif // INET_CONFIG_NUM_TCP_ENDPOINTS
+
+#ifndef INET_CONFIG_NUM_UDP_ENDPOINTS
+#define INET_CONFIG_NUM_UDP_ENDPOINTS 32
+#endif // INET_CONFIG_NUM_UDP_ENDPOINTS
+
+// On linux platform, we have sys/socket.h, so HAVE_SO_BINDTODEVICE should be set to 1
+#define HAVE_SO_BINDTODEVICE 1
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific implementatiuon of KVS for linux.
+ */
+
+#include <platform/KeyValueStoreManager.h>
+
+#include <algorithm>
+#include <string.h>
+
+#include <platform/Linux/CHIPLinuxStorage.h>
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace PersistedStorage {
+
+KeyValueStoreManagerImpl KeyValueStoreManagerImpl::sInstance;
+
+CHIP_ERROR KeyValueStoreManagerImpl::_Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size,
+ size_t offset_bytes)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ size_t read_size;
+ size_t copy_size;
+
+ // On linux read first without a buffer which returns the size, and then
+ // use a local buffer to read the entire object, which allows partial and
+ // offset reads.
+ err = mStorage.ReadValueBin(key, nullptr, 0, read_size);
+ uint8_t buf[read_size];
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ ExitNow(err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
+ }
+ err = mStorage.ReadValueBin(key, buf, read_size, read_size);
+ SuccessOrExit(err);
+
+ // Copy data into value buffer
+ if (!value)
+ {
+ ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
+ }
+ copy_size = std::min(value_size, read_size - offset_bytes);
+ if (read_bytes_size)
+ {
+ *read_bytes_size = copy_size;
+ }
+ ::memcpy(value, buf + offset_bytes, copy_size);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR KeyValueStoreManagerImpl::_Put(const char * key, const void * value, size_t value_size)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ err = mStorage.WriteValueBin(key, reinterpret_cast<const uint8_t *>(value), value_size);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = mStorage.Commit();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR KeyValueStoreManagerImpl::_Delete(const char * key)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ err = mStorage.ClearValue(key);
+
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ ExitNow(err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);
+ }
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = mStorage.Commit();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+} // namespace PersistedStorage
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific implementation of KVS for linux.
+ */
+
+#pragma once
+
+#include <platform/Linux/CHIPLinuxStorage.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace PersistedStorage {
+
+class KeyValueStoreManagerImpl : public KeyValueStoreManager
+{
+public:
+ /**
+ * @brief
+ * Initalize the KVS, must be called before using.
+ */
+ void Init(const char * file) { mStorage.Init(file); }
+
+ CHIP_ERROR _Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size = nullptr, size_t offset = 0);
+ CHIP_ERROR _Delete(const char * key);
+ CHIP_ERROR _Put(const char * key, const void * value, size_t value_size);
+
+private:
+ DeviceLayer::Internal::ChipLinuxStorage mStorage;
+
+ // ===== Members for internal use by the following friends.
+ friend KeyValueStoreManager & KeyValueStoreMgr();
+ friend KeyValueStoreManagerImpl & KeyValueStoreMgrImpl();
+
+ static KeyValueStoreManagerImpl sInstance;
+};
+
+/**
+ * Returns the public interface of the KeyValueStoreManager singleton object.
+ *
+ * Chip applications should use this to access features of the KeyValueStoreManager object
+ * that are common to all platforms.
+ */
+inline KeyValueStoreManager & KeyValueStoreMgr(void)
+{
+ return KeyValueStoreManagerImpl::sInstance;
+}
+
+/**
+ * Returns the platform-specific implementation of the KeyValueStoreManager singleton object.
+ *
+ * Chip applications can use this to gain access to features of the KeyValueStoreManager
+ * that are specific to the ESP32 platform.
+ */
+inline KeyValueStoreManagerImpl & KeyValueStoreMgrImpl(void)
+{
+ return KeyValueStoreManagerImpl::sInstance;
+}
+
+} // namespace PersistedStorage
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/* See Project CHIP LICENSE file for licensing information. */
+
+#include <platform/logging/LogV.h>
+
+#include <stdio.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+/**
+ * Called whenever a log message is emitted by chip or LwIP.
+ *
+ * This function is intended be overridden by the application to, e.g.,
+ * schedule output of queued log entries.
+ */
+void __attribute__((weak)) OnLogOutput() {}
+
+} // namespace DeviceLayer
+
+namespace Logging {
+namespace Platform {
+
+/**
+ * CHIP log output functions.
+ */
+void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+{
+ printf("CHIP:%s: ", module);
+ vprintf(msg, v);
+ printf("\n");
+
+ // Let the application know that a log message has been emitted.
+ DeviceLayer::OnLogOutput();
+}
+
+} // namespace Platform
+} // namespace Logging
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MdnsImpl.h"
+
+namespace chip {
+namespace Mdns {
+namespace Error {
+
+const char * ToString(DNSServiceErrorType errorCode)
+{
+ switch (errorCode)
+ {
+ case kDNSServiceErr_NoError:
+ return "kDNSServiceErr_NoError";
+ case kDNSServiceErr_Unknown:
+ return "kDNSServiceErr_Unknown";
+ case kDNSServiceErr_NoSuchName:
+ return "kDNSServiceErr_NoSuchName";
+ case kDNSServiceErr_NoMemory:
+ return "kDNSServiceErr_NoMemory";
+ case kDNSServiceErr_BadParam:
+ return "kDNSServiceErr_BadParam";
+ case kDNSServiceErr_BadReference:
+ return "kDNSServiceErr_BadReference";
+ case kDNSServiceErr_BadState:
+ return "kDNSServiceErr_BadState";
+ case kDNSServiceErr_BadFlags:
+ return "kDNSServiceErr_BadFlags";
+ case kDNSServiceErr_Unsupported:
+ return "kDNSServiceErr_Unsupported";
+ case kDNSServiceErr_NotInitialized:
+ return "kDNSServiceErr_NotInitialized";
+ case kDNSServiceErr_AlreadyRegistered:
+ return "kDNSServiceErr_AlreadyRegistered";
+ case kDNSServiceErr_NameConflict:
+ return "kDNSServiceErr_NameConflict";
+ case kDNSServiceErr_Invalid:
+ return "kDNSServiceErr_Invalid";
+ case kDNSServiceErr_Firewall:
+ return "kDNSServiceErr_Firewall";
+ case kDNSServiceErr_Incompatible:
+ return "kDNSServiceErr_Incompatible";
+ case kDNSServiceErr_BadInterfaceIndex:
+ return "kDNSServiceErr_BadInterfaceIndex";
+ case kDNSServiceErr_Refused:
+ return "kDNSServiceErr_Refused";
+ case kDNSServiceErr_NoSuchRecord:
+ return "kDNSServiceErr_NoSuchRecord";
+ case kDNSServiceErr_NoAuth:
+ return "kDNSServiceErr_NoAuth";
+ case kDNSServiceErr_NoSuchKey:
+ return "kDNSServiceErr_NoSuchKey";
+ case kDNSServiceErr_NATTraversal:
+ return "kDNSServiceErr_NATTraversal";
+ case kDNSServiceErr_DoubleNAT:
+ return "kDNSServiceErr_DoubleNAT";
+ case kDNSServiceErr_BadTime:
+ return "kDNSServiceErr_BadTime";
+ case kDNSServiceErr_BadSig:
+ return "kDNSServiceErr_BadSig";
+ case kDNSServiceErr_BadKey:
+ return "kDNSServiceErr_BadKey";
+ case kDNSServiceErr_Transient:
+ return "kDNSServiceErr_Transient";
+ case kDNSServiceErr_ServiceNotRunning:
+ return "kDNSServiceErr_ServiceNotRunning";
+ case kDNSServiceErr_NATPortMappingUnsupported:
+ return "kDNSServiceErr_NATPortMappingUnsupported";
+ case kDNSServiceErr_NATPortMappingDisabled:
+ return "kDNSServiceErr_NATPortMappingDisabled";
+ case kDNSServiceErr_NoRouter:
+ return "kDNSServiceErr_NoRouter";
+ case kDNSServiceErr_PollingMode:
+ return "kDNSServiceErr_PollingMode";
+ case kDNSServiceErr_Timeout:
+ return "kDNSServiceErr_Timeout";
+ default:
+ return "Unknown DNSService error code";
+ }
+}
+
+} // namespace Error
+} // namespace Mdns
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MdnsImpl.h"
+
+namespace chip {
+namespace Mdns {
+namespace Error {
+
+const char * ToString(DNSServiceErrorType errorCode);
+
+} // namespace Error
+} // namespace Mdns
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "MdnsImpl.h"
+
+#include "MdnsError.h"
+
+#include <cstdio>
+#include <sstream>
+#include <string.h>
+#include <algorithm>
+
+#include <platform/CHIPDeviceLayer.h>
+#include <support/CHIPMem.h>
+#include <support/CodeUtils.h>
+#include <support/SafeInt.h>
+#include <support/logging/CHIPLogging.h>
+
+using namespace chip::Mdns;
+
+namespace {
+
+constexpr const char * kLocalDomain = "local.";
+constexpr const char * kProtocolTcp = "._tcp";
+constexpr const char * kProtocolUdp = "._udp";
+constexpr uint8_t kMdnsKeyMaxSize = 32;
+
+bool IsSupportedProtocol(MdnsServiceProtocol protocol)
+{
+ return (protocol == MdnsServiceProtocol::kMdnsProtocolUdp) || (protocol == MdnsServiceProtocol::kMdnsProtocolTcp);
+}
+
+uint32_t GetInterfaceId(chip::Inet::InterfaceId interfaceId)
+{
+ return INET_NULL_INTERFACEID ? 0 : interfaceId;
+}
+
+std::string GetFullType(const char * type, MdnsServiceProtocol protocol)
+{
+ std::ostringstream typeBuilder;
+ typeBuilder << type;
+ typeBuilder << (protocol == MdnsServiceProtocol::kMdnsProtocolUdp ? kProtocolUdp : kProtocolTcp);
+ return typeBuilder.str();
+}
+
+} // namespace
+
+namespace chip {
+namespace Mdns {
+
+MdnsContexts MdnsContexts::sInstance;
+
+void MdnsContexts::Delete(GenericContext * context)
+{
+ if (context->type == ContextType::GetAddrInfo)
+ {
+ GetAddrInfoContext * addrInfoContext = reinterpret_cast<GetAddrInfoContext *>(context);
+ std::vector<TextEntry>::iterator textEntry;
+ for (textEntry = addrInfoContext->textEntries.begin(); textEntry != addrInfoContext->textEntries.end(); textEntry++)
+ {
+ free(const_cast<char *>(textEntry->mKey));
+ free(const_cast<uint8_t *>(textEntry->mData));
+ }
+ }
+
+ DNSServiceRefDeallocate(context->serviceRef);
+ chip::Platform::Delete(context);
+}
+
+MdnsContexts::~MdnsContexts()
+{
+ std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
+ while (iter != mContexts.cend())
+ {
+ Delete(*iter);
+ mContexts.erase(iter);
+ }
+}
+
+CHIP_ERROR MdnsContexts::Add(GenericContext * context, DNSServiceRef sdRef)
+{
+ VerifyOrReturnError(context != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(sdRef != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+ context->serviceRef = sdRef;
+ mContexts.push_back(context);
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR MdnsContexts::Remove(GenericContext * context)
+{
+ bool found = false;
+
+ std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
+ while (iter != mContexts.cend())
+ {
+ if (*iter != context)
+ {
+ iter++;
+ continue;
+ }
+
+ Delete(*iter);
+ mContexts.erase(iter);
+ found = true;
+ break;
+ }
+
+ return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
+}
+
+CHIP_ERROR MdnsContexts::Removes(ContextType type)
+{
+ bool found = false;
+
+ std::vector<GenericContext *>::const_iterator iter = mContexts.cbegin();
+ while (iter != mContexts.cend())
+ {
+ if ((*iter)->type != type)
+ {
+ iter++;
+ continue;
+ }
+
+ Delete(*iter);
+ mContexts.erase(iter);
+ found = true;
+ }
+
+ return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
+}
+
+CHIP_ERROR MdnsContexts::Get(ContextType type, GenericContext * context)
+{
+ bool found = false;
+ std::vector<GenericContext *>::iterator iter;
+
+ for (iter = mContexts.begin(); iter != mContexts.end(); iter++)
+ {
+ if ((*iter)->type == type)
+ {
+ context = *iter;
+ if (context != nullptr)
+ found = true;
+
+ break;
+ }
+ }
+
+ return found ? CHIP_NO_ERROR : CHIP_ERROR_KEY_NOT_FOUND;
+}
+
+void MdnsContexts::PrepareSelect(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout)
+{
+ std::vector<DNSServiceRef> serviceRefs(mContexts.size());
+ std::transform(mContexts.begin(), mContexts.end(), serviceRefs.begin(),
+ [](GenericContext * context) { return context->serviceRef; });
+
+ std::vector<DNSServiceRef>::iterator iter;
+ for (iter = serviceRefs.begin(); iter != serviceRefs.end(); iter++)
+ {
+ int fd = DNSServiceRefSockFD(*iter);
+ FD_SET(fd, &readFdSet);
+ if (maxFd < fd)
+ {
+ maxFd = fd;
+ }
+ }
+}
+
+void MdnsContexts::HandleSelectResult(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet)
+{
+ std::vector<DNSServiceRef> serviceRefs(mContexts.size());
+ std::transform(mContexts.begin(), mContexts.end(), serviceRefs.begin(),
+ [](GenericContext * context) { return context->serviceRef; });
+
+ std::vector<DNSServiceRef>::iterator iter;
+ for (iter = serviceRefs.begin(); iter != serviceRefs.end(); iter++)
+ {
+ int fd = DNSServiceRefSockFD(*iter);
+ if (FD_ISSET(fd, &readFdSet))
+ {
+ DNSServiceProcessResult(*iter);
+ }
+ }
+}
+
+CHIP_ERROR PopulateTextRecord(TXTRecordRef * record, char * buffer, uint16_t bufferLen, TextEntry * textEntries,
+ size_t textEntrySize)
+{
+ VerifyOrReturnError(textEntrySize <= kMdnsTextMaxSize, CHIP_ERROR_INVALID_ARGUMENT);
+
+ DNSServiceErrorType err;
+ TXTRecordCreate(record, bufferLen, buffer);
+
+ for (size_t i = 0; i < textEntrySize; i++)
+ {
+ TextEntry entry = textEntries[i];
+ VerifyOrReturnError(chip::CanCastTo<uint8_t>(entry.mDataSize), CHIP_ERROR_INVALID_ARGUMENT);
+
+ err = TXTRecordSetValue(record, entry.mKey, static_cast<uint8_t>(entry.mDataSize), entry.mData);
+ VerifyOrReturnError(err == kDNSServiceErr_NoError, CHIP_ERROR_INVALID_ARGUMENT);
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+bool CheckForSuccess(GenericContext * context, const char * name, DNSServiceErrorType err, bool useCallback = false)
+{
+ if (context == nullptr)
+ {
+ ChipLogError(DeviceLayer, "%s (%s)", name, "Mdns context is null.");
+ return false;
+ }
+
+ if (kDNSServiceErr_NoError != err)
+ {
+ ChipLogError(DeviceLayer, "%s (%s)", name, Error::ToString(err));
+
+ if (useCallback)
+ {
+ switch (context->type)
+ {
+ case ContextType::Register:
+ // Nothing special to do. Maybe ChipMdnsPublishService should take a callback ?
+ break;
+ case ContextType::Browse: {
+ BrowseContext * browseContext = reinterpret_cast<BrowseContext *>(context);
+ browseContext->callback(browseContext->context, nullptr, 0, CHIP_ERROR_INTERNAL);
+ break;
+ }
+ case ContextType::Resolve: {
+ ResolveContext * resolveContext = reinterpret_cast<ResolveContext *>(context);
+ resolveContext->callback(resolveContext->context, nullptr, CHIP_ERROR_INTERNAL);
+ break;
+ }
+ case ContextType::GetAddrInfo: {
+ GetAddrInfoContext * resolveContext = reinterpret_cast<GetAddrInfoContext *>(context);
+ resolveContext->callback(resolveContext->context, nullptr, CHIP_ERROR_INTERNAL);
+ break;
+ }
+ }
+ }
+
+ if (CHIP_ERROR_KEY_NOT_FOUND == MdnsContexts::GetInstance().Remove(context))
+ {
+ chip::Platform::Delete(context);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType err, const char * name, const char * type,
+ const char * domain, void * context)
+{
+ RegisterContext * sdCtx = reinterpret_cast<RegisterContext *>(context);
+ VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err));
+
+ ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags);
+};
+
+CHIP_ERROR Register(uint32_t interfaceId, const char * type, const char * name, uint16_t port, TXTRecordRef * recordRef)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef sdRef;
+ RegisterContext * sdCtx = nullptr;
+
+ uint16_t recordLen = TXTRecordGetLength(recordRef);
+ const void * recordBytesPtr = TXTRecordGetBytesPtr(recordRef);
+
+ if (CHIP_NO_ERROR == MdnsContexts::GetInstance().Get(ContextType::Register, sdCtx))
+ {
+ err = DNSServiceUpdateRecord(sdCtx->serviceRef, NULL, 0 /* flags */, recordLen, recordBytesPtr, 0 /* ttl */);
+ TXTRecordDeallocate(recordRef);
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
+ return CHIP_NO_ERROR;
+ }
+
+ sdCtx = chip::Platform::New<RegisterContext>(nullptr);
+ err = DNSServiceRegister(&sdRef, 0 /* flags */, interfaceId, name, type, kLocalDomain, NULL, port, recordLen, recordBytesPtr,
+ OnRegister, sdCtx);
+ TXTRecordDeallocate(recordRef);
+
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
+
+ return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
+}
+
+void OnBrowseAdd(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId)
+{
+ ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, interface: %d", __func__, name, type, domain,
+ interfaceId);
+
+ VerifyOrReturn(strcmp(kLocalDomain, domain) == 0);
+
+ char * tokens = strdup(type);
+ char * regtype = strtok(tokens, ".");
+ free(tokens);
+
+ MdnsService service = {};
+ service.mInterface = interfaceId;
+ service.mProtocol = context->protocol;
+
+ strncpy(service.mName, name, sizeof(service.mName));
+ service.mName[kMdnsNameMaxSize] = 0;
+
+ strncpy(service.mType, regtype, sizeof(service.mType));
+ service.mType[kMdnsTypeMaxSize] = 0;
+
+ context->services.push_back(service);
+}
+
+void OnBrowseRemove(BrowseContext * context, const char * name, const char * type, const char * domain, uint32_t interfaceId)
+{
+ ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, interface: %d", __func__, name, type, domain,
+ interfaceId);
+
+ VerifyOrReturn(strcmp(kLocalDomain, domain) == 0);
+
+ std::remove_if(context->services.begin(), context->services.end(), [name, type, interfaceId](const MdnsService & service) {
+ return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol) &&
+ service.mInterface == interfaceId;
+ });
+}
+
+static void OnBrowse(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err, const char * name,
+ const char * type, const char * domain, void * context)
+{
+ BrowseContext * sdCtx = reinterpret_cast<BrowseContext *>(context);
+ VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
+
+ (flags & kDNSServiceFlagsAdd) ? OnBrowseAdd(sdCtx, name, type, domain, interfaceId)
+ : OnBrowseRemove(sdCtx, name, type, domain, interfaceId);
+
+ if (!(flags & kDNSServiceFlagsMoreComing))
+ {
+ sdCtx->callback(sdCtx->context, sdCtx->services.data(), sdCtx->services.size(), CHIP_NO_ERROR);
+ MdnsContexts::GetInstance().Remove(sdCtx);
+ }
+}
+
+CHIP_ERROR Browse(void * context, MdnsBrowseCallback callback, uint32_t interfaceId, const char * type,
+ MdnsServiceProtocol protocol)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef sdRef;
+ BrowseContext * sdCtx;
+
+ sdCtx = chip::Platform::New<BrowseContext>(context, callback, protocol);
+ err = DNSServiceBrowse(&sdRef, 0 /* flags */, interfaceId, type, kLocalDomain, OnBrowse, sdCtx);
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
+
+ return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
+}
+
+static void OnGetAddrInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err,
+ const char * hostname, const struct sockaddr * address, uint32_t ttl, void * context)
+{
+ GetAddrInfoContext * sdCtx = reinterpret_cast<GetAddrInfoContext *>(context);
+ VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
+
+ ChipLogDetail(DeviceLayer, "Mdns: %s hostname:%s", __func__, hostname);
+
+ MdnsService service = {};
+ service.mPort = sdCtx->port;
+ service.mTextEntries = sdCtx->textEntries.empty() ? nullptr : sdCtx->textEntries.data();
+ service.mTextEntrySize = sdCtx->textEntries.empty() ? 0 : sdCtx->textEntries.size();
+ service.mAddress.SetValue(chip::Inet::IPAddress::FromSockAddr(*address));
+
+ sdCtx->callback(sdCtx->context, &service, CHIP_NO_ERROR);
+ MdnsContexts::GetInstance().Remove(sdCtx);
+}
+
+CHIP_ERROR GetAddrInfo(void * context, MdnsResolveCallback callback, uint32_t interfaceId, const char * hostname, uint16_t port,
+ uint16_t txtLen, const unsigned char * txtRecord)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef sdRef;
+ GetAddrInfoContext * sdCtx;
+
+ sdCtx = chip::Platform::New<GetAddrInfoContext>(context, callback, port);
+
+ char key[kMdnsKeyMaxSize];
+ char value[kMdnsTextMaxSize];
+ uint8_t valueLen;
+ const void * valuePtr;
+
+ uint16_t recordCount = TXTRecordGetCount(txtLen, txtRecord);
+ for (uint16_t i = 0; i < recordCount; i++)
+ {
+ err = TXTRecordGetItemAtIndex(txtLen, txtRecord, i, kMdnsKeyMaxSize, key, &valueLen, &valuePtr);
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err, true), CHIP_ERROR_INTERNAL);
+
+ strncpy(value, reinterpret_cast<const char *>(valuePtr), valueLen);
+ value[valueLen] = 0;
+
+ sdCtx->textEntries.push_back(TextEntry{ strdup(key), reinterpret_cast<const uint8_t *>(strdup(value)), valueLen });
+ }
+
+ err = DNSServiceGetAddrInfo(&sdRef, 0 /* flags */, interfaceId, kDNSServiceProtocol_IPv4, hostname, OnGetAddrInfo, sdCtx);
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err, true), CHIP_ERROR_INTERNAL);
+
+ return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
+}
+
+static void OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceId, DNSServiceErrorType err,
+ const char * fullname, const char * hostname, uint16_t port, uint16_t txtLen, const unsigned char * txtRecord,
+ void * context)
+{
+ ResolveContext * sdCtx = reinterpret_cast<ResolveContext *>(context);
+ VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err, true));
+
+ GetAddrInfo(sdCtx->context, sdCtx->callback, interfaceId, hostname, port, txtLen, txtRecord);
+ MdnsContexts::GetInstance().Remove(sdCtx);
+}
+
+CHIP_ERROR Resolve(void * context, MdnsResolveCallback callback, uint32_t interfaceId, const char * type, const char * name)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef sdRef;
+ ResolveContext * sdCtx;
+
+ sdCtx = chip::Platform::New<ResolveContext>(context, callback);
+ err = DNSServiceResolve(&sdRef, 0 /* flags */, interfaceId, name, type, kLocalDomain, OnResolve, sdCtx);
+ VerifyOrReturnError(CheckForSuccess(sdCtx, __func__, err), CHIP_ERROR_INTERNAL);
+
+ return MdnsContexts::GetInstance().Add(sdCtx, sdRef);
+}
+
+CHIP_ERROR ChipMdnsInit(MdnsAsyncReturnCallback successCallback, MdnsAsyncReturnCallback errorCallback, void * context)
+{
+ VerifyOrReturnError(successCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(errorCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+ successCallback(context, CHIP_NO_ERROR);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR ChipMdnsSetHostname(const char * hostname)
+{
+ MdnsContexts::GetInstance().SetHostname(hostname);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR ChipMdnsPublishService(const MdnsService * service)
+{
+ VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
+
+ std::string regtype = GetFullType(service->mType, service->mProtocol);
+ uint32_t interfaceId = GetInterfaceId(service->mInterface);
+
+ TXTRecordRef record;
+ char buffer[kMdnsTextMaxSize];
+ ReturnErrorOnFailure(PopulateTextRecord(&record, buffer, sizeof(buffer), service->mTextEntries, service->mTextEntrySize));
+
+ return Register(interfaceId, regtype.c_str(), service->mName, service->mPort, &record);
+}
+
+CHIP_ERROR ChipMdnsStopPublish()
+{
+ return MdnsContexts::GetInstance().Removes(ContextType::Register);
+}
+
+CHIP_ERROR ChipMdnsBrowse(const char * type, MdnsServiceProtocol protocol, chip::Inet::IPAddressType addressType,
+ chip::Inet::InterfaceId interface, MdnsBrowseCallback callback, void * context)
+{
+ VerifyOrReturnError(type != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(IsSupportedProtocol(protocol), CHIP_ERROR_INVALID_ARGUMENT);
+
+ std::string regtype = GetFullType(type, protocol);
+ uint32_t interfaceId = GetInterfaceId(interface);
+
+ return Browse(context, callback, interfaceId, regtype.c_str(), protocol);
+}
+
+CHIP_ERROR ChipMdnsResolve(MdnsService * service, chip::Inet::InterfaceId interface, MdnsResolveCallback callback, void * context)
+{
+ VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT);
+
+ std::string regtype = GetFullType(service->mType, service->mProtocol);
+ uint32_t interfaceId = GetInterfaceId(service->mInterface);
+
+ return Resolve(context, callback, interfaceId, regtype.c_str(), service->mName);
+}
+
+void UpdateMdnsDataset(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout)
+{
+ MdnsContexts::GetInstance().PrepareSelect(readFdSet, writeFdSet, errorFdSet, maxFd, timeout);
+}
+
+void ProcessMdns(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet)
+{
+ MdnsContexts::GetInstance().HandleSelectResult(readFdSet, writeFdSet, errorFdSet);
+}
+
+} // namespace Mdns
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dns_sd.h>
+#include <lib/mdns/platform/Mdns.h>
+#include <string>
+#include <vector>
+
+namespace chip {
+namespace Mdns {
+
+enum class ContextType
+{
+ Register,
+ Browse,
+ Resolve,
+ GetAddrInfo,
+};
+
+struct GenericContext
+{
+ ContextType type;
+ void * context;
+ DNSServiceRef serviceRef;
+};
+
+struct RegisterContext : public GenericContext
+{
+ RegisterContext(void * cbContext)
+ {
+ type = ContextType::Register;
+ context = cbContext;
+ }
+};
+
+struct BrowseContext : public GenericContext
+{
+ MdnsBrowseCallback callback;
+ std::vector<MdnsService> services;
+ MdnsServiceProtocol protocol;
+
+ BrowseContext(void * cbContext, MdnsBrowseCallback cb, MdnsServiceProtocol cbContextProtocol)
+ {
+ type = ContextType::Browse;
+ context = cbContext;
+ callback = cb;
+ protocol = cbContextProtocol;
+ }
+};
+
+struct ResolveContext : public GenericContext
+{
+ MdnsResolveCallback callback;
+
+ ResolveContext(void * cbContext, MdnsResolveCallback cb)
+ {
+ type = ContextType::Resolve;
+ context = cbContext;
+ callback = cb;
+ }
+};
+
+struct GetAddrInfoContext : public GenericContext
+{
+ MdnsResolveCallback callback;
+ std::vector<TextEntry> textEntries;
+ uint16_t port;
+
+ GetAddrInfoContext(void * cbContext, MdnsResolveCallback cb, uint16_t cbContextPort)
+ {
+ type = ContextType::GetAddrInfo;
+ context = cbContext;
+ callback = cb;
+ port = cbContextPort;
+ }
+};
+
+class MdnsContexts
+{
+public:
+ MdnsContexts(const MdnsContexts &) = delete;
+ MdnsContexts & operator=(const MdnsContexts &) = delete;
+ ~MdnsContexts();
+ static MdnsContexts & GetInstance() { return sInstance; }
+
+ void PrepareSelect(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout);
+ void HandleSelectResult(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet);
+
+ CHIP_ERROR Add(GenericContext * context, DNSServiceRef sdRef);
+ CHIP_ERROR Remove(GenericContext * context);
+ CHIP_ERROR Removes(ContextType type);
+ CHIP_ERROR Get(ContextType type, GenericContext * context);
+
+ void SetHostname(const char * name) { mHostname = name; }
+ const char * GetHostname() { return mHostname.c_str(); }
+
+private:
+ MdnsContexts(){};
+ static MdnsContexts sInstance;
+ std::string mHostname;
+
+ void Delete(GenericContext * context);
+ std::vector<GenericContext *> mContexts;
+};
+
+} // namespace Mdns
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * Copyright (c) 2018 Nest Labs, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the PlatformManager object
+ * for Linux platforms.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <platform/PlatformManager.h>
+#include <platform/internal/GenericPlatformManagerImpl_POSIX.cpp>
+#include <support/CHIPMem.h>
+#include <support/logging/CHIPLogging.h>
+
+#include <thread>
+
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+PlatformManagerImpl PlatformManagerImpl::sInstance;
+
+#if CHIP_WITH_GIO
+static void GDBus_Thread()
+{
+ GMainLoop * loop = g_main_loop_new(nullptr, false);
+
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+#endif
+
+void PlatformManagerImpl::WiFIIPChangeListener()
+{
+ int sock;
+ if ((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+ {
+ ChipLogError(DeviceLayer, "Failed to init netlink socket for ip addresses.");
+ return;
+ }
+
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR;
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
+ {
+ ChipLogError(DeviceLayer, "Failed to bind netlink socket for ip addresses.");
+ return;
+ }
+
+ ssize_t len;
+ char buffer[4096];
+ for (struct nlmsghdr * header = reinterpret_cast<struct nlmsghdr *>(buffer); (len = recv(sock, header, sizeof(buffer), 0)) > 0;)
+ {
+ for (struct nlmsghdr * messageHeader = header;
+ (NLMSG_OK(messageHeader, static_cast<uint32_t>(len))) && (messageHeader->nlmsg_type != NLMSG_DONE);
+ messageHeader = NLMSG_NEXT(messageHeader, len))
+ {
+ if (header->nlmsg_type == RTM_NEWADDR)
+ {
+ struct ifaddrmsg * addressMessage = (struct ifaddrmsg *) NLMSG_DATA(header);
+ struct rtattr * routeInfo = IFA_RTA(addressMessage);
+ size_t rtl = IFA_PAYLOAD(header);
+
+ while (rtl && RTA_OK(routeInfo, rtl))
+ {
+ if (routeInfo->rta_type == IFA_LOCAL)
+ {
+ char name[IFNAMSIZ];
+ ChipDeviceEvent event;
+ if_indextoname(addressMessage->ifa_index, name);
+ if (strcmp(name, CHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME) != 0)
+ {
+ continue;
+ }
+
+ event.Type = DeviceEventType::kInternetConnectivityChange;
+ event.InternetConnectivityChange.IPv4 = kConnectivity_Established;
+ event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange;
+ inet_ntop(AF_INET, RTA_DATA(routeInfo), event.InternetConnectivityChange.address,
+ sizeof(event.InternetConnectivityChange.address));
+
+ ChipLogDetail(DeviceLayer, "Got IP address on interface: %s IP: %s", name,
+ event.InternetConnectivityChange.address);
+
+ PlatformMgr().LockChipStack();
+ PlatformMgr().PostEvent(&event);
+ PlatformMgr().UnlockChipStack();
+ }
+ routeInfo = RTA_NEXT(routeInfo, rtl);
+ }
+ }
+ }
+ }
+}
+
+CHIP_ERROR PlatformManagerImpl::_InitChipStack()
+{
+ CHIP_ERROR err;
+
+#if CHIP_WITH_GIO
+ GError * error = nullptr;
+
+ this->mpGDBusConnection = UniqueGDBusConnection(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error));
+
+ std::thread gdbusThread(GDBus_Thread);
+ gdbusThread.detach();
+#endif
+
+ std::thread wifiIPThread(WiFIIPChangeListener);
+ wifiIPThread.detach();
+
+ // Initialize the configuration system.
+ err = Internal::PosixConfig::Init();
+ SuccessOrExit(err);
+ // Call _InitChipStack() on the generic implementation base class
+ // to finish the initialization process.
+ err = Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>::_InitChipStack();
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+#if CHIP_WITH_GIO
+GDBusConnection * PlatformManagerImpl::GetGDBusConnection()
+{
+ return this->mpGDBusConnection.get();
+}
+#endif
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * Copyright (c) 2018 Nest Labs, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides an implementation of the PlatformManager object.
+ */
+
+#pragma once
+
+#include <memory>
+#include <platform/internal/GenericPlatformManagerImpl_POSIX.h>
+
+#if CHIP_WITH_GIO
+#include <gio/gio.h>
+#endif
+
+namespace chip {
+namespace DeviceLayer {
+
+/**
+ * Concrete implementation of the PlatformManager singleton object for Linux platforms.
+ */
+class PlatformManagerImpl final : public PlatformManager, public Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>
+{
+ // Allow the PlatformManager interface class to delegate method calls to
+ // the implementation methods provided by this class.
+ friend PlatformManager;
+
+ // Allow the generic implementation base class to call helper methods on
+ // this class.
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ friend Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>;
+#endif
+
+public:
+ // ===== Platform-specific members that may be accessed directly by the application.
+#if CHIP_WITH_GIO
+ GDBusConnection * GetGDBusConnection();
+#endif
+
+private:
+ // ===== Methods that implement the PlatformManager abstract interface.
+
+ CHIP_ERROR _InitChipStack();
+
+ // ===== Members for internal use by the following friends.
+
+ friend PlatformManager & PlatformMgr();
+ friend PlatformManagerImpl & PlatformMgrImpl();
+ friend class Internal::BLEManagerImpl;
+
+ static PlatformManagerImpl sInstance;
+
+ // The temporary hack for getting IP address change on linux for network provisioning in the rendezvous session.
+ // This should be removed or find a better place once we depercate the rendezvous session.
+ static void WiFIIPChangeListener();
+
+#if CHIP_WITH_GIO
+ struct GDBusConnectionDeleter
+ {
+ void operator()(GDBusConnection * conn) { g_object_unref(conn); }
+ };
+ using UniqueGDBusConnection = std::unique_ptr<GDBusConnection, GDBusConnectionDeleter>;
+ UniqueGDBusConnection mpGDBusConnection;
+#endif
+};
+
+/**
+ * Returns the public interface of the PlatformManager singleton object.
+ *
+ * chip applications should use this to access features of the PlatformManager object
+ * that are common to all platforms.
+ */
+inline PlatformManager & PlatformMgr()
+{
+ return PlatformManagerImpl::sInstance;
+}
+
+/**
+ * Returns the platform-specific implementation of the PlatformManager singleton object.
+ *
+ * chip applications can use this to gain access to features of the PlatformManager
+ * that are specific to the ESP32 platform.
+ */
+inline PlatformManagerImpl & PlatformMgrImpl()
+{
+ return PlatformManagerImpl::sInstance;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ * Copyright (c) 2019-2020 Google LLC.
+ * Copyright (c) 2018 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Utilities for interacting with multiple file partitions and maps
+ * key-value config calls to the correct partition.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+#include <platform/internal/testing/ConfigUnitTest.h>
+
+#include <core/CHIPEncoding.h>
+#include <platform/Linux/CHIPLinuxStorage.h>
+#include <platform/Linux/PosixConfig.h>
+#include <support/CodeUtils.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+static ChipLinuxStorage gChipLinuxFactoryStorage;
+static ChipLinuxStorage gChipLinuxConfigStorage;
+static ChipLinuxStorage gChipLinuxCountersStorage;
+
+// *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices.
+
+// NVS namespaces used to store device configuration information.
+const char PosixConfig::kConfigNamespace_ChipFactory[] = "chip-factory";
+const char PosixConfig::kConfigNamespace_ChipConfig[] = "chip-config";
+const char PosixConfig::kConfigNamespace_ChipCounters[] = "chip-counters";
+
+// Keys stored in the Chip-factory namespace
+const PosixConfig::Key PosixConfig::kConfigKey_SerialNum = { kConfigNamespace_ChipFactory, "serial-num" };
+const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceId = { kConfigNamespace_ChipFactory, "device-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceCert = { kConfigNamespace_ChipFactory, "device-cert" };
+const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceICACerts = { kConfigNamespace_ChipFactory, "device-ca-certs" };
+const PosixConfig::Key PosixConfig::kConfigKey_MfrDevicePrivateKey = { kConfigNamespace_ChipFactory, "device-key" };
+const PosixConfig::Key PosixConfig::kConfigKey_ProductRevision = { kConfigNamespace_ChipFactory, "product-rev" };
+const PosixConfig::Key PosixConfig::kConfigKey_ManufacturingDate = { kConfigNamespace_ChipFactory, "mfg-date" };
+const PosixConfig::Key PosixConfig::kConfigKey_SetupPinCode = { kConfigNamespace_ChipFactory, "pin-code" };
+const PosixConfig::Key PosixConfig::kConfigKey_SetupDiscriminator = { kConfigNamespace_ChipFactory, "discriminator" };
+
+// Keys stored in the Chip-config namespace
+const PosixConfig::Key PosixConfig::kConfigKey_FabricId = { kConfigNamespace_ChipConfig, "fabric-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_ServiceConfig = { kConfigNamespace_ChipConfig, "service-config" };
+const PosixConfig::Key PosixConfig::kConfigKey_PairedAccountId = { kConfigNamespace_ChipConfig, "account-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_ServiceId = { kConfigNamespace_ChipConfig, "service-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_FabricSecret = { kConfigNamespace_ChipConfig, "fabric-secret" };
+const PosixConfig::Key PosixConfig::kConfigKey_GroupKeyIndex = { kConfigNamespace_ChipConfig, "group-key-index" };
+const PosixConfig::Key PosixConfig::kConfigKey_LastUsedEpochKeyId = { kConfigNamespace_ChipConfig, "last-ek-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_FailSafeArmed = { kConfigNamespace_ChipConfig, "fail-safe-armed" };
+const PosixConfig::Key PosixConfig::kConfigKey_WiFiStationSecType = { kConfigNamespace_ChipConfig, "sta-sec-type" };
+const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceId = { kConfigNamespace_ChipConfig, "op-device-id" };
+const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceCert = { kConfigNamespace_ChipConfig, "op-device-cert" };
+const PosixConfig::Key PosixConfig::kConfigKey_OperationalDeviceICACerts = { kConfigNamespace_ChipConfig, "op-device-ca-certs" };
+const PosixConfig::Key PosixConfig::kConfigKey_OperationalDevicePrivateKey = { kConfigNamespace_ChipConfig, "op-device-key" };
+
+// Prefix used for NVS keys that contain Chip group encryption keys.
+const char PosixConfig::kGroupKeyNamePrefix[] = "gk-";
+
+ChipLinuxStorage * PosixConfig::GetStorageForNamespace(Key key)
+{
+ if (strcmp(key.Namespace, kConfigNamespace_ChipFactory) == 0)
+ return &gChipLinuxFactoryStorage;
+
+ if (strcmp(key.Namespace, kConfigNamespace_ChipConfig) == 0)
+ return &gChipLinuxConfigStorage;
+
+ if (strcmp(key.Namespace, kConfigNamespace_ChipCounters) == 0)
+ return &gChipLinuxCountersStorage;
+
+ return nullptr;
+}
+
+CHIP_ERROR PosixConfig::Init()
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ReadConfigValue(Key key, bool & val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+ uint32_t intVal;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ReadValue(key.Name, intVal);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ SuccessOrExit(err);
+
+ val = (intVal != 0);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint32_t & val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ReadValue(key.Name, val);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint64_t & val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ // Special case the MfrDeviceId value, optionally allowing it to be read as a blob containing
+ // a 64-bit big-endian integer, instead of a u64 value.
+ if (key == kConfigKey_MfrDeviceId)
+ {
+ uint8_t deviceIdBytes[sizeof(uint64_t)];
+ size_t deviceIdLen = sizeof(deviceIdBytes);
+ size_t deviceIdOutLen;
+ err = storage->ReadValueBin(key.Name, deviceIdBytes, deviceIdLen, deviceIdOutLen);
+ if (err == CHIP_NO_ERROR)
+ {
+ VerifyOrExit(deviceIdOutLen == sizeof(deviceIdBytes), err = CHIP_ERROR_INCORRECT_STATE);
+ val = Encoding::BigEndian::Get64(deviceIdBytes);
+ ExitNow();
+ }
+ }
+
+ err = storage->ReadValue(key.Name, val);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ReadValueStr(key.Name, buf, bufSize, outLen);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ outLen = 0;
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
+ {
+ err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL;
+ }
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ReadValueBin(key.Name, buf, bufSize, outLen);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ outLen = 0;
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
+ {
+ err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL;
+ }
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValue(Key key, bool val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->WriteValue(key.Name, val);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %s", key.Namespace, key.Name, val ? "true" : "false");
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint32_t val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->WriteValue(key.Name, val);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu32 " (0x%" PRIX32 ")", key.Namespace, key.Name, val, val);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint64_t val)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->WriteValue(key.Name, val);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu64 " (0x%" PRIX64 ")", key.Namespace, key.Name, val, val);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ if (str != nullptr)
+ {
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->WriteValueStr(key.Name, str);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS set: %s/%s = \"%s\"", key.Namespace, key.Name, str);
+ }
+
+ else
+ {
+ err = ClearConfigValue(key);
+ SuccessOrExit(err);
+ }
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str, size_t strLen)
+{
+#if CHIP_CONFIG_MEMORY_MGMT_MALLOC
+ CHIP_ERROR err;
+ char * strCopy = nullptr;
+
+ if (str != nullptr)
+ {
+ strCopy = strndup(str, strLen);
+ VerifyOrExit(strCopy != nullptr, err = CHIP_ERROR_NO_MEMORY);
+ }
+
+ err = PosixConfig::WriteConfigValueStr(key, strCopy);
+
+exit:
+ if (strCopy != nullptr)
+ {
+ free(strCopy);
+ }
+ return err;
+#else
+#error "Unsupported CHIP_CONFIG_MEMORY_MGMT configuration"
+#endif
+}
+
+CHIP_ERROR PosixConfig::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ if (data != nullptr)
+ {
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->WriteValueBin(key.Name, data, dataLen);
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS set: %s/%s = (blob length %" PRId32 ")", key.Namespace, key.Name, dataLen);
+ }
+ else
+ {
+ err = ClearConfigValue(key);
+ SuccessOrExit(err);
+ }
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ClearConfigValue(Key key)
+{
+ CHIP_ERROR err;
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ClearValue(key.Name);
+ if (err == CHIP_ERROR_KEY_NOT_FOUND)
+ {
+ ExitNow(err = CHIP_NO_ERROR);
+ }
+ SuccessOrExit(err);
+
+ // Commit the value to the persistent store.
+ err = storage->Commit();
+ SuccessOrExit(err);
+
+ ChipLogProgress(DeviceLayer, "NVS erase: %s/%s", key.Namespace, key.Name);
+
+exit:
+ return err;
+}
+
+bool PosixConfig::ConfigValueExists(Key key)
+{
+ ChipLinuxStorage * storage;
+
+ storage = GetStorageForNamespace(key);
+ if (storage == nullptr)
+ return false;
+
+ return storage->HasValue(key.Name);
+}
+
+CHIP_ERROR PosixConfig::EnsureNamespace(const char * ns)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ ChipLinuxStorage * storage = nullptr;
+
+ if (strcmp(ns, kConfigNamespace_ChipFactory) == 0)
+ {
+ storage = &gChipLinuxFactoryStorage;
+ err = storage->Init(CHIP_DEFAULT_FACTORY_PATH);
+ }
+ else if (strcmp(ns, kConfigNamespace_ChipConfig) == 0)
+ {
+ storage = &gChipLinuxConfigStorage;
+ err = storage->Init(CHIP_DEFAULT_CONFIG_PATH);
+ }
+ else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0)
+ {
+ storage = &gChipLinuxCountersStorage;
+ err = storage->Init(CHIP_DEFAULT_DATA_PATH);
+ }
+
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::ClearNamespace(const char * ns)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ ChipLinuxStorage * storage = nullptr;
+
+ if (strcmp(ns, kConfigNamespace_ChipConfig) == 0)
+ {
+ storage = &gChipLinuxConfigStorage;
+ }
+ else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0)
+ {
+ storage = &gChipLinuxCountersStorage;
+ }
+
+ VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
+
+ err = storage->ClearAll();
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err));
+ }
+ SuccessOrExit(err);
+
+ err = storage->Commit();
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err));
+ }
+
+exit:
+ return err;
+}
+
+CHIP_ERROR PosixConfig::FactoryResetConfig()
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ ChipLinuxStorage * storage;
+
+ ChipLogProgress(DeviceLayer, "Performing factory reset");
+
+ storage = &gChipLinuxConfigStorage;
+ if (storage == nullptr)
+ {
+ ChipLogError(DeviceLayer, "Storage get failed");
+ err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
+ }
+ SuccessOrExit(err);
+
+ err = storage->ClearAll();
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err));
+ }
+ SuccessOrExit(err);
+
+ err = storage->Commit();
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err));
+ }
+
+exit:
+ return err;
+}
+
+void PosixConfig::RunConfigUnitTest()
+{
+ // Run common unit test.
+ ::chip::DeviceLayer::Internal::RunConfigUnitTest<PosixConfig>();
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Utilities for accessing persisted device configuration on
+ * Linux platforms.
+ */
+
+#pragma once
+
+#include <functional>
+#include <inttypes.h>
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+class ChipLinuxStorage;
+
+/**
+ * Provides functions and definitions for accessing device configuration information on the Posix.
+ *
+ * This class is designed to be mixed-in to concrete implementation classes as a means to
+ * provide access to configuration information to generic base classes.
+ */
+class PosixConfig
+{
+public:
+ struct Key;
+
+ // Maximum length of an NVS key name.
+ static constexpr size_t kMaxConfigKeyNameLength = 15;
+
+ // NVS namespaces used to store device configuration information.
+ static const char kConfigNamespace_ChipFactory[];
+ static const char kConfigNamespace_ChipConfig[];
+ static const char kConfigNamespace_ChipCounters[];
+
+ // Key definitions for well-known keys.
+ static const Key kConfigKey_SerialNum;
+ static const Key kConfigKey_MfrDeviceId;
+ static const Key kConfigKey_MfrDeviceCert;
+ static const Key kConfigKey_MfrDeviceICACerts;
+ static const Key kConfigKey_MfrDevicePrivateKey;
+ static const Key kConfigKey_ProductRevision;
+ static const Key kConfigKey_ManufacturingDate;
+ static const Key kConfigKey_SetupPinCode;
+ static const Key kConfigKey_FabricId;
+ static const Key kConfigKey_ServiceConfig;
+ static const Key kConfigKey_PairedAccountId;
+ static const Key kConfigKey_ServiceId;
+ static const Key kConfigKey_FabricSecret;
+ static const Key kConfigKey_GroupKeyIndex;
+ static const Key kConfigKey_LastUsedEpochKeyId;
+ static const Key kConfigKey_FailSafeArmed;
+ static const Key kConfigKey_WiFiStationSecType;
+ static const Key kConfigKey_OperationalDeviceId;
+ static const Key kConfigKey_OperationalDeviceCert;
+ static const Key kConfigKey_OperationalDeviceICACerts;
+ static const Key kConfigKey_OperationalDevicePrivateKey;
+ static const Key kConfigKey_SetupDiscriminator;
+
+ static const char kGroupKeyNamePrefix[];
+
+ static CHIP_ERROR Init();
+
+ // Config value accessors.
+ static CHIP_ERROR ReadConfigValue(Key key, bool & val);
+ static CHIP_ERROR ReadConfigValue(Key key, uint32_t & val);
+ static CHIP_ERROR ReadConfigValue(Key key, uint64_t & val);
+ static CHIP_ERROR ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen);
+ static CHIP_ERROR ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen);
+ static CHIP_ERROR WriteConfigValue(Key key, bool val);
+ static CHIP_ERROR WriteConfigValue(Key key, uint32_t val);
+ static CHIP_ERROR WriteConfigValue(Key key, uint64_t val);
+ static CHIP_ERROR WriteConfigValueStr(Key key, const char * str);
+ static CHIP_ERROR WriteConfigValueStr(Key key, const char * str, size_t strLen);
+ static CHIP_ERROR WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen);
+ static CHIP_ERROR ClearConfigValue(Key key);
+ static bool ConfigValueExists(Key key);
+ static CHIP_ERROR FactoryResetConfig();
+
+ static void RunConfigUnitTest();
+
+protected:
+ // NVS Namespace helper functions.
+ static CHIP_ERROR EnsureNamespace(const char * ns);
+ static CHIP_ERROR ClearNamespace(const char * ns);
+
+private:
+ static ChipLinuxStorage * GetStorageForNamespace(Key key);
+};
+
+struct PosixConfig::Key
+{
+ const char * Namespace;
+ const char * Name;
+
+ bool operator==(const Key & other) const;
+};
+
+inline bool PosixConfig::Key::operator==(const Key & other) const
+{
+ return strcmp(Namespace, other.Namespace) == 0 && strcmp(Name, other.Name) == 0;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+# Overview of CHIP Linux Adaption
+
+The following is a quick overview of the Linux adaptation of CHIP. Most of this
+code will have parallels in any new adaptation.
+
+(All file names are relative to `connectedhomeip/src/...`).
+
+`include/platform/Linux/PlatformManagerImpl.h`<br>`Linux/PlatformManagerImpl.cpp`
+
+- Concrete implementation of PlatformManager interface
+- Provides initialization of the CHIP stack and core event loop for the chip
+ task
+- Relies on GenericPlatformManagerImpl_POSIX<> class to provide most of the
+ implementation
+
+`include/platform/Linux/ConfigurationManagerImpl.h`<br>`Linux/ConfigurationManagerImpl.cpp`
+
+- Concrete implementation of ConfigurationManager interface
+- Manages storage and retrieval of persistent configuration data
+- Relies on GenericConfigurationManagerImpl<> classes to implement most API
+ functionality
+- Delegates low-level reading and writing of persistent values to PosixConfig
+ class
+
+`include/platform/Linux/ConnectivityManagerImpl.h`<br>`Linux/ConnectivityManagerImpl.cpp`
+
+- Concrete implementation of ConnectivityManager interface
+- Provides high-level APIs for managing device connectivity
+- Relies on various generic implementation classes to provide API
+ functionality
+- Very much a work-in-progress in the Linux branch
+
+`include/platform/Linux/ThreadStackManagerImpl.h`<br>`Linux/ThreadStackManagerImpl.cpp`
+
+- Concrete implementation of ThreadStackManager interface
+- Supports Thread stack initialization and core event loop processing
+- Relies on GenericThreadStackManagerImpl_OpenThread/POSIX<> classes to
+ implement most API functionaltiy
+
+`include/platform/Linux/BLEManagerImpl.h`<br>`Linux/BLEManagerImpl.cpp`
+
+- Concrete implementation of the BLEManager interface
+- Maps CHIP's BLE interface abstractions (BleLayer, BlePlatformDelegate,
+ BleApplicationDelegate) onto the platform's native BLE services
+- Implements chip-compatible BLE advertising.
+
+`platform/Linux/Entropy.cpp`
+
+- Implements interface to platform entropy source
+
+`platform/Linux/Logging.cpp`
+
+- Adaption of chip debug logging to platform logging facility.
+
+`platform/Linux/PosixConfig.cpp`
+
+- Implements low-level read/write of persistent configuration values
+- Class API specifically designed to work in conjunction with the
+ GenericConfigurationManagerImpl<> class.
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Platform-specific configuration overrides for the CHIP System
+ * Layer on Linux platforms.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+namespace chip {
+namespace DeviceLayer {
+struct ChipDeviceEvent;
+} // namespace DeviceLayer
+} // namespace chip
+
+// ==================== Platform Adaptations ====================
+
+#define CHIP_SYSTEM_CONFIG_POSIX_LOCKING 1
+#define CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING 0
+#define CHIP_SYSTEM_CONFIG_NO_LOCKING 0
+#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_EVENT_FUNCTIONS 1
+#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 0
+
+#define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1
+
+// ========== Platform-specific Configuration Overrides =========
+
+#ifndef CHIP_SYSTEM_CONFIG_NUM_TIMERS
+#define CHIP_SYSTEM_CONFIG_NUM_TIMERS 16
+#endif // CHIP_SYSTEM_CONFIG_NUM_TIMERS
--- /dev/null
+/*
+ *
+ * Copyright (c) 2018 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * Provides implementations of the CHIP System Layer platform
+ * time/clock functions that are suitable for use on the Posix platform.
+ */
+
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <support/TimeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+#include <chrono>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/time.h>
+
+namespace chip {
+namespace System {
+namespace Platform {
+namespace Layer {
+
+uint64_t GetClock_Monotonic()
+{
+ std::chrono::microseconds epoch =
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
+ // count() is nominally signed, but for a monotonic clock it cannot be
+ // negative.
+ return static_cast<uint64_t>(epoch.count());
+}
+
+uint64_t GetClock_MonotonicMS()
+{
+ std::chrono::milliseconds epoch =
+ std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
+ // count() is nominally signed, but for a monotonic clock it cannot be
+ // negative.
+ return static_cast<uint64_t>(epoch.count());
+}
+
+uint64_t GetClock_MonotonicHiRes()
+{
+ std::chrono::microseconds epoch =
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
+ // count() is nominally signed, but for a monotonic clock it cannot be
+ // negative.
+ return static_cast<uint64_t>(epoch.count());
+}
+
+System::Error GetClock_RealTime(uint64_t & curTime)
+{
+ struct timeval tv;
+ int res = gettimeofday(&tv, nullptr);
+ if (res != 0)
+ {
+ return MapErrorPOSIX(errno);
+ }
+ if (tv.tv_sec < CHIP_SYSTEM_CONFIG_VALID_REAL_TIME_THRESHOLD)
+ {
+ return CHIP_SYSTEM_ERROR_REAL_TIME_NOT_SYNCED;
+ }
+ if (tv.tv_usec < 0)
+ {
+ return CHIP_SYSTEM_ERROR_REAL_TIME_NOT_SYNCED;
+ }
+ static_assert(CHIP_SYSTEM_CONFIG_VALID_REAL_TIME_THRESHOLD >= 0, "We might be letting through negative tv_sec values!");
+ curTime = (static_cast<uint64_t>(tv.tv_sec) * UINT64_C(1000000)) + static_cast<uint64_t>(tv.tv_usec);
+ return CHIP_SYSTEM_NO_ERROR;
+}
+
+System::Error GetClock_RealTimeMS(uint64_t & curTime)
+{
+ struct timeval tv;
+ int res = gettimeofday(&tv, nullptr);
+ if (res != 0)
+ {
+ return MapErrorPOSIX(errno);
+ }
+ if (tv.tv_sec < CHIP_SYSTEM_CONFIG_VALID_REAL_TIME_THRESHOLD)
+ {
+ return CHIP_SYSTEM_ERROR_REAL_TIME_NOT_SYNCED;
+ }
+ if (tv.tv_usec < 0)
+ {
+ return CHIP_SYSTEM_ERROR_REAL_TIME_NOT_SYNCED;
+ }
+ static_assert(CHIP_SYSTEM_CONFIG_VALID_REAL_TIME_THRESHOLD >= 0, "We might be letting through negative tv_sec values!");
+ curTime = (static_cast<uint64_t>(tv.tv_sec) * UINT64_C(1000)) + (static_cast<uint64_t>(tv.tv_usec) / 1000);
+ return CHIP_SYSTEM_NO_ERROR;
+}
+
+System::Error SetClock_RealTime(uint64_t newCurTime)
+{
+ struct timeval tv;
+ tv.tv_sec = static_cast<time_t>(newCurTime / UINT64_C(1000000));
+ tv.tv_usec = static_cast<long>(newCurTime % UINT64_C(1000000));
+ int res = settimeofday(&tv, nullptr);
+ if (res != 0)
+ {
+ return (errno == EPERM) ? CHIP_SYSTEM_ERROR_ACCESS_DENIED : MapErrorPOSIX(errno);
+ }
+#if CHIP_PROGRESS_LOGGING
+ {
+ const time_t timep = tv.tv_sec;
+ struct tm calendar;
+ localtime_r(&timep, &calendar);
+ ChipLogProgress(
+ DeviceLayer,
+ "Real time clock set to %ld (%04" PRId16 "/%02" PRId8 "/%02" PRId8 " %02" PRId8 ":%02" PRId8 ":%02" PRId8 " UTC)",
+ tv.tv_sec, calendar.tm_year, calendar.tm_mon, calendar.tm_mday, calendar.tm_hour, calendar.tm_min, calendar.tm_sec);
+ }
+#endif // CHIP_PROGRESS_LOGGING
+ return CHIP_SYSTEM_NO_ERROR;
+}
+
+} // namespace Layer
+} // namespace Platform
+} // namespace System
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <limits.h>
+#include <string.h>
+
+#include "platform/internal/CHIPDeviceLayerInternal.h"
+#include "platform/internal/DeviceNetworkInfo.h"
+
+#include "platform/PlatformManager.h"
+#include "platform/ThreadStackManager.h"
+#include "support/CodeUtils.h"
+#include "support/logging/CHIPLogging.h"
+
+#include "dbus/client/thread_api_dbus.hpp"
+
+using chip::DeviceLayer::Internal::DeviceNetworkInfo;
+using otbr::DBus::ClientError;
+using otbr::DBus::DeviceRole;
+using otbr::DBus::LinkModeConfig;
+using otbr::DBus::NeighborInfo;
+
+#ifndef CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN
+#define CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN 8000000
+#endif
+
+#define OTBR_TO_CHIP_ERROR(x) \
+ (x == ClientError::ERROR_NONE ? CHIP_NO_ERROR : _CHIP_ERROR(CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN + static_cast<int>(x)))
+
+#define LogClientError(error) \
+ do \
+ { \
+ if (error != ClientError::ERROR_NONE) \
+ { \
+ ChipLogError(DeviceLayer, __FILE__ " %d: Otbr ClientError %d", __LINE__, static_cast<int>(error)); \
+ } \
+ } while (0)
+
+constexpr int kDBusConnectionPollingTimeoutMS = 10;
+
+namespace chip {
+namespace DeviceLayer {
+
+ThreadStackManagerImpl ThreadStackManagerImpl::sInstance;
+
+ThreadStackManagerImpl::ThreadStackManagerImpl() : mThreadApi(nullptr), mConnection(nullptr), mNetworkInfo(), mAttached(false) {}
+
+CHIP_ERROR ThreadStackManagerImpl::_InitThreadStack()
+{
+ ClientError error;
+ DeviceRole role;
+ DBusError dbusError;
+ DBusConnection * dispatchConnection;
+
+ dbus_error_init(&dbusError);
+ mConnection = UniqueDBusConnection(dbus_bus_get_private(DBUS_BUS_SYSTEM, &dbusError));
+
+ VerifyOrExit(mConnection != nullptr, error = ClientError::ERROR_DBUS);
+ mThreadApi = std::unique_ptr<otbr::DBus::ThreadApiDBus>(new otbr::DBus::ThreadApiDBus(mConnection.get()));
+ mThreadApi->AddDeviceRoleHandler([this](DeviceRole newRole) { this->_ThreadDevcieRoleChangedHandler(newRole); });
+
+ SuccessOrExit(error = mThreadApi->GetDeviceRole(role));
+ _ThreadDevcieRoleChangedHandler(role);
+ mAttached = (role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED);
+
+ dispatchConnection = mConnection.get();
+ mDBusEventLoop = std::thread([dispatchConnection]() {
+ while (true)
+ {
+ // The dbus_connection_read_write will lock the connection until new message comes or timeout.
+ // This will block ot-br-posix APIs. Set timeout to 10ms so it can work.
+ // TODO: we should have a global event loop for dbus to take care of this.
+ dbus_connection_read_write_dispatch(dispatchConnection, kDBusConnectionPollingTimeoutMS);
+ }
+ });
+ mDBusEventLoop.detach();
+exit:
+ dbus_error_free(&dbusError);
+ LogClientError(error);
+ return OTBR_TO_CHIP_ERROR(error);
+}
+
+void ThreadStackManagerImpl::_ThreadDevcieRoleChangedHandler(DeviceRole role)
+{
+ bool attached = (role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED);
+ ChipDeviceEvent event = ChipDeviceEvent{};
+
+ if (attached != mAttached)
+ {
+ event.Type = DeviceEventType::kThreadConnectivityChange;
+ event.ThreadConnectivityChange.Result =
+ attached ? ConnectivityChange::kConnectivity_Established : ConnectivityChange::kConnectivity_Lost;
+ PlatformMgr().PostEvent(&event);
+ }
+
+ event.Type = DeviceEventType::kThreadStateChange;
+ event.ThreadStateChange.RoleChanged = true;
+ PlatformMgr().PostEvent(&event);
+}
+
+void ThreadStackManagerImpl::_ProcessThreadActivity() {}
+
+static bool RouteMatch(const otbr::DBus::Ip6Prefix & prefix, const Inet::IPAddress & addr)
+{
+ bool match = true;
+ const uint8_t * prefixBuffer;
+ const uint8_t * addrBuffer;
+ uint8_t wholeBytes = prefix.mLength / CHAR_BIT;
+ uint8_t pendingBits = prefix.mLength % CHAR_BIT;
+
+ VerifyOrExit(addr.IsIPv6(), match = false);
+ VerifyOrExit(prefix.mLength > 0, match = false);
+
+ prefixBuffer = static_cast<const uint8_t *>(&prefix.mPrefix[0]);
+ addrBuffer = reinterpret_cast<const uint8_t *>(&addr.Addr);
+ VerifyOrExit(memcmp(addrBuffer, prefixBuffer, wholeBytes) == 0, match = false);
+ if (pendingBits)
+ {
+ uint8_t mask = static_cast<uint8_t>(((UINT8_MAX >> pendingBits) << (CHAR_BIT - pendingBits)));
+
+ VerifyOrExit((addrBuffer[wholeBytes] & mask) == (addrBuffer[wholeBytes] & mask), match = false);
+ }
+ VerifyOrExit(memcmp(addrBuffer, prefixBuffer, wholeBytes) == 0, match = false);
+
+exit:
+ return match;
+}
+
+bool ThreadStackManagerImpl::_HaveRouteToAddress(const Inet::IPAddress & destAddr)
+{
+ std::vector<otbr::DBus::ExternalRoute> routes;
+ bool match = false;
+
+ VerifyOrExit(mThreadApi->GetExternalRoutes(routes) == ClientError::ERROR_NONE, match = false);
+ VerifyOrExit(_IsThreadAttached(), match = false);
+ VerifyOrExit(destAddr.IsIPv6LinkLocal(), match = true);
+ for (const auto & route : routes)
+ {
+ VerifyOrExit(!(match = RouteMatch(route.mPrefix, destAddr)), );
+ }
+
+exit:
+ return match;
+}
+
+void ThreadStackManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event)
+{
+ (void) event;
+ // The otbr-agent processes the Thread state handling by itself so there
+ // isn't much to do in the Chip stack.
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_SetThreadProvision(const Internal::DeviceNetworkInfo & netInfo)
+{
+ mNetworkInfo = netInfo;
+
+ // post an event alerting other subsystems about change in provisioning state
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kServiceProvisioningChange;
+ event.ServiceProvisioningChange.IsServiceProvisioned = true;
+ PlatformMgr().PostEvent(&event);
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_SetThreadProvision(const uint8_t * operationalDataset, size_t operationalDatasetLen)
+{
+ mOperationalDatasetTlv = std::vector<uint8_t>(operationalDataset, operationalDataset + operationalDatasetLen);
+
+ // post an event alerting other subsystems about change in provisioning state
+ ChipDeviceEvent event;
+ event.Type = DeviceEventType::kServiceProvisioningChange;
+ event.ServiceProvisioningChange.IsServiceProvisioned = true;
+ PlatformMgr().PostEvent(&event);
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetThreadProvision(Internal::DeviceNetworkInfo & netInfo, bool includeCredentials)
+{
+ netInfo = mNetworkInfo;
+
+ if (!includeCredentials)
+ {
+ memset(&netInfo.ThreadMasterKey, 0, sizeof(netInfo.ThreadMasterKey));
+ memset(&netInfo.ThreadPSKc, 0, sizeof(netInfo.ThreadPSKc));
+ netInfo.FieldPresent.ThreadPSKc = false;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+bool ThreadStackManagerImpl::_IsThreadProvisioned()
+{
+ return mNetworkInfo.ThreadNetworkName[0] != '\0';
+}
+
+void ThreadStackManagerImpl::_ErasePersistentInfo()
+{
+ mNetworkInfo = Internal::DeviceNetworkInfo{};
+}
+
+bool ThreadStackManagerImpl::_IsThreadEnabled()
+{
+ bool enabled = false;
+ DeviceRole role;
+ ClientError error;
+
+ SuccessOrExit(error = mThreadApi->GetDeviceRole(role));
+ enabled = (role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED);
+exit:
+ LogClientError(error);
+ return enabled;
+}
+
+bool ThreadStackManagerImpl::_IsThreadAttached()
+{
+ return mAttached;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_SetThreadEnabled(bool val)
+{
+ ClientError error = ClientError::ERROR_NONE;
+
+ if (val)
+ {
+ if (mOperationalDatasetTlv.size() > 0)
+ {
+ SuccessOrExit(error = mThreadApi->SetActiveDatasetTlvs(mOperationalDatasetTlv));
+ SuccessOrExit(error = mThreadApi->Attach([](ClientError result) {
+ // ThreadDevcieRoleChangedHandler should take care of this, so we don't emit another event.
+ ChipLogProgress(DeviceLayer, "Thread attach result %d", result);
+ }));
+ }
+ else
+ {
+ std::vector<uint8_t> masterkey(std::begin(mNetworkInfo.ThreadMasterKey), std::end(mNetworkInfo.ThreadMasterKey));
+ std::vector<uint8_t> pskc;
+ uint64_t extPanId = UINT64_MAX;
+ uint32_t channelMask = UINT32_MAX;
+
+ if (mNetworkInfo.FieldPresent.ThreadExtendedPANId)
+ {
+ extPanId = 0;
+ for (size_t i = 0; i < extPanId; i++)
+ {
+ extPanId <<= CHAR_BIT;
+ extPanId |= mNetworkInfo.ThreadExtendedPANId[i];
+ }
+ }
+ if (mNetworkInfo.FieldPresent.ThreadPSKc)
+ {
+ pskc = std::vector<uint8_t>(std::begin(mNetworkInfo.ThreadPSKc), std::end(mNetworkInfo.ThreadPSKc));
+ }
+ if (mNetworkInfo.ThreadChannel != Internal::kThreadChannel_NotSpecified)
+ {
+ channelMask = 1 << mNetworkInfo.ThreadChannel;
+ }
+
+ if (mNetworkInfo.FieldPresent.ThreadMeshPrefix)
+ {
+ std::array<uint8_t, Internal::kThreadMeshPrefixLength> prefix;
+
+ std::copy(std::begin(mNetworkInfo.ThreadMeshPrefix), std::end(mNetworkInfo.ThreadMeshPrefix), std::begin(prefix));
+ SuccessOrExit(error = mThreadApi->SetMeshLocalPrefix(prefix));
+ }
+
+ mThreadApi->Attach(mNetworkInfo.ThreadNetworkName, mNetworkInfo.ThreadPANId, extPanId, masterkey, pskc, channelMask,
+ [](ClientError result) { ChipLogProgress(DeviceLayer, "Thread attach result %d", result); });
+ }
+ }
+ else
+ {
+ mThreadApi->Reset();
+ }
+exit:
+ LogClientError(error);
+ return OTBR_TO_CHIP_ERROR(error);
+}
+
+ConnectivityManager::ThreadDeviceType ThreadStackManagerImpl::_GetThreadDeviceType()
+{
+ ClientError error;
+ DeviceRole role;
+ LinkModeConfig linkMode;
+ ConnectivityManager::ThreadDeviceType type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
+
+ SuccessOrExit(error = mThreadApi->GetDeviceRole(role));
+
+ switch (role)
+ {
+ case DeviceRole::OTBR_DEVICE_ROLE_DISABLED:
+ case DeviceRole::OTBR_DEVICE_ROLE_DETACHED:
+ break;
+ case DeviceRole::OTBR_DEVICE_ROLE_CHILD:
+ SuccessOrExit(error = mThreadApi->GetLinkMode(linkMode));
+ if (!linkMode.mRxOnWhenIdle)
+ {
+ type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_SleepyEndDevice;
+ }
+ else
+ {
+ type = linkMode.mDeviceType ? ConnectivityManager::ThreadDeviceType::kThreadDeviceType_FullEndDevice
+ : ConnectivityManager::ThreadDeviceType::kThreadDeviceType_MinimalEndDevice;
+ }
+ break;
+ case DeviceRole::OTBR_DEVICE_ROLE_ROUTER:
+ case DeviceRole::OTBR_DEVICE_ROLE_LEADER:
+ type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_Router;
+ break;
+ default:
+ ChipLogError(DeviceLayer, "Unknown Thread role: %d", static_cast<int>(role));
+ break;
+ }
+
+exit:
+ LogClientError(error);
+ return type;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType)
+{
+ LinkModeConfig linkMode{ true, true, true };
+ ClientError error = ClientError::ERROR_NONE;
+
+ if (deviceType == ConnectivityManager::ThreadDeviceType::kThreadDeviceType_MinimalEndDevice)
+ {
+ linkMode.mNetworkData = false;
+ }
+ else if (deviceType == ConnectivityManager::ThreadDeviceType::kThreadDeviceType_SleepyEndDevice)
+ {
+ linkMode.mRxOnWhenIdle = false;
+ linkMode.mNetworkData = false;
+ }
+
+ if (!linkMode.mNetworkData)
+ {
+ error = mThreadApi->SetLinkMode(linkMode);
+ }
+
+ LogClientError(error);
+ return OTBR_TO_CHIP_ERROR(error);
+}
+
+void ThreadStackManagerImpl::_GetThreadPollingConfig(ConnectivityManager::ThreadPollingConfig & pollingConfig)
+{
+ (void) pollingConfig;
+
+ ChipLogError(DeviceLayer, "Polling config is not supported on linux");
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_SetThreadPollingConfig(const ConnectivityManager::ThreadPollingConfig & pollingConfig)
+{
+ (void) pollingConfig;
+
+ ChipLogError(DeviceLayer, "Polling config is not supported on linux");
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+bool ThreadStackManagerImpl::_HaveMeshConnectivity()
+{
+ DeviceRole role;
+ ClientError error;
+ bool hasConnectivity = false;
+ std::vector<NeighborInfo> neighbors;
+
+ SuccessOrExit(error = mThreadApi->GetDeviceRole(role));
+ VerifyOrExit(role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED, );
+ if (role == DeviceRole::OTBR_DEVICE_ROLE_CHILD || role == DeviceRole::OTBR_DEVICE_ROLE_ROUTER)
+ {
+ hasConnectivity = true;
+ }
+
+ SuccessOrExit(error = mThreadApi->GetNeighborTable(neighbors));
+ for (const auto & neighbor : neighbors)
+ {
+ if (!neighbor.mIsChild)
+ {
+ hasConnectivity = true;
+ break;
+ }
+ }
+
+exit:
+ LogClientError(error);
+ return hasConnectivity;
+}
+
+void ThreadStackManagerImpl::_OnMessageLayerActivityChanged(bool messageLayerIsActive)
+{
+ (void) messageLayerIsActive;
+}
+
+void ThreadStackManagerImpl::_OnCHIPoBLEAdvertisingStart() {}
+
+void ThreadStackManagerImpl::_OnCHIPoBLEAdvertisingStop() {}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadStatsCounters()
+{
+ // TODO: implement after we decide on the profiling protocol
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyMinimal()
+{
+ // TODO: implement after we decide on the profiling protocol
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyFull()
+{
+ // TODO: implement after we decide on the profiling protocol
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetPrimary802154MACAddress(uint8_t * buf)
+{
+ uint64_t extAddr;
+ ClientError error;
+ SuccessOrExit(error = mThreadApi->GetExtendedAddress(extAddr));
+
+ for (size_t i = 0; i < sizeof(extAddr); i++)
+ {
+ buf[sizeof(uint64_t) - i - 1] = (extAddr & UINT8_MAX);
+ extAddr >>= CHAR_BIT;
+ }
+
+exit:
+ LogClientError(error);
+ return OTBR_TO_CHIP_ERROR(error);
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetFactoryAssignedEUI64(uint8_t (&buf)[8])
+{
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_GetExternalIPv6Address(chip::Inet::IPAddress & addr)
+{
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+CHIP_ERROR ThreadStackManagerImpl::_JoinerStart()
+{
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+}
+
+ThreadStackManager & ThreadStackMgr()
+{
+ return chip::DeviceLayer::ThreadStackManagerImpl::sInstance;
+}
+
+ThreadStackManagerImpl & ThreadStackMgrImpl()
+{
+ return chip::DeviceLayer::ThreadStackManagerImpl::sInstance;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include "platform/internal/CHIPDeviceLayerInternal.h"
+
+#include "dbus/client/thread_api_dbus.hpp"
+#include "platform/internal/DeviceNetworkInfo.h"
+
+namespace chip {
+namespace DeviceLayer {
+
+class ThreadStackManagerImpl : public ThreadStackManager
+{
+public:
+ ThreadStackManagerImpl();
+
+ CHIP_ERROR _InitThreadStack();
+ void _ProcessThreadActivity();
+
+ CHIP_ERROR _StartThreadTask() { return CHIP_NO_ERROR; } // Intentionally left blank
+ void _LockThreadStack() {} // Intentionally left blank
+ bool _TryLockThreadStack() { return false; } // Intentionally left blank
+ void _UnlockThreadStack() {} // Intentionally left blank
+
+ bool _HaveRouteToAddress(const Inet::IPAddress & destAddr);
+
+ void _OnPlatformEvent(const ChipDeviceEvent * event);
+
+ CHIP_ERROR _GetThreadProvision(Internal::DeviceNetworkInfo & netInfo, bool includeCredentials);
+
+ CHIP_ERROR _SetThreadProvision(const Internal::DeviceNetworkInfo & netInfo);
+
+ CHIP_ERROR _SetThreadProvision(const uint8_t * operationalDataset, size_t operationalDatasetLen);
+
+ void _ErasePersistentInfo();
+
+ bool _IsThreadProvisioned();
+
+ bool _IsThreadEnabled();
+
+ bool _IsThreadAttached();
+
+ CHIP_ERROR _SetThreadEnabled(bool val);
+
+ ConnectivityManager::ThreadDeviceType _GetThreadDeviceType();
+
+ CHIP_ERROR _SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType);
+
+ void _GetThreadPollingConfig(ConnectivityManager::ThreadPollingConfig & pollingConfig);
+
+ CHIP_ERROR _SetThreadPollingConfig(const ConnectivityManager::ThreadPollingConfig & pollingConfig);
+
+ bool _HaveMeshConnectivity();
+
+ void _OnMessageLayerActivityChanged(bool messageLayerIsActive);
+
+ void _OnCHIPoBLEAdvertisingStart();
+
+ void _OnCHIPoBLEAdvertisingStop();
+
+ CHIP_ERROR _GetAndLogThreadStatsCounters();
+
+ CHIP_ERROR _GetAndLogThreadTopologyMinimal();
+
+ CHIP_ERROR _GetAndLogThreadTopologyFull();
+
+ CHIP_ERROR _GetPrimary802154MACAddress(uint8_t * buf);
+
+ CHIP_ERROR _GetFactoryAssignedEUI64(uint8_t (&buf)[8]);
+
+ CHIP_ERROR _GetExternalIPv6Address(chip::Inet::IPAddress & addr);
+
+ CHIP_ERROR _JoinerStart();
+
+ ~ThreadStackManagerImpl() = default;
+
+ static ThreadStackManagerImpl sInstance;
+
+private:
+ struct DBusConnectionDeleter
+ {
+ void operator()(DBusConnection * aConnection) { dbus_connection_unref(aConnection); }
+ };
+
+ using UniqueDBusConnection = std::unique_ptr<DBusConnection, DBusConnectionDeleter>;
+
+ void _ThreadDevcieRoleChangedHandler(otbr::DBus::DeviceRole role);
+
+ std::unique_ptr<otbr::DBus::ThreadApiDBus> mThreadApi;
+ UniqueDBusConnection mConnection;
+ std::vector<uint8_t> mOperationalDatasetTlv;
+ Internal::DeviceNetworkInfo mNetworkInfo;
+ bool mAttached;
+ std::thread mDBusEventLoop;
+};
+
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+# Copyright (c) 2020 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+chip_device_platform = "linux"
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AdapterIterator.h"
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+AdapterIterator::~AdapterIterator()
+{
+ if (mManager != nullptr)
+ {
+ g_object_unref(mManager);
+ }
+
+ if (mObjectList != nullptr)
+ {
+ g_list_free_full(mObjectList, g_object_unref);
+ }
+
+ if (mCurrent.adapter != nullptr)
+ {
+ g_object_unref(mCurrent.adapter);
+ mCurrent.adapter = nullptr;
+ }
+}
+
+void AdapterIterator::Initialize()
+{
+ GError * error = nullptr;
+
+ mManager = g_dbus_object_manager_client_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+ BLUEZ_INTERFACE, "/", bluez_object_manager_client_get_proxy_type,
+ nullptr /* unused user data in the Proxy Type Func */,
+ nullptr /*destroy notify */, nullptr /* cancellable */, &error);
+
+ VerifyOrExit(mManager != nullptr, ChipLogError(DeviceLayer, "Failed to get DBUS object manager for listing adapters."));
+
+ mObjectList = g_dbus_object_manager_get_objects(mManager);
+ mCurrentListItem = mObjectList;
+
+exit:
+ if (error != nullptr)
+ {
+ ChipLogError(DeviceLayer, "DBus error: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+bool AdapterIterator::Advance()
+{
+ if (mCurrentListItem == nullptr)
+ {
+ return false;
+ }
+
+ while (mCurrentListItem != nullptr)
+ {
+ BluezAdapter1 * adapter = bluez_object_get_adapter1(BLUEZ_OBJECT(mCurrentListItem->data));
+ if (adapter == nullptr)
+ {
+ mCurrentListItem = mCurrentListItem->next;
+ continue;
+ }
+
+ // PATH is of the for BLUEZ_PATH / hci<nr>, i.e. like
+ // '/org/bluez/hci0'
+ // Index represents the number after hci
+ const char * path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(adapter));
+ unsigned index = 0;
+
+ if (sscanf(path, BLUEZ_PATH "/hci%u", &index) != 1)
+ {
+ ChipLogError(DeviceLayer, "Failed to extract HCI index from '%s'", path);
+ index = 0;
+ }
+
+ if (mCurrent.adapter != nullptr)
+ {
+ g_object_unref(mCurrent.adapter);
+ mCurrent.adapter = nullptr;
+ }
+
+ mCurrent.index = index;
+ mCurrent.address = bluez_adapter1_get_address(adapter);
+ mCurrent.alias = bluez_adapter1_get_alias(adapter);
+ mCurrent.name = bluez_adapter1_get_name(adapter);
+ mCurrent.powered = bluez_adapter1_get_powered(adapter);
+ mCurrent.adapter = adapter;
+
+ mCurrentListItem = mCurrentListItem->next;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool AdapterIterator::Next()
+{
+ if (mManager == nullptr)
+ {
+ Initialize();
+ }
+
+ return Advance();
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Types.h"
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+/// Iterates over available BlueZ adapters
+///
+/// Usage example:
+///
+/// AdapterIterator iterator;
+/// while (iterator.Next()) {
+/// std::cout << iterator.GetAddress() << std::endl;
+/// }
+///
+/// Data is provided through the bluez dbus interface. You can view
+/// this data in the commandline using commands such as:
+///
+/// busctl introspect org.bluez /org/bluez/hci0
+class AdapterIterator
+{
+public:
+ ~AdapterIterator();
+
+ /// Moves to the next DBUS interface.
+ ///
+ /// MUST be called before any of the 'current value' methods are
+ /// used (iterator gets initialized on the first call of Next).
+ bool Next();
+
+ // Information about the current value. Safe to call only after
+ // "Next" has returned true.
+ uint32_t GetIndex() const { return mCurrent.index; }
+ const char * GetAddress() const { return mCurrent.address.c_str(); }
+ const char * GetAlias() const { return mCurrent.alias.c_str(); }
+ const char * GetName() const { return mCurrent.name.c_str(); }
+ bool IsPowered() const { return mCurrent.powered; }
+ BluezAdapter1 * GetAdapter() const { return mCurrent.adapter; }
+
+private:
+ /// Sets up the DBUS manager and loads the list
+ void Initialize();
+
+ /// Loads the next value in the list.
+ ///
+ /// Returns true if a value could be loaded, false if no more items to
+ /// iterate through.
+ bool Advance();
+
+ static constexpr size_t kMaxAddressLength = 19; // xx:xx:xx:xx:xx:xx
+ static constexpr size_t kMaxNameLength = 64;
+
+ GDBusObjectManager * mManager = nullptr; // DBus connection
+ GList * mObjectList = nullptr; // listing of objects on the bus
+ GList * mCurrentListItem = nullptr; // current item viewed in the list
+
+ // data valid only if Next() returns true
+ struct
+ {
+ uint32_t index;
+ std::string address;
+ std::string alias;
+ std::string name;
+ bool powered;
+ BluezAdapter1 * adapter;
+ } mCurrent = { 0 };
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#include <platform/CHIPDeviceConfig.h>
+#include <platform/Linux/dbus/bluez/DbusBluez.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+/**
+ * Helper class to iterate over a list of Bluez objects.
+ */
+class BluezObjectIterator
+{
+public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = BluezObject;
+ using pointer = BluezObject *;
+ using reference = BluezObject &;
+
+ BluezObjectIterator() = default;
+ explicit BluezObjectIterator(GList * position) : mPosition(position) {}
+
+ reference operator*() const { return *BLUEZ_OBJECT(mPosition->data); }
+ pointer operator->() const { return BLUEZ_OBJECT(mPosition->data); }
+ bool operator==(const BluezObjectIterator & other) const { return mPosition == other.mPosition; }
+ bool operator!=(const BluezObjectIterator & other) const { return mPosition != other.mPosition; }
+
+ BluezObjectIterator & operator++()
+ {
+ mPosition = mPosition->next;
+ return *this;
+ }
+
+ BluezObjectIterator operator++(int)
+ {
+ const auto currentPosition = mPosition;
+ mPosition = mPosition->next;
+ return BluezObjectIterator(currentPosition);
+ }
+
+private:
+ GList * mPosition = nullptr;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#include <platform/CHIPDeviceConfig.h>
+#include <platform/Linux/dbus/bluez/DbusBluez.h>
+#include <support/logging/CHIPLogging.h>
+
+#include "BluezObjectIterator.h"
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+/**
+ * C++ wrapper for a Bluez object list based on a object manager
+ */
+class BluezObjectList
+{
+public:
+ explicit BluezObjectList(GDBusObjectManager * manager) { Initialize(manager); }
+
+ ~BluezObjectList() { g_list_free_full(mObjectList, g_object_unref); }
+
+ BluezObjectIterator begin() const { return BluezObjectIterator(mObjectList); }
+ BluezObjectIterator end() const { return BluezObjectIterator(); }
+
+protected:
+ BluezObjectList() {}
+
+ void Initialize(GDBusObjectManager * manager)
+ {
+ if (manager == nullptr)
+ {
+ ChipLogError(DeviceLayer, "Manager is NULL in %s", __func__);
+ return;
+ }
+
+ mObjectList = g_dbus_object_manager_get_objects(manager);
+ }
+
+private:
+ GList * mObjectList = nullptr;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ChipDeviceScanner.h"
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include "BluezObjectList.h"
+#include "MainLoop.h"
+#include "Types.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+namespace {
+
+struct GObjectUnref
+{
+ template <typename T>
+ void operator()(T * value)
+ {
+ g_object_unref(value);
+ }
+};
+
+using GCancellableUniquePtr = std::unique_ptr<GCancellable, GObjectUnref>;
+using GDBusObjectManagerUniquePtr = std::unique_ptr<GDBusObjectManager, GObjectUnref>;
+
+/// Retrieve CHIP device identification info from the device advertising data
+bool BluezGetChipDeviceInfo(BluezDevice1 & aDevice, chip::Ble::ChipBLEDeviceIdentificationInfo & aDeviceInfo)
+{
+ GVariant * serviceData = bluez_device1_get_service_data(&aDevice);
+ VerifyOrReturnError(serviceData != nullptr, false);
+
+ GVariant * dataValue = g_variant_lookup_value(serviceData, CHIP_BLE_UUID_SERVICE_STRING, nullptr);
+ VerifyOrReturnError(dataValue != nullptr, false);
+
+ size_t dataLen = 0;
+ const void * dataBytes = g_variant_get_fixed_array(dataValue, &dataLen, sizeof(uint8_t));
+ VerifyOrReturnError(dataBytes != nullptr && dataLen >= sizeof(aDeviceInfo), false);
+
+ memcpy(&aDeviceInfo, dataBytes, sizeof(aDeviceInfo));
+ return true;
+}
+
+} // namespace
+
+ChipDeviceScanner::ChipDeviceScanner(GDBusObjectManager * manager, BluezAdapter1 * adapter, GCancellable * cancellable,
+ ChipDeviceScannerDelegate * delegate) :
+ mManager(manager),
+ mAdapter(adapter), mCancellable(cancellable), mDelegate(delegate)
+{
+ g_object_ref(mAdapter);
+ g_object_ref(mCancellable);
+ g_object_ref(mManager);
+}
+
+ChipDeviceScanner::~ChipDeviceScanner()
+{
+ StopScan();
+
+ // In case the timeout timer is still active
+ chip::DeviceLayer::SystemLayer.CancelTimer(TimerExpiredCallback, this);
+
+ g_object_unref(mManager);
+ g_object_unref(mCancellable);
+ g_object_unref(mAdapter);
+
+ mManager = nullptr;
+ mAdapter = nullptr;
+ mCancellable = nullptr;
+ mDelegate = nullptr;
+}
+
+std::unique_ptr<ChipDeviceScanner> ChipDeviceScanner::Create(BluezAdapter1 * adapter, ChipDeviceScannerDelegate * delegate)
+{
+ GError * error = nullptr;
+
+ GCancellableUniquePtr cancellable(g_cancellable_new(), GObjectUnref());
+
+ if (!cancellable)
+ {
+ return std::unique_ptr<ChipDeviceScanner>();
+ }
+
+ GDBusObjectManagerUniquePtr manager(
+ g_dbus_object_manager_client_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, BLUEZ_INTERFACE,
+ "/", bluez_object_manager_client_get_proxy_type,
+ nullptr /* unused user data in the Proxy Type Func */,
+ nullptr /*destroy notify */, cancellable.get(), &error),
+ GObjectUnref());
+ if (!manager)
+ {
+ ChipLogError(Ble, "Failed to get DBUS object manager for device scanning: %s", error->message);
+ g_error_free(error);
+ return std::unique_ptr<ChipDeviceScanner>();
+ }
+
+ return std::make_unique<ChipDeviceScanner>(manager.get(), adapter, cancellable.get(), delegate);
+}
+
+CHIP_ERROR ChipDeviceScanner::StartScan(unsigned timeoutMs)
+{
+ ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE);
+
+ ReturnErrorOnFailure(MainLoop::Instance().EnsureStarted());
+
+ mIsScanning = true; // optimistic, to allow all callbacks to check this
+ if (!MainLoop::Instance().Schedule(MainLoopStartScan, this))
+ {
+ ChipLogError(Ble, "Failed to schedule BLE scan start.");
+ mIsScanning = false;
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ CHIP_ERROR err = chip::DeviceLayer::SystemLayer.StartTimer(timeoutMs, TimerExpiredCallback, static_cast<void *>(this));
+
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Ble, "Failed to schedule scan timeout.");
+ StopScan();
+ return err;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+void ChipDeviceScanner::TimerExpiredCallback(chip::System::Layer * layer, void * appState, chip::System::Error error)
+{
+ static_cast<ChipDeviceScanner *>(appState)->StopScan();
+}
+
+CHIP_ERROR ChipDeviceScanner::StopScan()
+{
+ ReturnErrorCodeIf(!mIsScanning, CHIP_NO_ERROR);
+ ReturnErrorCodeIf(mIsStopping, CHIP_NO_ERROR);
+ mIsStopping = true;
+ g_cancellable_cancel(mCancellable); // in case we are currently running a scan
+
+ if (mObjectAddedSignal)
+ {
+ g_signal_handler_disconnect(mManager, mObjectAddedSignal);
+ mObjectAddedSignal = 0;
+ }
+
+ if (mInterfaceChangedSignal)
+ {
+ g_signal_handler_disconnect(mManager, mInterfaceChangedSignal);
+ mInterfaceChangedSignal = 0;
+ }
+
+ if (!MainLoop::Instance().ScheduleAndWait(MainLoopStopScan, this))
+ {
+ ChipLogError(Ble, "Failed to schedule BLE scan stop.");
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+int ChipDeviceScanner::MainLoopStopScan(ChipDeviceScanner * self)
+{
+ GError * error = nullptr;
+
+ if (!bluez_adapter1_call_stop_discovery_sync(self->mAdapter, nullptr /* not cancellable */, &error))
+ {
+ ChipLogError(Ble, "Failed to stop discovery %s", error->message);
+ g_error_free(error);
+ }
+ ChipDeviceScannerDelegate * delegate = self->mDelegate;
+ self->mIsScanning = false;
+
+ // callback is explicitly allowed to delete the scanner (hence no more
+ // references to 'self' here)
+ delegate->OnScanComplete();
+
+ return 0;
+}
+
+void ChipDeviceScanner::SignalObjectAdded(GDBusObjectManager * manager, GDBusObject * object, ChipDeviceScanner * self)
+{
+ self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
+}
+
+void ChipDeviceScanner::SignalInterfaceChanged(GDBusObjectManagerClient * manager, GDBusObjectProxy * object,
+ GDBusProxy * aInterface, GVariant * aChangedProperties,
+ const gchar * const * aInvalidatedProps, ChipDeviceScanner * self)
+{
+ self->ReportDevice(bluez_object_get_device1(BLUEZ_OBJECT(object)));
+}
+
+void ChipDeviceScanner::ReportDevice(BluezDevice1 * device)
+{
+ if (device == nullptr)
+ {
+ return;
+ }
+
+ if (strcmp(bluez_device1_get_adapter(device), g_dbus_proxy_get_object_path(G_DBUS_PROXY(mAdapter))) != 0)
+ {
+ return;
+ }
+
+ chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo;
+
+ if (!BluezGetChipDeviceInfo(*device, deviceInfo))
+ {
+ ChipLogDetail(Ble, "Device %s does not look like a CHIP device.", bluez_device1_get_address(device));
+ return;
+ }
+
+ mDelegate->OnDeviceScanned(device, deviceInfo);
+}
+
+int ChipDeviceScanner::MainLoopStartScan(ChipDeviceScanner * self)
+{
+ GError * error = nullptr;
+
+ self->mObjectAddedSignal = g_signal_connect(self->mManager, "object-added", G_CALLBACK(SignalObjectAdded), self);
+ self->mInterfaceChangedSignal =
+ g_signal_connect(self->mManager, "interface-proxy-properties-changed", G_CALLBACK(SignalInterfaceChanged), self);
+
+ ChipLogProgress(Ble, "BLE scanning through known devices.");
+ for (BluezObject & object : BluezObjectList(self->mManager))
+ {
+ self->ReportDevice(bluez_object_get_device1(&object));
+ }
+
+ ChipLogProgress(Ble, "BLE initiating scan.");
+ if (!bluez_adapter1_call_start_discovery_sync(self->mAdapter, self->mCancellable, &error))
+ {
+ ChipLogError(Ble, "Failed to start discovery: %s", error->message);
+ g_error_free(error);
+
+ self->mIsScanning = false;
+ self->mDelegate->OnScanComplete();
+ }
+
+ return 0;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <platform/CHIPDeviceConfig.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include <glib.h>
+#include <memory>
+
+#include <ble/CHIPBleServiceData.h>
+#include <core/CHIPError.h>
+#include <platform/Linux/dbus/bluez/DbusBluez.h>
+#include <system/SystemLayer.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+/// Receives callbacks when chip devices are being scanned
+class ChipDeviceScannerDelegate
+{
+public:
+ virtual ~ChipDeviceScannerDelegate() {}
+
+ // Called when a CHIP device was found
+ virtual void OnDeviceScanned(BluezDevice1 * device, const chip::Ble::ChipBLEDeviceIdentificationInfo & info) = 0;
+
+ // Called when a scan was completed (stopped or timed out)
+ virtual void OnScanComplete() = 0;
+};
+
+/// Allows scanning for CHIP devices
+///
+/// Will perform scan operations and call back whenever a device is discovered.
+class ChipDeviceScanner
+{
+public:
+ /// NOTE: prefer to use the ::Create method instead direct constructor calling.
+ ChipDeviceScanner(GDBusObjectManager * manager, BluezAdapter1 * adapter, GCancellable * cancellable,
+ ChipDeviceScannerDelegate * delegate);
+
+ ChipDeviceScanner(ChipDeviceScanner &&) = default;
+ ChipDeviceScanner(const ChipDeviceScanner &) = delete;
+ ChipDeviceScanner & operator=(const ChipDeviceScanner &) = delete;
+
+ ~ChipDeviceScanner();
+
+ /// Initiate a scan for devices, with the given timeout
+ CHIP_ERROR StartScan(unsigned timeoutMs);
+
+ /// Stop any currently running scan
+ CHIP_ERROR StopScan();
+
+ /// Create a new device scanner
+ ///
+ /// Convenience method to allocate any required variables.
+ /// On success, maintains a reference to the provided adapter.
+ static std::unique_ptr<ChipDeviceScanner> Create(BluezAdapter1 * adapter, ChipDeviceScannerDelegate * delegate);
+
+private:
+ static void TimerExpiredCallback(chip::System::Layer * layer, void * appState, chip::System::Error error);
+ static int MainLoopStartScan(ChipDeviceScanner * self);
+ static int MainLoopStopScan(ChipDeviceScanner * self);
+ static void SignalObjectAdded(GDBusObjectManager * manager, GDBusObject * object, ChipDeviceScanner * self);
+ static void SignalInterfaceChanged(GDBusObjectManagerClient * manager, GDBusObjectProxy * object, GDBusProxy * aInterface,
+ GVariant * aChangedProperties, const gchar * const * aInvalidatedProps,
+ ChipDeviceScanner * self);
+
+ /// Check if a given device is a CHIP device and if yes, report it as discovered
+ void ReportDevice(BluezDevice1 * device);
+
+ GDBusObjectManager * mManager = nullptr;
+ BluezAdapter1 * mAdapter = nullptr;
+ GCancellable * mCancellable = nullptr;
+ ChipDeviceScannerDelegate * mDelegate = nullptr;
+ gulong mObjectAddedSignal = 0;
+ gulong mInterfaceChangedSignal = 0;
+ bool mIsScanning = false;
+ bool mIsStopping = false;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020-2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (c) 2016-2019, 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
+ * Provides Bluez dbus implementatioon for BLE
+ */
+
+#include <ble/BleUUID.h>
+#include <ble/CHIPBleServiceData.h>
+#include <platform/CHIPDeviceLayer.h>
+#include <protocols/Protocols.h>
+#include <setup_payload/AdditionalDataPayloadGenerator.h>
+#include <support/BitFlags.h>
+#include <support/CHIPMem.h>
+#include <support/CHIPMemString.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+#include <cassert>
+#include <errno.h>
+#include <gio/gunixfdlist.h>
+#include <limits>
+#include <stdarg.h>
+#include <strings.h>
+#include <unistd.h>
+#include <utility>
+
+#include <platform/Linux/BLEManagerImpl.h>
+#include <support/CodeUtils.h>
+#include <system/TLVPacketBufferBackingStore.h>
+
+#include "BluezObjectIterator.h"
+#include "BluezObjectList.h"
+#include "Helper.h"
+#include "MainLoop.h"
+
+using namespace ::nl;
+using namespace chip::Protocols;
+using chip::Platform::CopyString;
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+static BluezConnection * GetBluezConnectionViaDevice(BluezEndpoint * apEndpoint);
+
+namespace {
+
+class BluezEndpointObjectList : public BluezObjectList
+{
+public:
+ explicit BluezEndpointObjectList(BluezEndpoint * apEndpoint)
+ {
+ VerifyOrReturn(apEndpoint != nullptr, ChipLogError(DeviceLayer, "apEndpoint is NULL in %s", __func__));
+ Initialize(apEndpoint->mpObjMgr);
+ }
+};
+
+} // namespace
+
+static gboolean BluezAdvertisingRelease(BluezLEAdvertisement1 * aAdv, GDBusMethodInvocation * aInvocation, gpointer apClosure)
+{
+ bool isSuccess = false;
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrExit(aAdv != nullptr, ChipLogError(DeviceLayer, "BluezLEAdvertisement1 is NULL in %s", __func__));
+ ChipLogDetail(DeviceLayer, "Release adv object in %s", __func__);
+
+ g_dbus_object_manager_server_unexport(endpoint->mpRoot, endpoint->mpAdvPath);
+ endpoint->mIsAdvertising = false;
+ isSuccess = true;
+exit:
+
+ return isSuccess ? TRUE : FALSE;
+}
+
+static BluezLEAdvertisement1 * BluezAdvertisingCreate(BluezEndpoint * apEndpoint)
+{
+ BluezLEAdvertisement1 * adv = nullptr;
+ BluezObjectSkeleton * object;
+ GVariant * serviceData;
+ GVariant * serviceUUID;
+ gchar * localName;
+ GVariantBuilder serviceDataBuilder;
+ GVariantBuilder serviceUUIDsBuilder;
+ char * debugStr;
+
+ VerifyOrExit(apEndpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ if (apEndpoint->mpAdvPath == nullptr)
+ apEndpoint->mpAdvPath = g_strdup_printf("%s/advertising", apEndpoint->mpRootPath);
+
+ ChipLogDetail(DeviceLayer, "Create adv object at %s", apEndpoint->mpAdvPath);
+ object = bluez_object_skeleton_new(apEndpoint->mpAdvPath);
+
+ adv = bluez_leadvertisement1_skeleton_new();
+
+ g_variant_builder_init(&serviceDataBuilder, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_init(&serviceUUIDsBuilder, G_VARIANT_TYPE("as"));
+
+ g_variant_builder_add(&serviceDataBuilder, "{sv}", apEndpoint->mpAdvertisingUUID,
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, &apEndpoint->mDeviceIdInfo,
+ sizeof(apEndpoint->mDeviceIdInfo), sizeof(uint8_t)));
+ g_variant_builder_add(&serviceUUIDsBuilder, "s", apEndpoint->mpAdvertisingUUID);
+
+ if (apEndpoint->mpAdapterName != nullptr)
+ localName = g_strdup_printf("%s", apEndpoint->mpAdapterName);
+ else
+ localName = g_strdup_printf("%s%04x", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, getpid() & 0xffff);
+
+ serviceData = g_variant_builder_end(&serviceDataBuilder);
+ serviceUUID = g_variant_builder_end(&serviceUUIDsBuilder);
+
+ debugStr = g_variant_print(serviceData, TRUE);
+ ChipLogDetail(DeviceLayer, "SET service data to %s", debugStr);
+ g_free(debugStr);
+
+ bluez_leadvertisement1_set_type_(adv, (apEndpoint->mType & BLUEZ_ADV_TYPE_CONNECTABLE) ? "peripheral" : "broadcast");
+ // empty manufacturer data
+ // empty solicit UUIDs
+ bluez_leadvertisement1_set_service_data(adv, serviceData);
+ // empty data
+
+ // Setting "Discoverable" to False on the adapter and to True on the advertisement convinces
+ // Bluez to set "BR/EDR Not Supported" flag. Bluez doesn't provide API to do that explicitly
+ // and the flag is necessary to force using LE transport.
+ bluez_leadvertisement1_set_discoverable(adv, (apEndpoint->mType & BLUEZ_ADV_TYPE_SCANNABLE) ? TRUE : FALSE);
+ if (apEndpoint->mType & BLUEZ_ADV_TYPE_SCANNABLE)
+ bluez_leadvertisement1_set_discoverable_timeout(adv, UINT16_MAX);
+
+ // advertising name corresponding to the PID and object path, for debug purposes
+ bluez_leadvertisement1_set_local_name(adv, localName);
+ bluez_leadvertisement1_set_service_uuids(adv, serviceUUID);
+
+ // 0xffff means no appearance
+ bluez_leadvertisement1_set_appearance(adv, 0xffff);
+
+ bluez_leadvertisement1_set_duration(adv, apEndpoint->mDuration);
+ // empty duration, we don't have a clear notion what it would mean to timeslice between toble and anyone else
+ bluez_leadvertisement1_set_timeout(adv, 0);
+ // empty secondary channel for now
+
+ bluez_object_skeleton_set_leadvertisement1(object, adv);
+ g_signal_connect(adv, "handle-release", G_CALLBACK(BluezAdvertisingRelease), apEndpoint);
+
+ g_dbus_object_manager_server_export(apEndpoint->mpRoot, G_DBUS_OBJECT_SKELETON(object));
+ g_object_unref(object);
+
+ BLEManagerImpl::NotifyBLEPeripheralAdvConfiguredComplete(true, nullptr);
+
+exit:
+ return adv;
+}
+
+static void BluezAdvStartDone(GObject * aObject, GAsyncResult * aResult, gpointer apClosure)
+{
+ BluezLEAdvertisingManager1 * advMgr = BLUEZ_LEADVERTISING_MANAGER1(aObject);
+ GError * error = nullptr;
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ gboolean success = FALSE;
+
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ success = bluez_leadvertising_manager1_call_register_advertisement_finish(advMgr, aResult, &error);
+ if (success == FALSE)
+ {
+ g_dbus_object_manager_server_unexport(endpoint->mpRoot, endpoint->mpAdvPath);
+ }
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: RegisterAdvertisement : %s", error->message));
+
+ endpoint->mIsAdvertising = true;
+
+ ChipLogDetail(DeviceLayer, "RegisterAdvertisement complete");
+
+exit:
+ BLEManagerImpl::NotifyBLEPeripheralAdvStartComplete(success == TRUE, nullptr);
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static void BluezAdvStopDone(GObject * aObject, GAsyncResult * aResult, gpointer apClosure)
+{
+ BluezLEAdvertisingManager1 * advMgr = BLUEZ_LEADVERTISING_MANAGER1(aObject);
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ GError * error = nullptr;
+ gboolean success = FALSE;
+
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ success = bluez_leadvertising_manager1_call_unregister_advertisement_finish(advMgr, aResult, &error);
+
+ if (success == FALSE)
+ {
+ g_dbus_object_manager_server_unexport(endpoint->mpRoot, endpoint->mpAdvPath);
+ }
+ else
+ {
+ endpoint->mIsAdvertising = false;
+ }
+
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: UnregisterAdvertisement : %s", error->message));
+
+ ChipLogDetail(DeviceLayer, "UnregisterAdvertisement complete");
+
+exit:
+ BLEManagerImpl::NotifyBLEPeripheralAdvStopComplete(success == TRUE, nullptr);
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static gboolean BluezAdvSetup(BluezEndpoint * endpoint)
+{
+ BluezLEAdvertisement1 * adv;
+
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrExit(endpoint->mIsAdvertising == FALSE, ChipLogError(DeviceLayer, "FAIL: Advertising already enabled in %s", __func__));
+ VerifyOrExit(endpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL endpoint->mpAdapter in %s", __func__));
+
+ adv = BluezAdvertisingCreate(endpoint);
+ VerifyOrExit(adv != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adv in %s", __func__));
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean BluezAdvStart(BluezEndpoint * endpoint)
+{
+ GDBusObject * adapter;
+ BluezLEAdvertisingManager1 * advMgr = nullptr;
+ GVariantBuilder optionsBuilder;
+ GVariant * options;
+
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrExit(!endpoint->mIsAdvertising,
+ ChipLogError(DeviceLayer, "FAIL: Advertising has already been enabled in %s", __func__));
+ VerifyOrExit(endpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL endpoint->mpAdapter in %s", __func__));
+
+ adapter = g_dbus_interface_get_object(G_DBUS_INTERFACE(endpoint->mpAdapter));
+ VerifyOrExit(adapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapter in %s", __func__));
+
+ advMgr = bluez_object_get_leadvertising_manager1(BLUEZ_OBJECT(adapter));
+ VerifyOrExit(advMgr != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL advMgr in %s", __func__));
+
+ g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE("a{sv}"));
+ options = g_variant_builder_end(&optionsBuilder);
+
+ bluez_leadvertising_manager1_call_register_advertisement(advMgr, endpoint->mpAdvPath, options, nullptr, BluezAdvStartDone,
+ endpoint);
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean BluezAdvStop(BluezEndpoint * endpoint)
+{
+ GDBusObject * adapter;
+ BluezLEAdvertisingManager1 * advMgr = nullptr;
+
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrExit(endpoint->mIsAdvertising,
+ ChipLogError(DeviceLayer, "FAIL: Advertising has already been disabled in %s", __func__));
+ VerifyOrExit(endpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL endpoint->mpAdapter in %s", __func__));
+
+ adapter = g_dbus_interface_get_object(G_DBUS_INTERFACE(endpoint->mpAdapter));
+ VerifyOrExit(adapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapter in %s", __func__));
+
+ advMgr = bluez_object_get_leadvertising_manager1(BLUEZ_OBJECT(adapter));
+ VerifyOrExit(advMgr != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL advMgr in %s", __func__));
+
+ bluez_leadvertising_manager1_call_unregister_advertisement(advMgr, endpoint->mpAdvPath, nullptr, BluezAdvStopDone, endpoint);
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean BluezCharacteristicReadValue(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aOptions)
+{
+ GVariant * val;
+ ChipLogDetail(DeviceLayer, "Received BluezCharacteristicReadValue");
+ val = bluez_gatt_characteristic1_get_value(aChar);
+ bluez_gatt_characteristic1_complete_read_value(aChar, aInvocation, val);
+ return TRUE;
+}
+
+#if CHIP_BLUEZ_CHAR_WRITE_VALUE
+static gboolean BluezCharacteristicWriteValue(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aValue, GVariant * aOptions, gpointer apEndpoint)
+{
+ const uint8_t * tmpBuf;
+ uint8_t * buf;
+ size_t len;
+ bool isSuccess = false;
+ BluezConnection * conn = NULL;
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apEndpoint);
+ VerifyOrExit(endpoint != NULL, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ VerifyOrExit(aValue != NULL, ChipLogError(DeviceLayer, "aValue is NULL in %s", __func__));
+
+ conn = GetBluezConnectionViaDevice(endpoint);
+ VerifyOrExit(conn != NULL,
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No CHIP Bluez connection"));
+
+ bluez_gatt_characteristic1_set_value(aChar, g_variant_ref(aValue));
+
+ tmpBuf = (uint8_t *) (g_variant_get_fixed_array(aValue, &len, sizeof(uint8_t)));
+ buf = (uint8_t *) (g_memdup(tmpBuf, len));
+
+ BLEManagerImpl::HandleRXCharWrite(conn, buf, len);
+ bluez_gatt_characteristic1_complete_write_value(aChar, aInvocation);
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+#endif
+
+static gboolean BluezCharacteristicWriteValueError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aValue, GVariant * aOptions, gpointer apClosure)
+{
+ ChipLogDetail(DeviceLayer, "BluezCharacteristicWriteValueError");
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
+ "Write for characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicWriteFD(GIOChannel * aChannel, GIOCondition aCond, gpointer apEndpoint)
+{
+ GVariant * newVal;
+ gchar * buf;
+ ssize_t len;
+ int fd;
+ bool isSuccess = false;
+
+ BluezConnection * conn = static_cast<BluezConnection *>(apEndpoint);
+
+ VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "No CHIP Bluez connection in %s", __func__));
+
+ VerifyOrExit(!(aCond & G_IO_HUP), ChipLogError(DeviceLayer, "INFO: socket disconnected in %s", __func__));
+ VerifyOrExit(!(aCond & (G_IO_ERR | G_IO_NVAL)), ChipLogError(DeviceLayer, "INFO: socket error in %s", __func__));
+ VerifyOrExit(aCond == G_IO_IN, ChipLogError(DeviceLayer, "FAIL: error in %s", __func__));
+
+ ChipLogDetail(DeviceLayer, "c1 %s mtu, %d", __func__, conn->mMtu);
+
+ buf = static_cast<gchar *>(g_malloc(conn->mMtu));
+ fd = g_io_channel_unix_get_fd(aChannel);
+
+ len = read(fd, buf, conn->mMtu);
+
+ VerifyOrExit(len > 0, ChipLogError(DeviceLayer, "FAIL: short read in %s (%d)", __func__, len));
+
+ // Casting len to size_t is safe, since we ensured that it's not negative.
+ newVal = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, buf, static_cast<size_t>(len), sizeof(uint8_t));
+
+ bluez_gatt_characteristic1_set_value(conn->mpC1, newVal);
+ BLEManagerImpl::HandleRXCharWrite(conn, reinterpret_cast<uint8_t *>(buf), static_cast<size_t>(len));
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+
+static void Bluez_gatt_characteristic1_complete_acquire_write_with_fd(GDBusMethodInvocation * invocation, int fd, guint16 mtu)
+{
+ GUnixFDList * fd_list = g_unix_fd_list_new();
+ int index;
+
+ index = g_unix_fd_list_append(fd_list, fd, nullptr);
+
+ g_dbus_method_invocation_return_value_with_unix_fd_list(invocation, g_variant_new("(@hq)", g_variant_new_handle(index), mtu),
+ fd_list);
+}
+
+static gboolean bluezCharacteristicDestroyFD(GIOChannel * aChannel, GIOCondition aCond, gpointer apClosure)
+{
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean BluezCharacteristicAcquireWrite(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aOptions, gpointer apEndpoint)
+{
+ int fds[2] = { -1, -1 };
+ GIOChannel * channel;
+ char * errStr;
+ GVariantDict options;
+ bool isSuccess = false;
+ BluezConnection * conn = nullptr;
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apEndpoint);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ conn = GetBluezConnectionViaDevice(endpoint);
+ VerifyOrExit(conn != nullptr,
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No Chipoble connection"));
+
+ ChipLogDetail(DeviceLayer, "BluezCharacteristicAcquireWrite is called, conn: %p", conn);
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0)
+ {
+ errStr = strerror(errno);
+ ChipLogError(DeviceLayer, "FAIL: socketpair: %s in %s", errStr, __func__);
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "FD creation failed");
+ SuccessOrExit(false);
+ }
+
+ g_variant_dict_init(&options, aOptions);
+ if (g_variant_dict_contains(&options, "mtu") == TRUE)
+ {
+ GVariant * v = g_variant_dict_lookup_value(&options, "mtu", G_VARIANT_TYPE_UINT16);
+ conn->mMtu = g_variant_get_uint16(v);
+ }
+ else
+ {
+ ChipLogError(DeviceLayer, "FAIL: no MTU in options in %s", __func__);
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.InvalidArguments", "MTU negotiation failed");
+ SuccessOrExit(false);
+ }
+
+ channel = g_io_channel_unix_new(fds[0]);
+ g_io_channel_set_encoding(channel, nullptr, nullptr);
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ conn->mC1Channel.mpChannel = channel;
+ conn->mC1Channel.mWatch = g_io_add_watch(channel, static_cast<GIOCondition>(G_IO_HUP | G_IO_IN | G_IO_ERR | G_IO_NVAL),
+ BluezCharacteristicWriteFD, conn);
+
+ bluez_gatt_characteristic1_set_write_acquired(aChar, TRUE);
+
+ Bluez_gatt_characteristic1_complete_acquire_write_with_fd(aInvocation, fds[1], conn->mMtu);
+ close(fds[1]);
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+
+static gboolean BluezCharacteristicAcquireWriteError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aOptions)
+{
+ ChipLogDetail(DeviceLayer, "BluezCharacteristicAcquireWriteError is called");
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
+ "AcquireWrite for characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicAcquireNotify(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aOptions, gpointer apEndpoint)
+{
+ int fds[2] = { -1, -1 };
+ GIOChannel * channel;
+ char * errStr;
+ GVariantDict options;
+ BluezConnection * conn = nullptr;
+ bool isSuccess = false;
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apEndpoint);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ conn = GetBluezConnectionViaDevice(endpoint);
+ VerifyOrExit(conn != nullptr,
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No Chipoble connection"));
+
+ g_variant_dict_init(&options, aOptions);
+ if ((g_variant_dict_contains(&options, "mtu") == TRUE))
+ {
+ GVariant * v = g_variant_dict_lookup_value(&options, "mtu", G_VARIANT_TYPE_UINT16);
+ conn->mMtu = g_variant_get_uint16(v);
+ }
+
+ if (bluez_gatt_characteristic1_get_notifying(aChar))
+ {
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotPermitted", "Already notifying");
+ }
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, fds) < 0)
+ {
+ errStr = strerror(errno);
+ ChipLogError(DeviceLayer, "FAIL: socketpair: %s in %s", errStr, __func__);
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "FD creation failed");
+ SuccessOrExit(false);
+ }
+ channel = g_io_channel_unix_new(fds[0]);
+ g_io_channel_set_encoding(channel, nullptr, nullptr);
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_buffered(channel, FALSE);
+ conn->mC2Channel.mpChannel = channel;
+ conn->mC2Channel.mWatch =
+ g_io_add_watch_full(channel, G_PRIORITY_DEFAULT_IDLE, static_cast<GIOCondition>(G_IO_HUP | G_IO_ERR | G_IO_NVAL),
+ bluezCharacteristicDestroyFD, conn, nullptr);
+
+ bluez_gatt_characteristic1_set_notify_acquired(aChar, TRUE);
+
+ // same reply as for AcquireWrite
+ Bluez_gatt_characteristic1_complete_acquire_write_with_fd(aInvocation, fds[1], conn->mMtu);
+ close(fds[1]);
+
+ conn->mIsNotify = true;
+ BLEManagerImpl::HandleTXCharCCCDWrite(conn);
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+
+static gboolean BluezCharacteristicAcquireNotifyError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ GVariant * aOptions)
+{
+ ChipLogDetail(DeviceLayer, "TRACE: AcquireNotify is called");
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
+ "AcquireNotify for characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicStartNotify(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ gpointer apEndpoint)
+{
+ bool isSuccess = false;
+ BluezConnection * conn = nullptr;
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apEndpoint);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ conn = GetBluezConnectionViaDevice(endpoint);
+ VerifyOrExit(conn != nullptr,
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No Chipoble connection"));
+
+ if (bluez_gatt_characteristic1_get_notifying(aChar) == TRUE)
+ {
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "Characteristic is already subscribed");
+ }
+ else
+ {
+ bluez_gatt_characteristic1_complete_start_notify(aChar, aInvocation);
+ bluez_gatt_characteristic1_set_notifying(aChar, TRUE);
+ conn->mIsNotify = true;
+ BLEManagerImpl::HandleTXCharCCCDWrite(conn);
+ }
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+
+static gboolean BluezCharacteristicStartNotifyError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation)
+{
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.NotSupported",
+ "Subscribing to characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicStopNotify(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ gpointer apEndpoint)
+{
+ bool isSuccess = false;
+ BluezConnection * conn = nullptr;
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apEndpoint);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ conn = GetBluezConnectionViaDevice(endpoint);
+ VerifyOrExit(conn != nullptr,
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "No Chipoble connection"));
+
+ if (bluez_gatt_characteristic1_get_notifying(aChar) == FALSE)
+ {
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "Characteristic is already unsubscribed");
+ }
+ else
+ {
+ bluez_gatt_characteristic1_complete_start_notify(aChar, aInvocation);
+ bluez_gatt_characteristic1_set_notifying(aChar, FALSE);
+ }
+ conn->mIsNotify = false;
+
+ isSuccess = true;
+
+exit:
+ return isSuccess ? TRUE : FALSE;
+}
+
+static gboolean BluezCharacteristicConfirm(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation,
+ gpointer apClosure)
+{
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ BluezConnection * conn = GetBluezConnectionViaDevice(endpoint);
+
+ ChipLogDetail(Ble, "Indication confirmation, %p", conn);
+ BLEManagerImpl::HandleTXComplete(conn);
+
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicStopNotifyError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation)
+{
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed",
+ "Unsubscribing from characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezCharacteristicConfirmError(BluezGattCharacteristic1 * aChar, GDBusMethodInvocation * aInvocation)
+{
+ g_dbus_method_invocation_return_dbus_error(aInvocation, "org.bluez.Error.Failed", "Confirm from characteristic is unsupported");
+ return TRUE;
+}
+
+static gboolean BluezIsDeviceOnAdapter(BluezDevice1 * aDevice, BluezAdapter1 * aAdapter)
+{
+ return strcmp(bluez_device1_get_adapter(aDevice), g_dbus_proxy_get_object_path(G_DBUS_PROXY(aAdapter))) == 0 ? TRUE : FALSE;
+}
+
+static gboolean BluezIsServiceOnDevice(BluezGattService1 * aService, BluezDevice1 * aDevice)
+{
+ return strcmp(bluez_gatt_service1_get_device(aService), g_dbus_proxy_get_object_path(G_DBUS_PROXY(aDevice))) == 0 ? TRUE
+ : FALSE;
+}
+
+static gboolean BluezIsCharOnService(BluezGattCharacteristic1 * aChar, BluezGattService1 * aService)
+{
+ ChipLogDetail(DeviceLayer, "Char1 %s", bluez_gatt_characteristic1_get_service(aChar));
+ ChipLogDetail(DeviceLayer, "Char1 %s", g_dbus_proxy_get_object_path(G_DBUS_PROXY(aService)));
+ return strcmp(bluez_gatt_characteristic1_get_service(aChar), g_dbus_proxy_get_object_path(G_DBUS_PROXY(aService))) == 0 ? TRUE
+ : FALSE;
+}
+
+static void BluezConnectionInit(BluezConnection * apConn)
+{
+ // populate the service and the characteristics
+ GList * objects = nullptr;
+ GList * l;
+ BluezEndpoint * endpoint = nullptr;
+
+ VerifyOrExit(apConn != nullptr, ChipLogError(DeviceLayer, "Bluez connection is NULL in %s", __func__));
+
+ endpoint = apConn->mpEndpoint;
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ if (!endpoint->mIsCentral)
+ {
+ apConn->mpService = BLUEZ_GATT_SERVICE1(g_object_ref(apConn->mpEndpoint->mpService));
+ apConn->mpC1 = BLUEZ_GATT_CHARACTERISTIC1(g_object_ref(endpoint->mpC1));
+ apConn->mpC2 = BLUEZ_GATT_CHARACTERISTIC1(g_object_ref(endpoint->mpC2));
+ }
+ else
+ {
+ objects = g_dbus_object_manager_get_objects(endpoint->mpObjMgr);
+
+ for (l = objects; l != nullptr; l = l->next)
+ {
+ BluezObject * object = BLUEZ_OBJECT(l->data);
+ BluezGattService1 * service = bluez_object_get_gatt_service1(object);
+
+ if (service != nullptr)
+ {
+ if ((BluezIsServiceOnDevice(service, apConn->mpDevice)) == TRUE &&
+ (strcmp(bluez_gatt_service1_get_uuid(service), CHIP_BLE_UUID_SERVICE_STRING) == 0))
+ {
+ apConn->mpService = service;
+ break;
+ }
+ g_object_unref(service);
+ }
+ }
+
+ VerifyOrExit(apConn->mpService != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL service in %s", __func__));
+
+ for (l = objects; l != nullptr; l = l->next)
+ {
+ BluezObject * object = BLUEZ_OBJECT(l->data);
+ BluezGattCharacteristic1 * char1 = bluez_object_get_gatt_characteristic1(object);
+
+ if (char1 != nullptr)
+ {
+ if ((BluezIsCharOnService(char1, apConn->mpService) == TRUE) &&
+ (strcmp(bluez_gatt_characteristic1_get_uuid(char1), CHIP_PLAT_BLE_UUID_C1_STRING) == 0))
+ {
+ apConn->mpC1 = char1;
+ }
+ else if ((BluezIsCharOnService(char1, apConn->mpService) == TRUE) &&
+ (strcmp(bluez_gatt_characteristic1_get_uuid(char1), CHIP_PLAT_BLE_UUID_C2_STRING) == 0))
+ {
+ apConn->mpC2 = char1;
+ }
+ else if ((BluezIsCharOnService(char1, apConn->mpService) == TRUE) &&
+ (strcmp(bluez_gatt_characteristic1_get_uuid(char1), CHIP_PLAT_BLE_UUID_C3_STRING) == 0))
+ {
+ apConn->mpC3 = char1;
+ }
+ else
+ {
+ g_object_unref(char1);
+ }
+ if ((apConn->mpC1 != nullptr) && (apConn->mpC2 != nullptr))
+ {
+ break;
+ }
+ }
+ }
+
+ VerifyOrExit(apConn->mpC1 != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL C1 in %s", __func__));
+ VerifyOrExit(apConn->mpC2 != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL C2 in %s", __func__));
+ }
+
+exit:
+ if (objects != nullptr)
+ g_list_free_full(objects, g_object_unref);
+}
+
+static void BluezOTConnectionDestroy(BluezConnection * aConn)
+{
+ if (aConn)
+ {
+ if (aConn->mpDevice)
+ g_object_unref(aConn->mpDevice);
+ if (aConn->mpService)
+ g_object_unref(aConn->mpService);
+ if (aConn->mpC1)
+ g_object_unref(aConn->mpC1);
+ if (aConn->mpC2)
+ g_object_unref(aConn->mpC2);
+ if (aConn->mpPeerAddress)
+ g_free(aConn->mpPeerAddress);
+ if (aConn->mC1Channel.mWatch > 0)
+ g_source_remove(aConn->mC1Channel.mWatch);
+ if (aConn->mC1Channel.mpChannel)
+ g_io_channel_unref(aConn->mC1Channel.mpChannel);
+ if (aConn->mC2Channel.mWatch > 0)
+ g_source_remove(aConn->mC2Channel.mWatch);
+ if (aConn->mC2Channel.mpChannel)
+ g_io_channel_unref(aConn->mC2Channel.mpChannel);
+
+ g_free(aConn);
+ }
+}
+
+static BluezGattCharacteristic1 * BluezCharacteristicCreate(BluezGattService1 * aService, const char * aCharName,
+ const char * aUUID, GDBusObjectManagerServer * aRoot)
+{
+ char * servicePath = g_strdup(g_dbus_object_get_object_path(g_dbus_interface_get_object(G_DBUS_INTERFACE(aService))));
+ char * charPath = g_strdup_printf("%s/%s", servicePath, aCharName);
+ BluezObjectSkeleton * object;
+ BluezGattCharacteristic1 * characteristic;
+
+ ChipLogDetail(DeviceLayer, "Create characteristic object at %s", charPath);
+ object = bluez_object_skeleton_new(charPath);
+
+ characteristic = bluez_gatt_characteristic1_skeleton_new();
+ bluez_gatt_characteristic1_set_uuid(characteristic, aUUID);
+ bluez_gatt_characteristic1_set_service(characteristic, servicePath);
+
+ bluez_object_skeleton_set_gatt_characteristic1(object, characteristic);
+ g_dbus_object_manager_server_export(aRoot, G_DBUS_OBJECT_SKELETON(object));
+ g_object_unref(object);
+
+ return characteristic;
+}
+
+static void BluezPeripheralRegisterAppDone(GObject * aObject, GAsyncResult * aResult, gpointer apClosure)
+{
+ GError * error = nullptr;
+ BluezGattManager1 * gattMgr = BLUEZ_GATT_MANAGER1(aObject);
+
+ gboolean success = bluez_gatt_manager1_call_register_application_finish(gattMgr, aResult, &error);
+
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: RegisterApplication : %s", error->message));
+
+ BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(true, nullptr);
+ ChipLogDetail(DeviceLayer, "BluezPeripheralRegisterAppDone done");
+
+exit:
+ if (error != nullptr)
+ {
+ BLEManagerImpl::NotifyBLEPeripheralRegisterAppComplete(false, nullptr);
+ g_error_free(error);
+ }
+}
+
+gboolean BluezPeripheralRegisterApp(BluezEndpoint * endpoint)
+{
+ GDBusObject * adapter;
+ BluezGattManager1 * gattMgr;
+ GVariantBuilder optionsBuilder;
+ GVariant * options;
+
+ VerifyOrExit(endpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL endpoint->mpAdapter in %s", __func__));
+
+ adapter = g_dbus_interface_get_object(G_DBUS_INTERFACE(endpoint->mpAdapter));
+ VerifyOrExit(adapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL adapter in %s", __func__));
+
+ gattMgr = bluez_object_get_gatt_manager1(BLUEZ_OBJECT(adapter));
+ VerifyOrExit(gattMgr != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL gattMgr in %s", __func__));
+
+ g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE("a{sv}"));
+ options = g_variant_builder_end(&optionsBuilder);
+
+ bluez_gatt_manager1_call_register_application(gattMgr, endpoint->mpRootPath, options, nullptr, BluezPeripheralRegisterAppDone,
+ nullptr);
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+/// Update the table of open BLE connections whevener a new device is spotted or its attributes have changed.
+static void UpdateConnectionTable(BluezDevice1 * apDevice, BluezEndpoint & aEndpoint)
+{
+ const gchar * objectPath = g_dbus_proxy_get_object_path(G_DBUS_PROXY(apDevice));
+ BluezConnection * connection = static_cast<BluezConnection *>(g_hash_table_lookup(aEndpoint.mpConnMap, objectPath));
+
+ if (connection != nullptr && !bluez_device1_get_connected(apDevice))
+ {
+ ChipLogDetail(DeviceLayer, "Bluez disconnected");
+ BLEManagerImpl::CHIPoBluez_ConnectionClosed(connection);
+ // TODO: the connection object should be released after BLEManagerImpl finishes cleaning up its resources
+ // after the disconnection. Releasing it here doesn't cause any issues, but it's error-prone.
+ BluezOTConnectionDestroy(connection);
+ g_hash_table_remove(aEndpoint.mpConnMap, objectPath);
+ return;
+ }
+
+ if (connection == nullptr && !bluez_device1_get_connected(apDevice) && aEndpoint.mIsCentral)
+ {
+ return;
+ }
+
+ if (connection == nullptr && bluez_device1_get_connected(apDevice) &&
+ (!aEndpoint.mIsCentral || bluez_device1_get_services_resolved(apDevice)))
+ {
+ connection = g_new0(BluezConnection, 1);
+ connection->mpPeerAddress = g_strdup(bluez_device1_get_address(apDevice));
+ connection->mpDevice = static_cast<BluezDevice1 *>(g_object_ref(apDevice));
+ connection->mpEndpoint = &aEndpoint;
+ BluezConnectionInit(connection);
+ aEndpoint.mpPeerDevicePath = g_strdup(objectPath);
+ g_hash_table_insert(aEndpoint.mpConnMap, aEndpoint.mpPeerDevicePath, connection);
+
+ ChipLogDetail(DeviceLayer, "New BLE connection %p, device %s, path %s", connection, connection->mpPeerAddress,
+ aEndpoint.mpPeerDevicePath);
+
+ BLEManagerImpl::HandleNewConnection(connection);
+ }
+}
+
+static void BluezSignalInterfacePropertiesChanged(GDBusObjectManagerClient * aManager, GDBusObjectProxy * aObject,
+ GDBusProxy * aInterface, GVariant * aChangedProperties,
+ const gchar * const * aInvalidatedProps, gpointer apClosure)
+{
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ VerifyOrReturn(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrReturn(endpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL endpoint->mpAdapter in %s", __func__));
+ VerifyOrReturn(strcmp(g_dbus_proxy_get_interface_name(aInterface), DEVICE_INTERFACE) == 0, );
+
+ BluezDevice1 * device = BLUEZ_DEVICE1(aInterface);
+ VerifyOrReturn(BluezIsDeviceOnAdapter(device, endpoint->mpAdapter));
+
+ UpdateConnectionTable(device, *endpoint);
+}
+
+static void BluezHandleNewDevice(BluezDevice1 * device, BluezEndpoint * apEndpoint)
+{
+ VerifyOrExit(apEndpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ if (apEndpoint->mIsCentral)
+ {
+ return;
+ }
+
+ // We need to handle device connection both this function and BluezSignalInterfacePropertiesChanged
+ // When a device is connected for first time, this function will be triggerred.
+ // The future connections for the same device will trigger ``Connect'' property change.
+ // TODO: Factor common code in the two function.
+ BluezConnection * conn;
+ VerifyOrExit(bluez_device1_get_connected(device), ChipLogError(DeviceLayer, "FAIL: device is not connected"));
+
+ conn = static_cast<BluezConnection *>(
+ g_hash_table_lookup(apEndpoint->mpConnMap, g_dbus_proxy_get_object_path(G_DBUS_PROXY(device))));
+ VerifyOrExit(conn == nullptr,
+ ChipLogError(DeviceLayer, "FAIL: connection already tracked: conn: %x new device: %s", conn,
+ g_dbus_proxy_get_object_path(G_DBUS_PROXY(device))));
+
+ conn = g_new0(BluezConnection, 1);
+ conn->mpPeerAddress = g_strdup(bluez_device1_get_address(device));
+ conn->mpDevice = static_cast<BluezDevice1 *>(g_object_ref(device));
+ conn->mpEndpoint = apEndpoint;
+ BluezConnectionInit(conn);
+ apEndpoint->mpPeerDevicePath = g_strdup(g_dbus_proxy_get_object_path(G_DBUS_PROXY(device)));
+ ChipLogDetail(DeviceLayer, "Device %s (Path: %s) Connected", conn->mpPeerAddress, apEndpoint->mpPeerDevicePath);
+ g_hash_table_insert(apEndpoint->mpConnMap, g_strdup(g_dbus_proxy_get_object_path(G_DBUS_PROXY(device))), conn);
+
+exit:
+ return;
+}
+
+static void BluezSignalOnObjectAdded(GDBusObjectManager * aManager, GDBusObject * aObject, BluezEndpoint * endpoint)
+{
+ // TODO: right now we do not handle addition/removal of adapters
+ // Primary focus here is to handle addition of a device
+ BluezDevice1 * device = bluez_object_get_device1(BLUEZ_OBJECT(aObject));
+ if (device == nullptr)
+ {
+ return;
+ }
+
+ if (BluezIsDeviceOnAdapter(device, endpoint->mpAdapter) == TRUE)
+ {
+ BluezHandleNewDevice(device, endpoint);
+ }
+
+ g_object_unref(device);
+}
+
+static void BluezSignalOnObjectRemoved(GDBusObjectManager * aManager, GDBusObject * aObject, gpointer apClosure)
+{
+ // TODO: for Device1, lookup connection, and call otPlatTobleHandleDisconnected
+ // for Adapter1: unclear, crash if this pertains to our adapter? at least null out the endpoint->mpAdapter.
+ // for Characteristic1, or GattService -- handle here via calling otPlatTobleHandleDisconnected, or ignore.
+}
+
+static BluezGattService1 * BluezServiceCreate(gpointer apClosure)
+{
+ BluezObjectSkeleton * object;
+ BluezGattService1 * service;
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+
+ endpoint->mpServicePath = g_strdup_printf("%s/service", endpoint->mpRootPath);
+ ChipLogDetail(DeviceLayer, "CREATE service object at %s", endpoint->mpServicePath);
+ object = bluez_object_skeleton_new(endpoint->mpServicePath);
+
+ service = bluez_gatt_service1_skeleton_new();
+ bluez_gatt_service1_set_uuid(service, "0xFEAF");
+ // device is only valid for remote services
+ bluez_gatt_service1_set_primary(service, TRUE);
+
+ // includes -- unclear whether required. Might be filled in later
+ bluez_object_skeleton_set_gatt_service1(object, service);
+ g_dbus_object_manager_server_export(endpoint->mpRoot, G_DBUS_OBJECT_SKELETON(object));
+ g_object_unref(object);
+
+ return service;
+}
+
+static void bluezObjectsSetup(BluezEndpoint * apEndpoint)
+{
+ GList * objects = nullptr;
+ GList * l;
+ char * expectedPath = nullptr;
+
+ VerifyOrExit(apEndpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ expectedPath = g_strdup_printf("%s/hci%d", BLUEZ_PATH, apEndpoint->mAdapterId);
+ objects = g_dbus_object_manager_get_objects(apEndpoint->mpObjMgr);
+
+ for (l = objects; l != nullptr && apEndpoint->mpAdapter == nullptr; l = l->next)
+ {
+ BluezObject * object = BLUEZ_OBJECT(l->data);
+ GList * interfaces;
+ GList * ll;
+ interfaces = g_dbus_object_get_interfaces(G_DBUS_OBJECT(object));
+
+ for (ll = interfaces; ll != nullptr; ll = ll->next)
+ {
+ if (BLUEZ_IS_ADAPTER1(ll->data))
+ { // we found the adapter
+ BluezAdapter1 * adapter = BLUEZ_ADAPTER1(ll->data);
+ char * addr = const_cast<char *>(bluez_adapter1_get_address(adapter));
+ if (apEndpoint->mpAdapterAddr == nullptr) // no adapter address provided, bind to the hci indicated by nodeid
+ {
+ if (strcmp(g_dbus_proxy_get_object_path(G_DBUS_PROXY(adapter)), expectedPath) == 0)
+ {
+ apEndpoint->mpAdapter = static_cast<BluezAdapter1 *>(g_object_ref(adapter));
+ }
+ }
+ else
+ {
+ if (strcmp(apEndpoint->mpAdapterAddr, addr) == 0)
+ {
+ apEndpoint->mpAdapter = static_cast<BluezAdapter1 *>(g_object_ref(adapter));
+ }
+ }
+ }
+ }
+ g_list_free_full(interfaces, g_object_unref);
+ }
+ VerifyOrExit(apEndpoint->mpAdapter != nullptr, ChipLogError(DeviceLayer, "FAIL: NULL apEndpoint->mpAdapter in %s", __func__));
+ bluez_adapter1_set_powered(apEndpoint->mpAdapter, TRUE);
+
+ // Setting "Discoverable" to False on the adapter and to True on the advertisement convinces
+ // Bluez to set "BR/EDR Not Supported" flag. Bluez doesn't provide API to do that explicitly
+ // and the flag is necessary to force using LE transport.
+ bluez_adapter1_set_discoverable(apEndpoint->mpAdapter, FALSE);
+
+exit:
+ g_list_free_full(objects, g_object_unref);
+ g_free(expectedPath);
+}
+
+static BluezConnection * GetBluezConnectionViaDevice(BluezEndpoint * apEndpoint)
+{
+ BluezConnection * retval =
+ static_cast<BluezConnection *>(g_hash_table_lookup(apEndpoint->mpConnMap, apEndpoint->mpPeerDevicePath));
+ // ChipLogError(DeviceLayer, "acquire connection object %p in (%s)", retval, __func__);
+ return retval;
+}
+
+#if CHIP_BLUEZ_CENTRAL_SUPPORT
+static BluezConnection * BluezCharacteristicGetBluezConnection(BluezGattCharacteristic1 * aChar, GVariant * aOptions,
+ BluezEndpoint * apEndpoint)
+{
+ BluezConnection * retval = NULL;
+ const gchar * path = NULL;
+ GVariantDict options;
+ GVariant * v;
+
+ VerifyOrExit(apEndpoint != NULL, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+ VerifyOrExit(apEndpoint->mIsCentral, );
+
+ /* TODO Unfortunately StartNotify/StopNotify doesn't provide info about
+ * peer device in call params so we need look this up ourselves.
+ */
+ if (aOptions == NULL)
+ {
+ GList * objects;
+ GList * l;
+ GList * ll;
+
+ objects = g_dbus_object_manager_get_objects(apEndpoint->mpObjMgr);
+ for (l = objects; l != NULL; l = l->next)
+ {
+ BluezDevice1 * device = bluez_object_get_device1(BLUEZ_OBJECT(l->data));
+ if (device != NULL)
+ {
+ if (BluezIsDeviceOnAdapter(device, apEndpoint->mpAdapter))
+ {
+ for (ll = objects; ll != NULL; ll = ll->next)
+ {
+ BluezGattService1 * service = bluez_object_get_gatt_service1(BLUEZ_OBJECT(ll->data));
+ if (service != NULL)
+ {
+ if (BluezIsServiceOnDevice(service, device))
+ {
+ if (BluezIsCharOnService(aChar, service))
+ {
+ retval = (BluezConnection *) g_hash_table_lookup(
+ apEndpoint->mpConnMap, g_dbus_proxy_get_object_path(G_DBUS_PROXY(device)));
+ }
+ }
+ g_object_unref(service);
+ if (retval != NULL)
+ break;
+ }
+ }
+ }
+ g_object_unref(device);
+ if (retval != NULL)
+ break;
+ }
+ }
+
+ g_list_free_full(objects, g_object_unref);
+ }
+ else
+ {
+ g_variant_dict_init(&options, aOptions);
+
+ v = g_variant_dict_lookup_value(&options, "device", G_VARIANT_TYPE_OBJECT_PATH);
+
+ VerifyOrExit(v != NULL, ChipLogError(DeviceLayer, "FAIL: No device option in dictionary (%s)", __func__));
+
+ path = g_variant_get_string(v, NULL);
+
+ retval = (BluezConnection *) g_hash_table_lookup(apEndpoint->mpConnMap, path);
+ }
+
+exit:
+ return retval;
+}
+#endif // CHIP_BLUEZ_CENTRAL_SUPPORT
+
+void EndpointCleanup(BluezEndpoint * apEndpoint)
+{
+ if (apEndpoint != nullptr)
+ {
+ if (apEndpoint->mpOwningName != nullptr)
+ {
+ g_free(apEndpoint->mpOwningName);
+ apEndpoint->mpOwningName = nullptr;
+ }
+ if (apEndpoint->mpAdapterName != nullptr)
+ {
+ g_free(apEndpoint->mpAdapterName);
+ apEndpoint->mpAdapterName = nullptr;
+ }
+ if (apEndpoint->mpAdapterAddr != nullptr)
+ {
+ g_free(apEndpoint->mpAdapterAddr);
+ apEndpoint->mpAdapterAddr = nullptr;
+ }
+ if (apEndpoint->mpRootPath != nullptr)
+ {
+ g_free(apEndpoint->mpRootPath);
+ apEndpoint->mpRootPath = nullptr;
+ }
+ if (apEndpoint->mpAdvPath != nullptr)
+ {
+ g_free(apEndpoint->mpAdvPath);
+ apEndpoint->mpAdvPath = nullptr;
+ }
+ if (apEndpoint->mpServicePath != nullptr)
+ {
+ g_free(apEndpoint->mpServicePath);
+ apEndpoint->mpServicePath = nullptr;
+ }
+ if (apEndpoint->mpConnMap != nullptr)
+ {
+ g_hash_table_destroy(apEndpoint->mpConnMap);
+ apEndpoint->mpConnMap = nullptr;
+ }
+ if (apEndpoint->mpAdvertisingUUID != nullptr)
+ {
+ g_free(apEndpoint->mpAdvertisingUUID);
+ apEndpoint->mpAdvertisingUUID = nullptr;
+ }
+ if (apEndpoint->mpPeerDevicePath != nullptr)
+ {
+ g_free(apEndpoint->mpPeerDevicePath);
+ apEndpoint->mpPeerDevicePath = nullptr;
+ }
+ g_free(apEndpoint->mpConnectCancellable);
+ g_free(apEndpoint);
+ }
+}
+
+int BluezObjectsCleanup(BluezEndpoint * apEndpoint)
+{
+ g_object_unref(apEndpoint->mpAdapter);
+ EndpointCleanup(apEndpoint);
+ return 0;
+}
+
+#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
+static void UpdateAdditionalDataCharacteristic(BluezGattCharacteristic1 * characteristic)
+{
+ if (characteristic == nullptr)
+ {
+ return;
+ }
+
+ // Construct the TLV for the additional data
+ GVariant * cValue = nullptr;
+ gpointer data;
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ chip::System::PacketBufferHandle bufferHandle;
+
+ char serialNumber[ConfigurationManager::kMaxSerialNumberLength + 1];
+ size_t serialNumberSize = 0;
+ uint16_t lifetimeCounter = 0;
+ BitFlags<AdditionalDataFields> additionalDataFields;
+
+#if CHIP_ENABLE_ROTATING_DEVICE_ID
+ err = ConfigurationMgr().GetSerialNumber(serialNumber, sizeof(serialNumber), serialNumberSize);
+ SuccessOrExit(err);
+ err = ConfigurationMgr().GetLifetimeCounter(lifetimeCounter);
+ SuccessOrExit(err);
+
+ additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId);
+#endif
+
+ err = AdditionalDataPayloadGenerator().generateAdditionalDataPayload(lifetimeCounter, serialNumber, serialNumberSize,
+ bufferHandle, additionalDataFields);
+ SuccessOrExit(err);
+
+ data = g_memdup(bufferHandle->Start(), bufferHandle->DataLength());
+
+ cValue = g_variant_new_from_data(G_VARIANT_TYPE("ay"), data, bufferHandle->DataLength(), TRUE, g_free, data);
+ bluez_gatt_characteristic1_set_value(characteristic, cValue);
+
+ return;
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(DeviceLayer, "Failed to generate TLV encoded Additional Data", __func__);
+ }
+ return;
+}
+#endif
+
+static void BluezPeripheralObjectsSetup(gpointer apClosure)
+{
+
+ static const char * const c1_flags[] = { "write", nullptr };
+ static const char * const c2_flags[] = { "read", "indicate", nullptr };
+ static const char * const c3_flags[] = { "read", nullptr };
+
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ endpoint->mpService = BluezServiceCreate(apClosure);
+ // C1 characteristic
+ endpoint->mpC1 =
+ BluezCharacteristicCreate(endpoint->mpService, g_strdup("c1"), g_strdup(CHIP_PLAT_BLE_UUID_C1_STRING), endpoint->mpRoot);
+ bluez_gatt_characteristic1_set_flags(endpoint->mpC1, c1_flags);
+
+ g_signal_connect(endpoint->mpC1, "handle-read-value", G_CALLBACK(BluezCharacteristicReadValue), apClosure);
+ g_signal_connect(endpoint->mpC1, "handle-write-value", G_CALLBACK(BluezCharacteristicWriteValueError), NULL);
+ g_signal_connect(endpoint->mpC1, "handle-acquire-write", G_CALLBACK(BluezCharacteristicAcquireWrite), apClosure);
+ g_signal_connect(endpoint->mpC1, "handle-acquire-notify", G_CALLBACK(BluezCharacteristicAcquireNotifyError), NULL);
+ g_signal_connect(endpoint->mpC1, "handle-start-notify", G_CALLBACK(BluezCharacteristicStartNotifyError), NULL);
+ g_signal_connect(endpoint->mpC1, "handle-stop-notify", G_CALLBACK(BluezCharacteristicStopNotifyError), NULL);
+ g_signal_connect(endpoint->mpC1, "handle-confirm", G_CALLBACK(BluezCharacteristicConfirmError), NULL);
+
+ endpoint->mpC2 =
+ BluezCharacteristicCreate(endpoint->mpService, g_strdup("c2"), g_strdup(CHIP_PLAT_BLE_UUID_C2_STRING), endpoint->mpRoot);
+ bluez_gatt_characteristic1_set_flags(endpoint->mpC2, c2_flags);
+ g_signal_connect(endpoint->mpC2, "handle-read-value", G_CALLBACK(BluezCharacteristicReadValue), apClosure);
+ g_signal_connect(endpoint->mpC2, "handle-write-value", G_CALLBACK(BluezCharacteristicWriteValueError), NULL);
+ g_signal_connect(endpoint->mpC2, "handle-acquire-write", G_CALLBACK(BluezCharacteristicAcquireWriteError), NULL);
+ g_signal_connect(endpoint->mpC2, "handle-acquire-notify", G_CALLBACK(BluezCharacteristicAcquireNotify), apClosure);
+ g_signal_connect(endpoint->mpC2, "handle-start-notify", G_CALLBACK(BluezCharacteristicStartNotify), apClosure);
+ g_signal_connect(endpoint->mpC2, "handle-stop-notify", G_CALLBACK(BluezCharacteristicStopNotify), apClosure);
+ g_signal_connect(endpoint->mpC2, "handle-confirm", G_CALLBACK(BluezCharacteristicConfirm), apClosure);
+
+ ChipLogDetail(DeviceLayer, "CHIP BTP C1 %s", bluez_gatt_characteristic1_get_service(endpoint->mpC1));
+ ChipLogDetail(DeviceLayer, "CHIP BTP C2 %s", bluez_gatt_characteristic1_get_service(endpoint->mpC2));
+
+#if CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING
+ ChipLogDetail(DeviceLayer, "CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING is TRUE");
+ // Additional data characteristics
+ endpoint->mpC3 =
+ BluezCharacteristicCreate(endpoint->mpService, g_strdup("c3"), g_strdup(CHIP_PLAT_BLE_UUID_C3_STRING), endpoint->mpRoot);
+ bluez_gatt_characteristic1_set_flags(endpoint->mpC3, c3_flags);
+ g_signal_connect(endpoint->mpC3, "handle-read-value", G_CALLBACK(BluezCharacteristicReadValue), apClosure);
+ g_signal_connect(endpoint->mpC3, "handle-write-value", G_CALLBACK(BluezCharacteristicWriteValueError), NULL);
+ g_signal_connect(endpoint->mpC3, "handle-acquire-write", G_CALLBACK(BluezCharacteristicAcquireWriteError), NULL);
+ g_signal_connect(endpoint->mpC3, "handle-acquire-notify", G_CALLBACK(BluezCharacteristicAcquireNotify), apClosure);
+ g_signal_connect(endpoint->mpC3, "handle-start-notify", G_CALLBACK(BluezCharacteristicStartNotify), apClosure);
+ g_signal_connect(endpoint->mpC3, "handle-stop-notify", G_CALLBACK(BluezCharacteristicStopNotify), apClosure);
+ g_signal_connect(endpoint->mpC3, "handle-confirm", G_CALLBACK(BluezCharacteristicConfirm), apClosure);
+ // update the characteristic value
+ UpdateAdditionalDataCharacteristic(endpoint->mpC3);
+ ChipLogDetail(DeviceLayer, "CHIP BTP C3 %s", bluez_gatt_characteristic1_get_service(endpoint->mpC3));
+#else
+ ChipLogDetail(DeviceLayer, "CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING is FALSE");
+ (void) c3_flags;
+#endif
+
+exit:
+ return;
+}
+
+static void BluezOnBusAcquired(GDBusConnection * aConn, const gchar * aName, gpointer apClosure)
+{
+ BluezEndpoint * endpoint = static_cast<BluezEndpoint *>(apClosure);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ ChipLogDetail(DeviceLayer, "TRACE: Bus acquired for name %s", aName);
+
+ if (!endpoint->mIsCentral)
+ {
+ endpoint->mpRootPath = g_strdup_printf("/chipoble/%04x", getpid() & 0xffff);
+ endpoint->mpRoot = g_dbus_object_manager_server_new(endpoint->mpRootPath);
+ g_dbus_object_manager_server_set_connection(endpoint->mpRoot, aConn);
+
+ BluezPeripheralObjectsSetup(apClosure);
+ }
+
+exit:
+ return;
+}
+
+#if CHIP_BLUEZ_NAME_MONITOR
+static void BluezOnNameAcquired(GDBusConnection * aConn, const gchar * aName, gpointer apClosure)
+{
+ ChipLogDetail(DeviceLayer, "TRACE: Owning name: Acquired %s", aName);
+}
+
+static void BluezOnNameLost(GDBusConnection * aConn, const gchar * aName, gpointer apClosure)
+{
+ ChipLogDetail(DeviceLayer, "TRACE: Owning name: lost %s", aName);
+}
+#endif
+
+static int StartupEndpointBindings(BluezEndpoint * endpoint)
+{
+ GDBusObjectManager * manager;
+ GError * error = nullptr;
+ GDBusConnection * conn = nullptr;
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "endpoint is NULL in %s", __func__));
+
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
+ VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "FAIL: get bus sync in %s, error: %s", __func__, error->message));
+
+ if (endpoint->mpAdapterName != nullptr)
+ endpoint->mpOwningName = g_strdup_printf("%s", endpoint->mpAdapterName);
+ else
+ endpoint->mpOwningName = g_strdup_printf("C-%04x", getpid() & 0xffff);
+
+ BluezOnBusAcquired(conn, endpoint->mpOwningName, endpoint);
+
+ manager = g_dbus_object_manager_client_new_sync(
+ conn, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, BLUEZ_INTERFACE, "/", bluez_object_manager_client_get_proxy_type,
+ nullptr /* unused user data in the Proxy Type Func */, nullptr /*destroy notify */, nullptr /* cancellable */, &error);
+
+ VerifyOrExit(manager != nullptr, ChipLogError(DeviceLayer, "FAIL: Error getting object manager client: %s", error->message));
+
+ endpoint->mpObjMgr = manager;
+
+ bluezObjectsSetup(endpoint);
+
+ g_signal_connect(manager, "object-added", G_CALLBACK(BluezSignalOnObjectAdded), endpoint);
+ g_signal_connect(manager, "object-removed", G_CALLBACK(BluezSignalOnObjectRemoved), endpoint);
+ g_signal_connect(manager, "interface-proxy-properties-changed", G_CALLBACK(BluezSignalInterfacePropertiesChanged), endpoint);
+
+ if (!MainLoop::Instance().SetCleanupFunction(BluezObjectsCleanup, endpoint))
+ {
+ ChipLogError(DeviceLayer, "Failed to schedule cleanup function");
+ }
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+
+ return 0;
+}
+
+static gboolean BluezC2Indicate(ConnectionDataBundle * closure)
+{
+ BluezConnection * conn = nullptr;
+ GError * error = nullptr;
+ GIOStatus status;
+ const char * buf;
+ size_t len, written;
+
+ VerifyOrExit(closure != nullptr, ChipLogError(DeviceLayer, "ConnectionDataBundle is NULL in %s", __func__));
+
+ conn = closure->mpConn;
+ VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "BluezConnection is NULL in %s", __func__));
+ VerifyOrExit(conn->mpC2 != nullptr, ChipLogError(DeviceLayer, "FAIL: C2 Indicate: %s", "NULL C2"));
+
+ if (bluez_gatt_characteristic1_get_notify_acquired(conn->mpC2) == TRUE)
+ {
+ buf = (char *) g_variant_get_fixed_array(closure->mpVal, &len, sizeof(uint8_t));
+ VerifyOrExit(len <= static_cast<size_t>(std::numeric_limits<gssize>::max()),
+ ChipLogError(DeviceLayer, "FAIL: buffer too large in %s", __func__));
+ status = g_io_channel_write_chars(conn->mC2Channel.mpChannel, buf, static_cast<gssize>(len), &written, &error);
+ g_variant_unref(closure->mpVal);
+ closure->mpVal = nullptr;
+
+ VerifyOrExit(status == G_IO_STATUS_NORMAL, ChipLogError(DeviceLayer, "FAIL: C2 Indicate: %s", error->message));
+ }
+ else
+ {
+ bluez_gatt_characteristic1_set_value(conn->mpC2, closure->mpVal);
+ closure->mpVal = nullptr;
+ }
+
+exit:
+ if (closure != nullptr)
+ {
+ if (closure->mpVal)
+ {
+ g_variant_unref(closure->mpVal);
+ }
+ g_free(closure);
+ }
+
+ if (error != nullptr)
+ g_error_free(error);
+ return G_SOURCE_REMOVE;
+}
+
+static ConnectionDataBundle * MakeConnectionDataBundle(BLE_CONNECTION_OBJECT apConn, const chip::System::PacketBufferHandle & apBuf)
+{
+ ConnectionDataBundle * bundle = g_new(ConnectionDataBundle, 1);
+ bundle->mpConn = static_cast<BluezConnection *>(apConn);
+ bundle->mpVal =
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, apBuf->Start(), apBuf->DataLength() * sizeof(uint8_t), sizeof(uint8_t));
+ return bundle;
+}
+
+bool SendBluezIndication(BLE_CONNECTION_OBJECT apConn, chip::System::PacketBufferHandle apBuf)
+{
+ bool success = false;
+
+ VerifyOrExit(!apBuf.IsNull(), ChipLogError(DeviceLayer, "apBuf is NULL in %s", __func__));
+
+ success = MainLoop::Instance().Schedule(BluezC2Indicate, MakeConnectionDataBundle(apConn, apBuf));
+
+exit:
+ return success;
+}
+
+static gboolean BluezDisconnect(void * apClosure)
+{
+ BluezConnection * conn = static_cast<BluezConnection *>(apClosure);
+ GError * error = nullptr;
+ gboolean success;
+
+ VerifyOrExit(conn != nullptr, ChipLogError(DeviceLayer, "conn is NULL in %s", __func__));
+ VerifyOrExit(conn->mpDevice != nullptr, ChipLogError(DeviceLayer, "FAIL: Disconnect: %s", "NULL Device"));
+
+ ChipLogDetail(DeviceLayer, "%s peer=%s", __func__, bluez_device1_get_address(conn->mpDevice));
+
+ success = bluez_device1_call_disconnect_sync(conn->mpDevice, nullptr, &error);
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: Disconnect: %s", error->message));
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+ return G_SOURCE_REMOVE;
+}
+
+static int CloseBleconnectionCB(void * apAppState)
+{
+ BluezDisconnect(apAppState);
+ return G_SOURCE_REMOVE;
+}
+
+bool CloseBluezConnection(BLE_CONNECTION_OBJECT apConn)
+{
+ return MainLoop::Instance().RunOnBluezThread(CloseBleconnectionCB, apConn);
+}
+
+CHIP_ERROR StartBluezAdv(BluezEndpoint * apEndpoint)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ if (!MainLoop::Instance().Schedule(BluezAdvStart, apEndpoint))
+ {
+ err = CHIP_ERROR_INCORRECT_STATE;
+ ChipLogError(Ble, "Failed to schedule BluezAdvStart() on CHIPoBluez thread");
+ }
+ return err;
+}
+
+CHIP_ERROR StopBluezAdv(BluezEndpoint * apEndpoint)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ if (!MainLoop::Instance().Schedule(BluezAdvStop, apEndpoint))
+ {
+ err = CHIP_ERROR_INCORRECT_STATE;
+ ChipLogError(Ble, "Failed to schedule BluezAdvStop() on CHIPoBluez thread");
+ }
+ return err;
+}
+
+CHIP_ERROR BluezAdvertisementSetup(BluezEndpoint * apEndpoint)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ if (!MainLoop::Instance().Schedule(BluezAdvSetup, apEndpoint))
+ {
+ err = CHIP_ERROR_INCORRECT_STATE;
+ ChipLogError(Ble, "Failed to schedule BluezAdvertisementSetup() on CHIPoBluez thread");
+ }
+ return err;
+}
+
+CHIP_ERROR BluezGattsAppRegister(BluezEndpoint * apEndpoint)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ if (!MainLoop::Instance().Schedule(BluezPeripheralRegisterApp, apEndpoint))
+ {
+ err = CHIP_ERROR_INCORRECT_STATE;
+ ChipLogError(Ble, "Failed to schedule BluezPeripheralRegisterApp() on CHIPoBluez thread");
+ }
+ return err;
+}
+
+CHIP_ERROR ConfigureBluezAdv(BLEAdvConfig & aBleAdvConfig, BluezEndpoint * apEndpoint)
+{
+ const char * msg = nullptr;
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ VerifyOrExit(aBleAdvConfig.mpBleName != nullptr, msg = "FAIL: BLE name is NULL");
+ VerifyOrExit(aBleAdvConfig.mpAdvertisingUUID != nullptr, msg = "FAIL: BLE mpAdvertisingUUID is NULL in %s");
+
+ apEndpoint->mpAdapterName = g_strdup(aBleAdvConfig.mpBleName);
+ apEndpoint->mpAdvertisingUUID = g_strdup(aBleAdvConfig.mpAdvertisingUUID);
+ apEndpoint->mAdapterId = aBleAdvConfig.mAdapterId;
+ apEndpoint->mType = aBleAdvConfig.mType;
+ apEndpoint->mDuration = aBleAdvConfig.mDuration;
+ apEndpoint->mDuration = aBleAdvConfig.mDuration;
+
+ err = ConfigurationMgr().GetBLEDeviceIdentificationInfo(apEndpoint->mDeviceIdInfo);
+ SuccessOrExit(err);
+
+exit:
+ if (nullptr != msg)
+ {
+ ChipLogDetail(DeviceLayer, "%s in %s", msg, __func__);
+ err = CHIP_ERROR_INCORRECT_STATE;
+ }
+ return err;
+}
+
+CHIP_ERROR InitBluezBleLayer(bool aIsCentral, char * apBleAddr, BLEAdvConfig & aBleAdvConfig, BluezEndpoint *& apEndpoint)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ bool retval = false;
+ BluezEndpoint * endpoint = nullptr;
+
+ // initialize server endpoint
+ endpoint = g_new0(BluezEndpoint, 1);
+ VerifyOrExit(endpoint != nullptr, ChipLogError(DeviceLayer, "FAIL: memory allocation in %s", __func__));
+
+ if (apBleAddr != nullptr)
+ endpoint->mpAdapterAddr = g_strdup(apBleAddr);
+ else
+ endpoint->mpAdapterAddr = nullptr;
+
+ endpoint->mpConnMap = g_hash_table_new(g_str_hash, g_str_equal);
+ endpoint->mIsCentral = aIsCentral;
+
+ if (!aIsCentral)
+ {
+ err = ConfigureBluezAdv(aBleAdvConfig, endpoint);
+ SuccessOrExit(err);
+ }
+ else
+ {
+ endpoint->mAdapterId = aBleAdvConfig.mAdapterId;
+ endpoint->mpConnectCancellable = g_cancellable_new();
+ }
+
+ err = MainLoop::Instance().EnsureStarted();
+ VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to start BLE main loop"));
+
+ if (!MainLoop::Instance().ScheduleAndWait(StartupEndpointBindings, endpoint))
+ {
+ ChipLogError(DeviceLayer, "Failed to schedule endpoint initialization");
+ ExitNow();
+ }
+
+ retval = TRUE;
+
+exit:
+ if (retval)
+ {
+ apEndpoint = endpoint;
+ ChipLogDetail(DeviceLayer, "PlatformBlueZInit init success");
+ }
+ else
+ {
+ EndpointCleanup(endpoint);
+ }
+
+ return err;
+}
+
+// BluezSendWriteRequest callbacks
+
+static void SendWriteRequestDone(GObject * aObject, GAsyncResult * aResult, gpointer apConnection)
+{
+ BluezGattCharacteristic1 * c1 = BLUEZ_GATT_CHARACTERISTIC1(aObject);
+ GError * error = nullptr;
+ gboolean success = bluez_gatt_characteristic1_call_write_value_finish(c1, aResult, &error);
+
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: BluezSendWriteRequest : %s", error->message));
+ BLEManagerImpl::HandleWriteComplete(static_cast<BLE_CONNECTION_OBJECT>(apConnection));
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static gboolean SendWriteRequestImpl(void * apConnectionData)
+{
+ ConnectionDataBundle * data = static_cast<ConnectionDataBundle *>(apConnectionData);
+ GVariant * options = nullptr;
+ GVariantBuilder optionsBuilder;
+
+ VerifyOrExit(data != nullptr, ChipLogError(DeviceLayer, "ConnectionDataBundle is NULL in %s", __func__));
+ VerifyOrExit(data->mpConn != nullptr, ChipLogError(DeviceLayer, "BluezConnection is NULL in %s", __func__));
+ VerifyOrExit(data->mpConn->mpC1 != nullptr, ChipLogError(DeviceLayer, "C1 is NULL in %s", __func__));
+
+ g_variant_builder_init(&optionsBuilder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add(&optionsBuilder, "{sv}", "type", g_variant_new_string("request"));
+ options = g_variant_builder_end(&optionsBuilder);
+
+ bluez_gatt_characteristic1_call_write_value(data->mpConn->mpC1, data->mpVal, options, nullptr, SendWriteRequestDone,
+ data->mpConn);
+
+exit:
+ g_free(data);
+ return G_SOURCE_REMOVE;
+}
+
+bool BluezSendWriteRequest(BLE_CONNECTION_OBJECT apConn, chip::System::PacketBufferHandle apBuf)
+{
+ bool success = false;
+
+ VerifyOrExit(!apBuf.IsNull(), ChipLogError(DeviceLayer, "apBuf is NULL in %s", __func__));
+
+ success = MainLoop::Instance().RunOnBluezThread(SendWriteRequestImpl, MakeConnectionDataBundle(apConn, apBuf));
+
+exit:
+ return success;
+}
+
+// BluezSubscribeCharacteristic callbacks
+
+static void OnCharacteristicChanged(GDBusProxy * aInterface, GVariant * aChangedProperties, const gchar * const * aInvalidatedProps,
+ gpointer apConnection)
+{
+ BLE_CONNECTION_OBJECT connection = static_cast<BLE_CONNECTION_OBJECT>(apConnection);
+ GVariant * value = g_variant_lookup_value(aChangedProperties, "Value", G_VARIANT_TYPE_BYTESTRING);
+ VerifyOrReturn(value != nullptr);
+
+ size_t bufferLen;
+ auto buffer = g_variant_get_fixed_array(value, &bufferLen, sizeof(uint8_t));
+ VerifyOrReturn(value != nullptr, ChipLogError(DeviceLayer, "Characteristic value has unexpected type"));
+
+ BLEManagerImpl::HandleTXCharChanged(connection, static_cast<const uint8_t *>(buffer), bufferLen);
+}
+
+static void SubscribeCharacteristicDone(GObject * aObject, GAsyncResult * aResult, gpointer apConnection)
+{
+ BluezGattCharacteristic1 * c2 = BLUEZ_GATT_CHARACTERISTIC1(aObject);
+ GError * error = nullptr;
+ gboolean success = bluez_gatt_characteristic1_call_write_value_finish(c2, aResult, &error);
+
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: BluezSubscribeCharacteristic : %s", error->message));
+
+ // Get notifications on the TX characteristic change (e.g. indication is received)
+ g_signal_connect(c2, "g-properties-changed", G_CALLBACK(OnCharacteristicChanged), apConnection);
+ BLEManagerImpl::HandleSubscribeOpComplete(static_cast<BLE_CONNECTION_OBJECT>(apConnection), true);
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static gboolean SubscribeCharacteristicImpl(BluezConnection * connection)
+{
+ VerifyOrExit(connection != nullptr, ChipLogError(DeviceLayer, "BluezConnection is NULL in %s", __func__));
+ VerifyOrExit(connection->mpC2 != nullptr, ChipLogError(DeviceLayer, "C2 is NULL in %s", __func__));
+
+ bluez_gatt_characteristic1_call_start_notify(connection->mpC2, nullptr, SubscribeCharacteristicDone, connection);
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+bool BluezSubscribeCharacteristic(BLE_CONNECTION_OBJECT apConn)
+{
+ return MainLoop::Instance().Schedule(SubscribeCharacteristicImpl, static_cast<BluezConnection *>(apConn));
+}
+
+// BluezUnsubscribeCharacteristic callbacks
+
+static void UnsubscribeCharacteristicDone(GObject * aObject, GAsyncResult * aResult, gpointer apConnection)
+{
+ BluezGattCharacteristic1 * c2 = BLUEZ_GATT_CHARACTERISTIC1(aObject);
+ GError * error = nullptr;
+ gboolean success = bluez_gatt_characteristic1_call_write_value_finish(c2, aResult, &error);
+
+ VerifyOrExit(success == TRUE, ChipLogError(DeviceLayer, "FAIL: BluezUnsubscribeCharacteristic : %s", error->message));
+
+ // Stop listening to the TX characteristic changes
+ g_signal_handlers_disconnect_by_data(c2, apConnection);
+ BLEManagerImpl::HandleSubscribeOpComplete(static_cast<BLE_CONNECTION_OBJECT>(apConnection), false);
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static gboolean UnsubscribeCharacteristicImpl(BluezConnection * connection)
+{
+ VerifyOrExit(connection != nullptr, ChipLogError(DeviceLayer, "BluezConnection is NULL in %s", __func__));
+ VerifyOrExit(connection->mpC2 != nullptr, ChipLogError(DeviceLayer, "C2 is NULL in %s", __func__));
+
+ bluez_gatt_characteristic1_call_stop_notify(connection->mpC2, nullptr, UnsubscribeCharacteristicDone, connection);
+
+exit:
+ return G_SOURCE_REMOVE;
+}
+
+bool BluezUnsubscribeCharacteristic(BLE_CONNECTION_OBJECT apConn)
+{
+ return MainLoop::Instance().Schedule(UnsubscribeCharacteristicImpl, static_cast<BluezConnection *>(apConn));
+}
+
+// ConnectDevice callbacks
+
+struct ConnectParams
+{
+ ConnectParams(BluezDevice1 * device, BluezEndpoint * endpoint) : mDevice(device), mEndpoint(endpoint) {}
+ BluezDevice1 * mDevice;
+ BluezEndpoint * mEndpoint;
+};
+
+static void ConnectDeviceDone(GObject * aObject, GAsyncResult * aResult, gpointer)
+{
+ BluezDevice1 * device = BLUEZ_DEVICE1(aObject);
+ GError * error = nullptr;
+ gboolean success = bluez_device1_call_connect_finish(device, aResult, &error);
+
+ if (!success)
+ {
+ ChipLogError(DeviceLayer, "FAIL: ConnectDevice : %s", error->message);
+ BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_INTERNAL);
+ ExitNow();
+ }
+
+ ChipLogDetail(DeviceLayer, "ConnectDevice complete");
+
+exit:
+ if (error != nullptr)
+ g_error_free(error);
+}
+
+static gboolean ConnectDeviceImpl(ConnectParams * apParams)
+{
+ BluezDevice1 * device = apParams->mDevice;
+ BluezEndpoint * endpoint = apParams->mEndpoint;
+
+ assert(device != nullptr);
+ assert(endpoint != nullptr);
+
+ g_cancellable_reset(endpoint->mpConnectCancellable);
+ bluez_device1_call_connect(device, endpoint->mpConnectCancellable, ConnectDeviceDone, nullptr);
+ g_object_unref(device);
+ chip::Platform::Delete(apParams);
+
+ return G_SOURCE_REMOVE;
+}
+
+CHIP_ERROR ConnectDevice(BluezDevice1 * apDevice, BluezEndpoint * apEndpoint)
+{
+ auto params = chip::Platform::New<ConnectParams>(apDevice, apEndpoint);
+ g_object_ref(apDevice);
+
+ if (!MainLoop::Instance().Schedule(ConnectDeviceImpl, params))
+ {
+ ChipLogError(Ble, "Failed to schedule ConnectDeviceImpl() on CHIPoBluez thread");
+ g_object_unref(apDevice);
+ chip::Platform::Delete(params);
+ return CHIP_ERROR_INCORRECT_STATE;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+void CancelConnect(BluezEndpoint * apEndpoint)
+{
+ assert(apEndpoint->mpConnectCancellable != nullptr);
+ g_cancellable_cancel(apEndpoint->mpConnectCancellable);
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2020 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (c) 2016-2019, 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
+ * Provides Bluez dbus implementation for BLE
+ */
+
+#pragma once
+
+#include "Types.h"
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+CHIP_ERROR InitBluezBleLayer(bool aIsCentral, char * apBleAddr, BLEAdvConfig & aBleAdvConfig, BluezEndpoint *& apEndpoint);
+bool BluezRunOnBluezThread(int (*aCallback)(void *), void * apClosure);
+bool SendBluezIndication(BLE_CONNECTION_OBJECT apConn, chip::System::PacketBufferHandle apBuf);
+bool CloseBluezConnection(BLE_CONNECTION_OBJECT apConn);
+CHIP_ERROR StartBluezAdv(BluezEndpoint * apEndpoint);
+CHIP_ERROR StopBluezAdv(BluezEndpoint * apEndpoint);
+CHIP_ERROR BluezGattsAppRegister(BluezEndpoint * apEndpoint);
+CHIP_ERROR BluezAdvertisementSetup(BluezEndpoint * apEndpoint);
+
+/// Write to the CHIP RX characteristic on the remote peripheral device
+bool BluezSendWriteRequest(BLE_CONNECTION_OBJECT apConn, chip::System::PacketBufferHandle apBuf);
+/// Subscribe to the CHIP TX characteristic on the remote peripheral device
+bool BluezSubscribeCharacteristic(BLE_CONNECTION_OBJECT apConn);
+/// Unsubscribe from the CHIP TX characteristic on the remote peripheral device
+bool BluezUnsubscribeCharacteristic(BLE_CONNECTION_OBJECT apConn);
+
+CHIP_ERROR ConnectDevice(BluezDevice1 * apDevice, BluezEndpoint * apEndpoint);
+void CancelConnect(BluezEndpoint * apEndpoint);
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MainLoop.h"
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include <errno.h>
+#include <pthread.h>
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+namespace {
+
+class Semaphore
+{
+public:
+ Semaphore()
+ {
+ if (sem_init(&mSemaphore, 0 /* shared */, 0 /*value*/) != 0)
+ {
+ ChipLogError(DeviceLayer, "Failed to initialize semaphore.");
+ }
+ }
+
+ ~Semaphore()
+ {
+ if (sem_destroy(&mSemaphore) != 0)
+ {
+ ChipLogError(DeviceLayer, "Failed to destroy semaphore.");
+ }
+ }
+
+ void Post() { sem_post(&mSemaphore); }
+
+ void Wait() { sem_wait(&mSemaphore); }
+
+private:
+ sem_t mSemaphore;
+};
+
+int PostSemaphore(Semaphore * semaphore)
+{
+ semaphore->Post();
+ return 0;
+}
+
+class CallbackIndirection
+{
+public:
+ CallbackIndirection(GSourceFunc f, void * a) : mCallback(f), mArgument(a) {}
+
+ void Wait() { mDoneSemaphore.Wait(); }
+
+ static int Callback(CallbackIndirection * self)
+ {
+ int result = self->mCallback(self->mArgument);
+ self->mDoneSemaphore.Post();
+ return result;
+ }
+
+private:
+ Semaphore mDoneSemaphore;
+ GSourceFunc mCallback;
+ void * mArgument;
+};
+
+} // namespace
+
+MainLoop & MainLoop::Instance()
+{
+ static MainLoop sMainLoop;
+ return sMainLoop;
+}
+
+void * MainLoop::Thread(void * self)
+{
+ MainLoop * loop = reinterpret_cast<MainLoop *>(self);
+
+ ChipLogDetail(DeviceLayer, "TRACE: Bluez mainloop starting %s", __func__);
+ g_main_loop_run(loop->mBluezMainLoop);
+ ChipLogDetail(DeviceLayer, "TRACE: Bluez mainloop stopping %s", __func__);
+
+ if (loop->mCleanup != nullptr)
+ {
+ ChipLogDetail(DeviceLayer, "TRACE: Executing cleanup %s", __func__);
+ loop->mCleanup(loop->mCleanupArgument);
+ }
+
+ return nullptr;
+}
+
+CHIP_ERROR MainLoop::EnsureStarted()
+{
+ if (mBluezMainLoop != nullptr)
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ mBluezMainLoop = g_main_loop_new(nullptr, TRUE);
+ if (mBluezMainLoop == nullptr)
+ {
+ ChipLogError(DeviceLayer, "FAIL: memory alloc in %s", __func__);
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ int pthreadErr = pthread_create(&mThread, nullptr, &MainLoop::Thread, reinterpret_cast<void *>(this));
+ int tmpErrno = errno;
+ if (pthreadErr != 0)
+ {
+ ChipLogError(DeviceLayer, "FAIL: pthread_create (%s) in %s", strerror(tmpErrno), __func__);
+ g_free(mBluezMainLoop);
+ mBluezMainLoop = nullptr;
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ Semaphore semaphore;
+
+ GMainContext * context = g_main_loop_get_context(mBluezMainLoop);
+ VerifyOrDie(context != nullptr);
+
+ g_main_context_invoke(context, GSourceFunc(PostSemaphore), &semaphore);
+
+ semaphore.Wait();
+
+ return CHIP_NO_ERROR;
+}
+
+bool MainLoop::RunOnBluezThread(GSourceFunc callback, void * argument)
+{
+ GMainContext * context = nullptr;
+ const char * msg = nullptr;
+
+ VerifyOrExit(mBluezMainLoop != nullptr, msg = "FAIL: NULL sBluezMainLoop");
+ VerifyOrExit(g_main_loop_is_running(mBluezMainLoop), msg = "FAIL: sBluezMainLoop not running");
+
+ context = g_main_loop_get_context(mBluezMainLoop);
+ VerifyOrExit(context != nullptr, msg = "FAIL: NULL main context");
+ g_main_context_invoke(context, callback, argument);
+
+exit:
+ if (msg != nullptr)
+ {
+ ChipLogDetail(DeviceLayer, "%s in %s", msg, __func__);
+ }
+
+ return msg == nullptr;
+}
+
+bool MainLoop::RunOnBluezThreadAndWait(GSourceFunc closure, void * argument)
+{
+ CallbackIndirection indirection(closure, argument);
+
+ if (!Schedule(&CallbackIndirection::Callback, &indirection))
+ {
+ return false;
+ }
+
+ indirection.Wait();
+
+ return true;
+}
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <core/CHIPError.h>
+#include <platform/CHIPDeviceLayer.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include <glib.h>
+#include <semaphore.h>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+/// The main loop provides a thread-based implementation that runs
+/// the GLib main loop.
+///
+/// The main loop is used execute callbacks (e.g. dbus notifications).
+class MainLoop
+{
+public:
+ /// Ensure that a thread with g_main_loop_run is executing.
+ CHIP_ERROR EnsureStarted();
+
+ /// Executes a callback on the underlying main loop.
+ ///
+ /// The main loop MUST have been started already.
+ bool RunOnBluezThread(GSourceFunc closure, void * arg);
+
+ /// Executes a callback on the underlying main loop and waits for
+ /// the method to complete.
+ ///
+ /// The main loop MUST have been started already.
+ bool RunOnBluezThreadAndWait(GSourceFunc closure, void * arg);
+
+ /// Convenience method to require less casts to void*
+ template <class T>
+ bool Schedule(int (*callback)(T *), T * value)
+ {
+ return RunOnBluezThread(G_SOURCE_FUNC(callback), value);
+ }
+
+ /// Convenience method to require less casts to void*
+ template <class T>
+ bool ScheduleAndWait(int (*callback)(T *), T * value)
+ {
+ return RunOnBluezThreadAndWait(G_SOURCE_FUNC(callback), value);
+ }
+
+ /// Schedules a method to be executed after the main loop has finished
+ ///
+ /// A single cleanup method can exist and the main loop has to be running
+ /// to set a cleanup method.
+ template <class T>
+ bool SetCleanupFunction(int (*callback)(T *), T * value)
+ {
+ if (mCleanup != nullptr)
+ {
+ return false;
+ }
+
+ if ((mBluezMainLoop == nullptr) || !g_main_loop_is_running(mBluezMainLoop))
+ {
+ return false;
+ }
+
+ mCleanup = G_SOURCE_FUNC(callback);
+ mCleanupArgument = static_cast<void *>(value);
+
+ return true;
+ }
+
+ static MainLoop & Instance();
+
+private:
+ MainLoop() {}
+
+ static void * Thread(void * self);
+
+ GMainLoop * mBluezMainLoop = nullptr;
+ pthread_t mThread = 0;
+
+ // allow a single cleanup method
+ GSourceFunc mCleanup = nullptr;
+ void * mCleanupArgument = nullptr;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+/*
+ *
+ * Copyright (c) 2021 Project CHIP Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (c) 2016-2019, 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 <platform/CHIPDeviceConfig.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
+
+#include <ble/CHIPBleServiceData.h>
+#include <platform/Linux/dbus/bluez/DbusBluez.h>
+
+#include <cstdint>
+#include <string>
+
+namespace chip {
+namespace DeviceLayer {
+namespace Internal {
+
+enum ChipAdvType
+{
+ BLUEZ_ADV_TYPE_CONNECTABLE = 0x01,
+ BLUEZ_ADV_TYPE_SCANNABLE = 0x02,
+ BLUEZ_ADV_TYPE_DIRECTED = 0x04,
+
+ BLUEZ_ADV_TYPE_UNDIRECTED_NONCONNECTABLE_NONSCANNABLE = 0,
+ BLUEZ_ADV_TYPE_UNDIRECTED_CONNECTABLE_NONSCANNABLE = BLUEZ_ADV_TYPE_CONNECTABLE,
+ BLUEZ_ADV_TYPE_UNDIRECTED_NONCONNECTABLE_SCANNABLE = BLUEZ_ADV_TYPE_SCANNABLE,
+ BLUEZ_ADV_TYPE_UNDIRECTED_CONNECTABLE_SCANNABLE = BLUEZ_ADV_TYPE_CONNECTABLE | BLUEZ_ADV_TYPE_SCANNABLE,
+
+ BLUEZ_ADV_TYPE_DIRECTED_NONCONNECTABLE_NONSCANNABLE = BLUEZ_ADV_TYPE_DIRECTED,
+ BLUEZ_ADV_TYPE_DIRECTED_CONNECTABLE_NONSCANNABLE = BLUEZ_ADV_TYPE_DIRECTED | BLUEZ_ADV_TYPE_CONNECTABLE,
+ BLUEZ_ADV_TYPE_DIRECTED_NONCONNECTABLE_SCANNABLE = BLUEZ_ADV_TYPE_DIRECTED | BLUEZ_ADV_TYPE_SCANNABLE,
+ BLUEZ_ADV_TYPE_DIRECTED_CONNECTABLE_SCANNABLE = BLUEZ_ADV_TYPE_DIRECTED | BLUEZ_ADV_TYPE_CONNECTABLE | BLUEZ_ADV_TYPE_SCANNABLE,
+};
+
+#define BLUEZ_ADDRESS_SIZE 6 ///< BLE address size (in bytes)
+#define BLUEZ_PATH "/org/bluez"
+#define BLUEZ_INTERFACE "org.bluez"
+#define ADAPTER_INTERFACE BLUEZ_INTERFACE ".Adapter1"
+#define PROFILE_INTERFACE BLUEZ_INTERFACE ".GattManager1"
+#define ADVERTISING_MANAGER_INTERFACE BLUEZ_INTERFACE ".LEAdvertisingManager1"
+#define SERVICE_INTERFACE BLUEZ_INTERFACE ".GattService1"
+#define CHARACTERISTIC_INTERFACE BLUEZ_INTERFACE ".GattCharacteristic1"
+#define ADVERTISING_INTERFACE BLUEZ_INTERFACE ".LEAdvertisement1"
+#define DEVICE_INTERFACE BLUEZ_INTERFACE ".Device1"
+
+#define CHIP_PLAT_BLE_UUID_C1_STRING "18ee2ef5-263d-4559-959f-4f9c429f9d11"
+#define CHIP_PLAT_BLE_UUID_C2_STRING "18ee2ef5-263d-4559-959f-4f9c429f9d12"
+#define CHIP_PLAT_BLE_UUID_C3_STRING "64630238-8772-45F2-B87D-748A83218F04"
+
+#define CHIP_BLE_BASE_SERVICE_UUID_STRING "-0000-1000-8000-00805f9b34fb"
+#define CHIP_BLE_SERVICE_PREFIX_LENGTH 8
+#define CHIP_BLE_BASE_SERVICE_PREFIX "0000"
+#define CHIP_BLE_UUID_SERVICE_SHORT_STRING "feaf"
+
+#define CHIP_BLE_UUID_SERVICE_STRING \
+ CHIP_BLE_BASE_SERVICE_PREFIX CHIP_BLE_UUID_SERVICE_SHORT_STRING CHIP_BLE_BASE_SERVICE_UUID_STRING
+
+#define BLUEZ_ADV_TYPE_FLAGS 0x01
+#define BLUEZ_ADV_TYPE_SERVICE_DATA 0x16
+
+#define BLUEZ_ADV_FLAGS_LE_LIMITED (1 << 0)
+#define BLUEZ_ADV_FLAGS_LE_DISCOVERABLE (1 << 1)
+#define BLUEZ_ADV_FLAGS_EDR_UNSUPPORTED (1 << 2)
+#define BLUEZ_ADV_FLAGS_LE_EDR_CONTROLLER (1 << 3)
+#define BLUEZ_ADV_FLAGS_LE_EDR_HOST (1 << 4)
+
+enum BluezAddressType
+{
+ BLUEZ_ADDRESS_TYPE_PUBLIC = 0, ///< Bluetooth public device address.
+ BLUEZ_ADDRESS_TYPE_RANDOM_STATIC = 1, ///< Bluetooth random static address.
+ BLUEZ_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE = 2, ///< Bluetooth random private resolvable address.
+ BLUEZ_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE = 3, ///< Bluetooth random private non-resolvable address.
+};
+
+struct BluezAddress
+{
+ BluezAddressType mType; ///< Bluetooth device address type.
+ uint8_t mAddress[BLUEZ_ADDRESS_SIZE]; ///< A 48-bit address of Bluetooth device in LSB format.
+};
+
+struct IOChannel
+{
+ GIOChannel * mpChannel;
+ guint mWatch;
+};
+
+struct BluezEndpoint
+{
+ char * mpOwningName; // Bus owning name
+
+ // Adapter properties
+ char * mpAdapterName;
+ char * mpAdapterAddr;
+
+ // Paths for objects published by this service
+ char * mpRootPath;
+ char * mpAdvPath;
+ char * mpServicePath;
+
+ // Objects (interfaces) subscibed to by this service
+ GDBusObjectManager * mpObjMgr = nullptr;
+ BluezAdapter1 * mpAdapter = nullptr;
+ BluezDevice1 * mpDevice = nullptr;
+
+ // Objects (interfaces) published by this service
+ GDBusObjectManagerServer * mpRoot;
+ BluezGattService1 * mpService;
+ BluezGattCharacteristic1 * mpC1;
+ BluezGattCharacteristic1 * mpC2;
+ // additional data characteristics
+ BluezGattCharacteristic1 * mpC3;
+
+ // map device path to the connection
+ GHashTable * mpConnMap;
+ uint32_t mAdapterId;
+ bool mIsCentral;
+ char * mpAdvertisingUUID;
+ chip::Ble::ChipBLEDeviceIdentificationInfo mDeviceIdInfo;
+ ChipAdvType mType; ///< Advertisement type.
+ uint16_t mDuration; ///< Advertisement interval (in ms).
+ bool mIsAdvertising;
+ char * mpPeerDevicePath;
+ GCancellable * mpConnectCancellable = nullptr;
+};
+
+struct BluezConnection
+{
+ char * mpPeerAddress;
+ BluezDevice1 * mpDevice;
+ BluezGattService1 * mpService;
+ BluezGattCharacteristic1 * mpC1;
+ BluezGattCharacteristic1 * mpC2;
+ // additional data characteristics
+ BluezGattCharacteristic1 * mpC3;
+
+ bool mIsNotify;
+ uint16_t mMtu;
+ struct IOChannel mC1Channel;
+ struct IOChannel mC2Channel;
+ BluezEndpoint * mpEndpoint;
+};
+
+struct ConnectionDataBundle
+{
+ BluezConnection * mpConn;
+ GVariant * mpVal;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
--- /dev/null
+# Copyright (c) 2020 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build_overrides/chip.gni")
+
+import("${chip_root}/build/chip/linux/gdbus_library.gni")
+
+gdbus_library("bluez") {
+ sources = [ "DbusBluez.xml" ]
+
+ c_namespace = "Bluez"
+ interface_prefix = "org.bluez"
+ c_generate_object_manager = true
+ dbus_out_dir = "platform/Linux/dbus/bluez"
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+# Copyright (c) 2020 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is constructed using the below method.
+# 1. running bluetoothd
+# 2. running two Bluetooth adapter with LE capability and create ble connection with Gatt servie and Gatt Char.
+# For the purposes of XML generation we use btvirt emulator with the LE only capability.
+# Assume the LE chip is on hci 0
+# we get the bluez XML via:
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez -x
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez/hci0 -x
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez/hci0/dev_00_AA_01_01_00_24 -x
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez/hci0/dev_00_AA_01_01_00_24/service0006 -x
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez/hci0/dev_00_AA_01_01_00_24/service0006/char0007 -x
+# sudo gdbus introspect -s -d org.bluez -r -o /org/bluez/hci0/dev_00_AA_01_01_00_24/service0006/char0007/desc0009 -x
+# sudo gdbus introspect -s -d org.bluez -r -o / -x
+-->
+
+<node>
+ <interface name="org.bluez.Adapter1">
+ <method name="StartDiscovery" />
+ <method name="SetDiscoveryFilter">
+ <arg name="properties" type="a{sv}" direction="in" />
+ </method>
+ <method name="StopDiscovery" />
+ <method name="RemoveDevice">
+ <arg name="device" type="o" direction="in" />
+ </method>
+ <method name="GetDiscoveryFilters">
+ <arg name="filters" type="as" direction="out" />
+ </method>
+ <method name="ConnectDevice">
+ <arg name="properties" type="a{sv}" direction="in" />
+ </method>
+
+ <property name="Address" type="s" access="read" />
+ <property name="AddressType" type="s" access="read" />
+ <property name="Name" type="s" access="read" />
+ <property name="Alias" type="s" access="readwrite" />
+ <property name="Class" type="u" access="read" />
+ <property name="Powered" type="b" access="readwrite" />
+ <property name="Discoverable" type="b" access="readwrite" />
+ <property name="DiscoverableTimeout" type="u" access="readwrite" />
+ <property name="Pairable" type="b" access="readwrite" />
+ <property name="PairableTimeout" type="u" access="readwrite" />
+ <property name="Discovering" type="b" access="read" />
+ <property name="UUIDs" type="as" access="read" />
+ <property name="Modalias" type="s" access="read" />
+ <property name="Roles" type="as" access="read" />
+ </interface>
+
+ <interface name="org.bluez.Device1">
+ <method name="Disconnect" />
+ <method name="Connect" />
+ <method name="ConnectProfile">
+ <arg name="UUID" type="s" direction="in" />
+ </method>
+ <method name="DisconnectProfile">
+ <arg name="UUID" type="s" direction="in" />
+ </method>
+ <method name="Pair" />
+ <method name="CancelPairing" />
+ <property name="Address" type="s" access="read" />
+ <property name="AddressType" type="s" access="read" />
+ <property name="Name" type="s" access="read" />
+ <property name="Alias" type="s" access="readwrite" />
+ <property name="Class" type="u" access="read" />
+ <property name="Appearance" type="q" access="read" />
+ <property name="Icon" type="s" access="read" />
+ <property name="Paired" type="b" access="read" />
+ <property name="Trusted" type="b" access="readwrite" />
+ <property name="Blocked" type="b" access="readwrite" />
+ <property name="LegacyPairing" type="b" access="read" />
+ <property name="RSSI" type="n" access="read" />
+ <property name="Connected" type="b" access="read" />
+ <property name="UUIDs" type="as" access="read" />
+ <property name="Modalias" type="s" access="read" />
+ <property name="Adapter" type="o" access="read" />
+ <property name="ManufacturerData" type="a{qv}" access="read" />
+ <property name="ServiceData" type="a{sv}" access="read" />
+ <property name="TxPower" type="n" access="read" />
+ <property name="ServicesResolved" type="b" access="read" />
+ <property name="AdvertisingFlags" type="ay" access="read" />
+ <property name="AdvertisingData" type="a{yv}" access="read" />
+ <property name="WakeAllowed" type="b" access="readwrite" />
+ </interface>
+
+ <interface name="org.bluez.GattManager1">
+ <method name="RegisterApplication">
+ <arg name="application" type="o" direction="in" />
+ <arg name="options" type="a{sv}" direction="in" />
+ </method>
+ <method name="UnregisterApplication">
+ <arg name="application" type="o" direction="in" />
+ </method>
+ </interface>
+
+ <interface name="org.bluez.GattService1">
+ <property name="UUID" type="s" access="read" />
+ <property name="Device" type="o" access="read" />
+ <property name="Primary" type="b" access="read" />
+ <!-- <property name="Includes" type="ao" access="read" /> -->
+ </interface>
+
+ <interface name="org.bluez.GattCharacteristic1">
+ <method name="ReadValue">
+ <arg name="options" type="a{sv}" direction="in"/>
+ <arg name="value" type="ay" direction="out">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+ <method name="WriteValue">
+ <arg name="value" type="ay" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <arg name="options" type="a{sv}" direction="in"/>
+ </method>
+ <method name="AcquireWrite">
+ <arg name="options" type="a{sv}" direction="in" />
+ <arg name="fd" type="h" direction="out" />
+ <arg name="mtu" type="q" direction="out" />
+ </method>
+ <method name="AcquireNotify">
+ <arg name="options" type="a{sv}" direction="in" />
+ <arg name="fd" type="h" direction="out" />
+ <arg name="mtu" type="q" direction="out" />
+ </method>
+ <method name="StartNotify" />
+ <method name="StopNotify" />
+ <method name="Confirm" />
+ <property name="UUID" type="s" access="read" />
+ <property name="Service" type="o" access="read" />
+ <property name="Value" type="ay" access="read">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </property>
+ <property name="Notifying" type="b" access="read" />
+ <property name="Flags" type="as" access="read" />
+ <property name="WriteAcquired" type="b" access="read" />
+ <property name="NotifyAcquired" type="b" access="read" />
+ </interface>
+
+ <interface name="org.bluez.GattDescriptor1">
+ <method name="ReadValue">
+ <arg name="options" type="a{sv}" direction="in"/>
+ <arg name="value" type="ay" direction="out">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+ <method name="WriteValue">
+ <arg name="value" type="ay" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ <arg name="options" type="a{sv}" direction="in"/>
+ </method>
+ <property name="UUID" type="s" access="read" />
+ <property name="Characteristic" type="o" access="read" />
+ <property name="Value" type="ay" access="read">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </property>
+ </interface>
+
+ <interface name="org.bluez.LEAdvertisement1">
+ <method name="Release"/>
+ <property name="Type" type="s" access="read"/>
+ <property name="ServiceUUIDs" type="as" access="read">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </property>
+ <property name="ManufacturerData" type="a{qv}" access="read"/>
+ <property name="SolicitUUIDs" type="as" access="read"/>
+ <property name="ServiceData" type="a{sv}" access="read"/>
+ <property name="Data" type="a{yay}" access="read"/>
+ <property name="Discoverable" type="b" access="read"/>
+ <property name="DiscoverableTimeout" type="q" access="read"/>
+ <property name="Includes" type="as" access="read"/>
+ <property name="LocalName" type="s" access="read"/>
+ <property name="Appearance" type="q" access="read"/>
+ <property name="Duration" type="q" access="read"/>
+ <property name="Timeout" type="q" access="read"/>
+ <!-- <property name="SecondaryChannel" type="s" access="read"/> -->
+ </interface>
+
+ <interface name="org.bluez.LEAdvertisingManager1">
+ <method name="RegisterAdvertisement">
+ <arg name="advertisement" type="o" direction="in" />
+ <arg name="options" type="a{sv}" direction="in" />
+ </method>
+ <method name="UnregisterAdvertisement">
+ <arg name="service" type="o" direction="in" />
+ </method>
+ <property name="ActiveInstances" type="y" access="read" />
+ <property name="SupportedInstances" type="y" access="read" />
+ <property name="SupportedIncludes" type="as" access="read" />
+ <property name="SupportedSecondaryChannels" type="as" access="read" />
+ </interface>
+
+ <interface name="org.freedesktop.DBus.Properties">
+ <method name="Get">
+ <arg name="interface" type="s" direction="in" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="value" type="v" direction="out" />
+ </method>
+ <method name="Set">
+ <arg name="interface" type="s" direction="in" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="value" type="v" direction="in" />
+ </method>
+ <method name="GetAll">
+ <arg name="interface" type="s" direction="in" />
+ <arg name="properties" type="a{sv}" direction="out" />
+ </method>
+ <signal name="PropertiesChanged">
+ <arg name="interface" type="s" />
+ <arg name="changed_properties" type="a{sv}" />
+ <arg name="invalidated_properties" type="as" />
+ </signal>
+ </interface>
+
+ <interface name="org.bluez.ProfileManager1">
+ <method name="RegisterProfile">
+ <arg name="profile" type="o" direction="in" />
+ <arg name="UUID" type="s" direction="in" />
+ <arg name="options" type="a{sv}" direction="in" />
+ </method>
+ <method name="UnregisterProfile">
+ <arg name="profile" type="o" direction="in" />
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.DBus.ObjectManager">
+ <method name="GetManagedObjects">
+ <arg name="objects" type="a{oa{sa{sv}}}" direction="out" />
+ </method>
+ <signal name="InterfacesAdded">
+ <arg name="object" type="o" />
+ <arg name="interfaces" type="a{sa{sv}}" />
+ </signal>
+ <signal name="InterfacesRemoved">
+ <arg name="object" type="o" />
+ <arg name="interfaces" type="as" />
+ </signal>
+ </interface>
+</node>
--- /dev/null
+# Copyright (c) 2020 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build_overrides/chip.gni")
+
+import("${chip_root}/build/chip/linux/gdbus_library.gni")
+
+gdbus_library("wpa") {
+ sources = [
+ "DBusWpa.xml",
+ "DBusWpaInterface.xml",
+ "DBusWpaNetwork.xml",
+ ]
+
+ c_namespace = "Wpa"
+ c_generate_object_manager = false
+ dbus_out_dir = "platform/Linux/dbus/wpa"
+}
--- /dev/null
+<?xml version="1.0" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://raw.githubusercontent.com/freedesktop/dbus/master/doc/introspect.dtd">
+<node>
+ <interface name="fi.w1.wpa_supplicant1">
+ <method name="CreateInterface">
+ <arg name="args" type="a{sv}" direction="in" />
+ <arg name="path" type="o" direction="out" />
+ </method>
+ <method name="RemoveInterface">
+ <arg name="path" type="o" direction="in" />
+ </method>
+ <method name="GetInterface">
+ <arg name="ifname" type="s" direction="in" />
+ <arg name="path" type="o" direction="out" />
+ </method>
+ <method name="ExpectDisconnect" />
+ <signal name="InterfaceAdded">
+ <arg name="path" type="o" />
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <signal name="InterfaceRemoved">
+ <arg name="path" type="o" />
+ </signal>
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <property name="DebugLevel" type="s" access="readwrite" />
+ <property name="DebugTimestamp" type="b" access="readwrite" />
+ <property name="DebugShowKeys" type="b" access="readwrite" />
+ <property name="Interfaces" type="ao" access="read" />
+ <property name="EapMethods" type="as" access="read" />
+ <property name="Capabilities" type="as" access="read" />
+ <property name="WFDIEs" type="ay" access="readwrite" />
+ </interface>
+ <node name="Interfaces" />
+</node>
--- /dev/null
+<?xml version="1.0" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://raw.githubusercontent.com/freedesktop/dbus/master/doc/introspect.dtd">
+<node>
+ <interface name="fi.w1.wpa_supplicant1.Interface">
+ <method name="Scan">
+ <arg name="args" type="a{sv}" direction="in" />
+ </method>
+ <method name="SignalPoll">
+ <arg name="args" type="a{sv}" direction="out" />
+ </method>
+ <method name="Disconnect" />
+ <method name="AddNetwork">
+ <arg name="args" type="a{sv}" direction="in" />
+ <arg name="path" type="o" direction="out" />
+ </method>
+ <method name="Reassociate" />
+ <method name="Reattach" />
+ <method name="Reconnect" />
+ <method name="RemoveNetwork">
+ <arg name="path" type="o" direction="in" />
+ </method>
+ <method name="RemoveAllNetworks" />
+ <method name="SelectNetwork">
+ <arg name="path" type="o" direction="in" />
+ </method>
+ <method name="NetworkReply">
+ <arg name="path" type="o" direction="in" />
+ <arg name="field" type="s" direction="in" />
+ <arg name="value" type="s" direction="in" />
+ </method>
+ <method name="AddBlob">
+ <arg name="name" type="s" direction="in" />
+ <arg name="data" type="ay" direction="in" />
+ </method>
+ <method name="GetBlob">
+ <arg name="name" type="s" direction="in" />
+ <arg name="data" type="ay" direction="out" />
+ </method>
+ <method name="RemoveBlob">
+ <arg name="name" type="s" direction="in" />
+ </method>
+ <method name="SetPKCS11EngineAndModulePath">
+ <arg name="pkcs11_engine_path" type="s" direction="in" />
+ <arg name="pkcs11_module_path" type="s" direction="in" />
+ </method>
+ <method name="FlushBSS">
+ <arg name="age" type="u" direction="in" />
+ </method>
+ <method name="SubscribeProbeReq" />
+ <method name="UnsubscribeProbeReq" />
+ <method name="EAPLogoff" />
+ <method name="EAPLogon" />
+ <method name="AutoScan">
+ <arg name="arg" type="s" direction="in" />
+ </method>
+ <method name="TDLSDiscover">
+ <arg name="peer_address" type="s" direction="in" />
+ </method>
+ <method name="TDLSSetup">
+ <arg name="peer_address" type="s" direction="in" />
+ </method>
+ <method name="TDLSStatus">
+ <arg name="peer_address" type="s" direction="in" />
+ <arg name="status" type="s" direction="out" />
+ </method>
+ <method name="TDLSTeardown">
+ <arg name="peer_address" type="s" direction="in" />
+ </method>
+ <method name="TDLSChannelSwitch">
+ <arg name="args" type="a{sv}" direction="in" />
+ </method>
+ <method name="TDLSCancelChannelSwitch">
+ <arg name="peer_address" type="s" direction="in" />
+ </method>
+ <method name="VendorElemAdd">
+ <arg name="frame_id" type="i" direction="in" />
+ <arg name="ielems" type="ay" direction="in" />
+ </method>
+ <method name="VendorElemGet">
+ <arg name="frame_id" type="i" direction="in" />
+ <arg name="ielems" type="ay" direction="out" />
+ </method>
+ <method name="VendorElemRem">
+ <arg name="frame_id" type="i" direction="in" />
+ <arg name="ielems" type="ay" direction="in" />
+ </method>
+ <method name="SaveConfig" />
+ <method name="AbortScan" />
+ <signal name="ScanDone">
+ <arg name="success" type="b" />
+ </signal>
+ <signal name="BSSAdded">
+ <arg name="path" type="o" />
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <signal name="BSSRemoved">
+ <arg name="path" type="o" />
+ </signal>
+ <signal name="BlobAdded">
+ <arg name="name" type="s" />
+ </signal>
+ <signal name="BlobRemoved">
+ <arg name="name" type="s" />
+ </signal>
+ <signal name="NetworkAdded">
+ <arg name="path" type="o" />
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <signal name="NetworkRemoved">
+ <arg name="path" type="o" />
+ </signal>
+ <signal name="NetworkSelected">
+ <arg name="path" type="o" />
+ </signal>
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <signal name="ProbeRequest">
+ <arg name="args" type="a{sv}" />
+ </signal>
+ <signal name="Certification">
+ <arg name="certification" type="a{sv}" />
+ </signal>
+ <signal name="EAP">
+ <arg name="status" type="s" />
+ <arg name="parameter" type="s" />
+ </signal>
+ <signal name="StaAuthorized">
+ <arg name="name" type="s" />
+ </signal>
+ <signal name="StaDeauthorized">
+ <arg name="name" type="s" />
+ </signal>
+ <signal name="StationAdded">
+ <arg name="path" type="o" />
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <signal name="StationRemoved">
+ <arg name="path" type="o" />
+ </signal>
+ <signal name="NetworkRequest">
+ <arg name="path" type="o" />
+ <arg name="field" type="s" />
+ <arg name="text" type="s" />
+ </signal>
+ <property name="Capabilities" type="a{sv}" access="read" />
+ <property name="State" type="s" access="read" />
+ <property name="Scanning" type="b" access="read" />
+ <property name="ApScan" type="u" access="readwrite" />
+ <property name="BSSExpireAge" type="u" access="readwrite" />
+ <property name="BSSExpireCount" type="u" access="readwrite" />
+ <property name="Country" type="s" access="readwrite" />
+ <property name="Ifname" type="s" access="read" />
+ <property name="Driver" type="s" access="read" />
+ <property name="BridgeIfname" type="s" access="read" />
+ <property name="ConfigFile" type="s" access="read" />
+ <property name="CurrentBSS" type="o" access="read" />
+ <property name="CurrentNetwork" type="o" access="read" />
+ <property name="CurrentAuthMode" type="s" access="read" />
+ <property name="Blobs" type="a{say}" access="read" />
+ <property name="BSSs" type="ao" access="read" />
+ <property name="Networks" type="ao" access="read" />
+ <property name="FastReauth" type="b" access="readwrite" />
+ <property name="ScanInterval" type="i" access="readwrite" />
+ <property name="PKCS11EnginePath" type="s" access="read" />
+ <property name="PKCS11ModulePath" type="s" access="read" />
+ <property name="DisconnectReason" type="i" access="read" />
+ <property name="AuthStatusCode" type="i" access="read" />
+ <property name="AssocStatusCode" type="i" access="read" />
+ <property name="RoamTime" type="u" access="read" />
+ <property name="RoamComplete" type="b" access="read" />
+ <property name="SessionLength" type="u" access="read" />
+ <property name="BSSTMStatus" type="u" access="read" />
+ <property name="Stations" type="ao" access="read" />
+ <property name="MACAddressRandomizationMask" type="a{say}" access="readwrite" />
+ <property name="CtrlInterface" type="s" access="readwrite" />
+ <property name="CtrlInterfaceGroup" type="s" access="readwrite" />
+ <property name="EapolVersion" type="s" access="readwrite" />
+ <property name="Bgscan" type="s" access="readwrite" />
+ <property name="DisableScanOffload" type="s" access="readwrite" />
+ <property name="OpenscEnginePath" type="s" access="readwrite" />
+ <property name="OpensslCiphers" type="s" access="readwrite" />
+ <property name="PcscReader" type="s" access="readwrite" />
+ <property name="PcscPin" type="s" access="readwrite" />
+ <property name="ExternalSim" type="s" access="readwrite" />
+ <property name="DriverParam" type="s" access="readwrite" />
+ <property name="Dot11RSNAConfigPMKLifetime" type="s" access="readwrite" />
+ <property name="Dot11RSNAConfigPMKReauthThreshold" type="s" access="readwrite" />
+ <property name="Dot11RSNAConfigSATimeout" type="s" access="readwrite" />
+ <property name="UpdateConfig" type="s" access="readwrite" />
+ <property name="Uuid" type="s" access="readwrite" />
+ <property name="AutoUuid" type="s" access="readwrite" />
+ <property name="DeviceName" type="s" access="readwrite" />
+ <property name="Manufacturer" type="s" access="readwrite" />
+ <property name="ModelName" type="s" access="readwrite" />
+ <property name="ModelNumber" type="s" access="readwrite" />
+ <property name="SerialNumber" type="s" access="readwrite" />
+ <property name="DeviceType" type="s" access="readwrite" />
+ <property name="OsVersion" type="s" access="readwrite" />
+ <property name="ConfigMethods" type="s" access="readwrite" />
+ <property name="SecDeviceType" type="s" access="readwrite" />
+ <property name="IpAddrGo" type="s" access="readwrite" />
+ <property name="IpAddrMask" type="s" access="readwrite" />
+ <property name="IpAddrStart" type="s" access="readwrite" />
+ <property name="IpAddrEnd" type="s" access="readwrite" />
+ <property name="BssMaxCount" type="s" access="readwrite" />
+ <property name="FilterSsids" type="s" access="readwrite" />
+ <property name="FilterRssi" type="s" access="readwrite" />
+ <property name="MaxNumSta" type="s" access="readwrite" />
+ <property name="ApIsolate" type="s" access="readwrite" />
+ <property name="DisassocLowAck" type="s" access="readwrite" />
+ <property name="Hs20" type="s" access="readwrite" />
+ <property name="Interworking" type="s" access="readwrite" />
+ <property name="Hessid" type="s" access="readwrite" />
+ <property name="AccessNetworkType" type="s" access="readwrite" />
+ <property name="GoInterworking" type="s" access="readwrite" />
+ <property name="GoAccessNetworkType" type="s" access="readwrite" />
+ <property name="GoInternet" type="s" access="readwrite" />
+ <property name="GoVenueGroup" type="s" access="readwrite" />
+ <property name="GoVenueType" type="s" access="readwrite" />
+ <property name="PbcInM1" type="s" access="readwrite" />
+ <property name="Autoscan" type="s" access="readwrite" />
+ <property name="ExtPasswordBackend" type="s" access="readwrite" />
+ <property name="AutoInterworking" type="s" access="readwrite" />
+ <property name="Okc" type="s" access="readwrite" />
+ <property name="Pmf" type="s" access="readwrite" />
+ <property name="SaeGroups" type="s" access="readwrite" />
+ <property name="SaePwe" type="s" access="readwrite" />
+ <property name="SaePmkidInAssoc" type="s" access="readwrite" />
+ <property name="DtimPeriod" type="s" access="readwrite" />
+ <property name="BeaconInt" type="s" access="readwrite" />
+ <property name="ApVendorElements" type="s" access="readwrite" />
+ <property name="IgnoreOldScanRes" type="s" access="readwrite" />
+ <property name="FreqList" type="s" access="readwrite" />
+ <property name="ScanCurFreq" type="s" access="readwrite" />
+ <property name="SchedScanInterval" type="s" access="readwrite" />
+ <property name="SchedScanStartDelay" type="s" access="readwrite" />
+ <property name="TdlsExternalControl" type="s" access="readwrite" />
+ <property name="OsuDir" type="s" access="readwrite" />
+ <property name="WowlanTriggers" type="s" access="readwrite" />
+ <property name="MacAddr" type="s" access="readwrite" />
+ <property name="RandAddrLifetime" type="s" access="readwrite" />
+ <property name="PreassocMacAddr" type="s" access="readwrite" />
+ <property name="KeyMgmtOffload" type="s" access="readwrite" />
+ <property name="PassiveScan" type="s" access="readwrite" />
+ <property name="ReassocSameBssOptim" type="s" access="readwrite" />
+ <property name="FstGroupId" type="s" access="readwrite" />
+ <property name="FstPriority" type="s" access="readwrite" />
+ <property name="FstLlt" type="s" access="readwrite" />
+ <property name="CertInCb" type="s" access="readwrite" />
+ <property name="WpaRscRelaxation" type="s" access="readwrite" />
+ <property name="SchedScanPlans" type="s" access="readwrite" />
+ <property name="GasAddress3" type="s" access="readwrite" />
+ <property name="FtmResponder" type="s" access="readwrite" />
+ <property name="FtmInitiator" type="s" access="readwrite" />
+ <property name="GasRandAddrLifetime" type="s" access="readwrite" />
+ <property name="GasRandMacAddr" type="s" access="readwrite" />
+ <property name="DppConfigProcessing" type="s" access="readwrite" />
+ <property name="DppName" type="s" access="readwrite" />
+ <property name="DppMudUrl" type="s" access="readwrite" />
+ <property name="ColocIntfReporting" type="s" access="readwrite" />
+ </interface>
+ <node name="BSSs" />
+ <node name="Networks" />
+</node>
--- /dev/null
+<?xml version="1.0" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://raw.githubusercontent.com/freedesktop/dbus/master/doc/introspect.dtd">
+<node>
+ <interface name="fi.w1.wpa_supplicant1.Network">
+ <signal name="PropertiesChanged">
+ <arg name="properties" type="a{sv}" />
+ </signal>
+ <property name="Properties" type="a{sv}" access="readwrite" />
+ <property name="Enabled" type="b" access="readwrite" />
+ </interface>
+</node>
# See the License for the specific language governing permissions and
# limitations under the License.
+# TIZEN_CHIP_MODIFY
+
import("//build_overrides/chip.gni")
import("${chip_root}/src/ble/ble.gni")
declare_args() {
- # Device platform layer: cc13x2_26x2, darwin, efr32, esp32, external, freertos, linux, nrfconnect, k32w, qpg6100, none.
- chip_device_platform = "auto"
+ # Device platform layer: cc13x2_26x2, darwin, efr32, esp32, external, freertos, linux, nrfconnect, k32w, qpg6100, tizen, none.
+ chip_device_platform = "tizen"
chip_platform_target = ""
}
# Enable openthread support.
chip_enable_openthread =
chip_device_platform == "linux" || chip_device_platform == "qpg6100" ||
- chip_device_platform == "cc13x2_26x2" || chip_device_platform == "k32w"
+ chip_device_platform == "cc13x2_26x2" || chip_device_platform == "k32w" || chip_device_platform == "tizen"
# Enable wifi support.
- chip_enable_wifi = chip_device_platform == "linux"
+ chip_enable_wifi = chip_device_platform == "linux" || chip_device_platform == "tizen"
# Enable ble support.
chip_enable_ble =
chip_config_network_layer_ble &&
(chip_device_platform == "linux" || chip_device_platform == "darwin" ||
- chip_device_platform == "cc13x2_26x2")
+ chip_device_platform == "cc13x2_26x2" || chip_device_platform == "tizen")
if (chip_device_platform == "linux" || chip_device_platform == "esp32") {
chip_mdns = "minimal"
- } else if (chip_device_platform == "darwin") {
+ } else if (chip_device_platform == "darwin" || chip_device_platform == "tizen") {
chip_mdns = "platform"
} else {
chip_mdns = "none"
_chip_device_layer = "ESP32"
} else if (chip_device_platform == "linux") {
_chip_device_layer = "Linux"
+} else if (chip_device_platform == "tizen") {
+ _chip_device_layer = "Tizen"
} else if (chip_device_platform == "nrfconnect") {
_chip_device_layer = "nrfconnect"
} else if (chip_device_platform == "qpg6100") {
chip_device_platform == "cc13x2_26x2" ||
chip_device_platform == "darwin" || chip_device_platform == "efr32" ||
chip_device_platform == "esp32" || chip_device_platform == "external" ||
- chip_device_platform == "linux" ||
+ chip_device_platform == "linux" || chip_device_platform == "tizen" ||
chip_device_platform == "nrfconnect" ||
chip_device_platform == "k32w" || chip_device_platform == "qpg6100",
"Please select a valid value for chip_device_platform")
* limitations under the License.
*/
+/* TIZEN_CHIP_MODIFY */
+
#include <assert.h>
#include <dbus/dbus.h>
#include <memory>
#include "platform/PlatformManager.h"
#include "platform/ThreadStackManager.h"
-#if CHIP_DEVICE_LAYER_TARGET == LINUX
+#if CHIP_DEVICE_LAYER_TARGET == LINUX || CHIP_DEVICE_LAYER_TARGET == TIZEN
#include <thread>
struct DBusConnectionDeleter