blob: cf9259038110348f13e5e45853986d302e983a55 [file]
/*
* Copyright (c) 2026 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.
*/
#pragma once
#include <clusters/ProximityRanging/Commands.h>
#include <clusters/ProximityRanging/Enums.h>
#include <clusters/ProximityRanging/Structs.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/BitMask.h>
#include <lib/support/Span.h>
#include <system/SystemClock.h>
#include <cstdint>
#include <optional>
namespace chip {
namespace app {
namespace Clusters {
namespace ProximityRanging {
/// Length of the Device Identity Key for Wi-Fi USD and BLTCS.
inline constexpr size_t kDeviceIdentityKeyLen = 16;
using DeviceIdentityKey = uint8_t[kDeviceIdentityKeyLen];
/// Per-technology configuration exposed via the cluster's optional attributes.
/// Adapters return one of these only when they implement the matching technology;
/// otherwise the corresponding Get accessor returns std::nullopt and the cluster
/// surfaces UnsupportedAttribute.
struct BleRbcConfig
{
uint64_t deviceId;
};
struct WiFiUsdConfig
{
DeviceIdentityKey deviceIdentityKey;
};
struct BltcsConfig
{
DeviceIdentityKey deviceIdentityKey;
BLTCSSecurityLevelEnum securityLevel;
BLTCSModeEnum modeCapability;
};
/**
* Narrowed parameter set passed to RangingAdapter::PrepareSession.
*
* The driver owns ALL wall-clock scheduling for a session: it gates the
* StartTime delay before invoking StartSession, drives subsequent periodic
* ranging instances by re-invoking StartSession on each rangingInstanceInterval
* tick, and applies the EndTime cutoff via StopSession. Adapters therefore
* never see the request's startTime / endTime / rangingInstanceInterval
* fields — only the role / band selectors the radio needs to range with the
* peer. The driver also filters OnMeasurementData against ReportingCondition
* itself, so adapters do not receive that field either.
*/
struct StartSessionParams
{
RangingTechEnum technology;
std::optional<Structs::BLERangingDeviceRoleConfigStruct::DecodableType> bleRoleConfig;
std::optional<Structs::WiFiRangingDeviceRoleConfigStruct::DecodableType> wifiRoleConfig;
std::optional<Structs::BLTChannelSoundingDeviceRoleConfigStruct::DecodableType> bltRoleConfig;
std::optional<chip::BitMask<RadioBandBitmap>> frequencyBand;
std::optional<chip::BitMask<RangingBandwidthBitmap>> bandwidth;
};
/**
* Interface for a single ranging technology (BLE RSSI, Channel Sounding, Wi-Fi USD, etc.).
*
* Lifecycle: platform-specific adapters are owned by the application and must
* be registered with ProximityRangingDriver before the Proximity Ranging
* cluster is initialised. Adapters must outlive the driver, or be
* unregistered before destruction.
*
* Single-shot session model
* -------------------------
* Each ranging session is brought up in two steps so the driver can centralise
* StartTime / EndTime / RangingInstanceInterval scheduling that would
* otherwise be duplicated in every adapter:
*
* 1. PrepareSession(sessionId, params) — synchronous. The adapter validates
* that the radio can satisfy the request (peer compatibility, security
* mode, resource availability, ...) and stages whatever per-session
* bookkeeping it needs. Returns a ResultCodeEnum: kAccepted commits the
* adapter to the session; any other value rejects the request before the
* cluster surfaces a session ID to the client. If the adapter accepted
* and stashed resources, those resources MUST be released on a subsequent
* StopSession call (see "Stop semantics" below).
*
* 2. StartSession(sessionId) — synchronous. Triggers ONE ranging instance.
* The driver invokes StartSession the first time after the StartTime
* delay (immediately when StartTime == 0); for periodic ranging, it
* invokes StartSession again on each rangingInstanceInterval tick. The
* adapter performs a single ranging measurement using the parameters
* stashed in PrepareSession, emits exactly one OnMeasurementData
* callback per accepted StartSession invocation, and then waits for
* either another StartSession (next periodic tick) or StopSession
* (terminal teardown). The session record is NOT released between
* StartSession invocations; only StopSession releases it.
*
* StartSession returns CHIP_NO_ERROR when the radio has been kicked off
* successfully. If the adapter is busy completing a previous ranging
* instance (the radio cannot satisfy a new request yet), it MAY return
* CHIP_ERROR_BUSY; the driver continues with its periodic schedule and
* will retry on the next tick. Asynchronous radio failures MUST be
* reported via OnRangingSessionStopped with kHardwareError.
*
* Session IDs are 1-byte values allocated by the driver and passed verbatim
* into both calls. The adapter is responsible for tracking which IDs are
* currently in flight (whether prepared, in flight, or idle between ticks)
* and reporting termination of every session it has accepted via
* PrepareSession through OnRangingSessionStopped.
*
* Stop semantics
* --------------
* StopSession(sessionId) MUST work uniformly regardless of the session's
* internal state — prepared-but-never-started, currently measuring, or
* idle-between-ticks. In every case the adapter releases its per-session
* resources, performs any radio teardown that applies to the current state,
* and emits OnRangingSessionStopped exactly once.
*
* Adapters MUST NOT deliver OnRangingSessionStopped synchronously from inside
* PrepareSession; if the start is already known to have failed at preparation
* time, return a non-Accepted ResultCodeEnum from PrepareSession instead.
* Termination callbacks are valid only after PrepareSession has returned
* kAccepted.
*
* Async events:
* - OnMeasurementData: emitted exactly once per accepted StartSession
* invocation, when the radio has produced a measurement. The driver
* forwards the measurement to the cluster immediately and, for periodic
* ranging, schedules the next StartSession on its own timer.
* - OnAttributeChanged: called when a runtime configuration value the
* adapter exposes as an attribute changes, so the cluster can mark it
* dirty.
* - OnRangingSessionStopped: emitted when a ranging session has stopped
* either by request (StopSession), by hardware failure, or by any other
* adapter-internal reason. The driver invokes StopSession at EndTime, so
* adapters do not need a self-managed end-time cutoff.
*
* Threading: methods on this interface are called from the Matter main
* thread. The Callback is thread-safe; adapters MAY invoke it from any
* thread.
*
* Optional capabilities: GetDeviceId defaults to std::nullopt; override only
* when the adapter exposes a stable per-device identifier (e.g. BLE Device ID).
*/
class RangingAdapter
{
public:
class Callback
{
public:
virtual ~Callback() = default;
virtual void OnRangingSessionStopped(uint8_t sessionId, RangingSessionStatusEnum status) = 0;
virtual void OnMeasurementData(uint8_t sessionId, const Structs::RangingMeasurementDataStruct::Type & measurement) = 0;
virtual void OnAttributeChanged(AttributeId attributeId) = 0;
};
virtual ~RangingAdapter() = default;
/**
* The callback must remain valid until the adapter is unregistered or destroyed.
*/
void SetCallback(Callback * callback) { mCallback = callback; }
virtual RangingTechEnum GetTechnology() const = 0;
virtual Structs::RangingCapabilitiesStruct::Type GetCapabilities() const = 0;
/**
* Validate and stage a ranging session. Returns kAccepted to commit the
* adapter to the session; any other value rejects it. The driver only
* surfaces a session ID to the client when this returns kAccepted.
*
* After kAccepted, the adapter MUST emit OnRangingSessionStopped exactly
* once for this session — either in response to a future StopSession
* call, or asynchronously on hardware-driven termination.
*/
virtual ResultCodeEnum PrepareSession(uint8_t sessionId, const StartSessionParams & params) = 0;
/**
* Trigger one ranging instance for a previously prepared session. The
* driver invokes this after the StartTime delay for the first measurement
* and again on each rangingInstanceInterval tick for periodic ranging.
*
* Returns:
* - CHIP_NO_ERROR : the radio has been kicked off; the adapter will
* emit exactly one OnMeasurementData callback when
* the measurement completes.
* - CHIP_ERROR_BUSY: the previous measurement has not yet completed and
* a new one cannot start. The driver will not retry
* this tick; the next periodic tick (if any) will
* attempt again.
* - other errors : terminal failure that the adapter SHOULD also
* surface via OnRangingSessionStopped(kHardwareError);
* the driver logs and continues until StopSession is
* invoked.
*/
virtual CHIP_ERROR StartSession(uint8_t sessionId) = 0;
/// Stop a session in any internal state (prepared-but-never-started,
/// measuring, or idle-between-ticks). The adapter MUST emit
/// OnRangingSessionStopped for the session as a result of this call.
virtual CHIP_ERROR StopSession(uint8_t sessionId) = 0;
virtual void StopAllSessions() = 0;
/**
* Append the adapter's currently-active session IDs to the caller-supplied
* span. The span's size on entry is its capacity; on exit the size reflects
* how many IDs were written. Returns CHIP_ERROR_BUFFER_TOO_SMALL if the
* capacity is insufficient.
*/
virtual CHIP_ERROR GetActiveSessionIds(Span<uint8_t> & sessionIds) = 0;
/// Override only when the adapter exposes a stable per-device 64-bit
/// identifier (e.g. BLE Device ID). Default returns std::nullopt.
virtual std::optional<uint64_t> GetDeviceId() { return std::nullopt; }
/// Override only on adapters whose technology is one of the Wi-Fi USD
/// proximity-detection technologies (WiFiRoundTripTimeRanging or
/// WiFiNextGenerationRanging). Default returns std::nullopt.
virtual std::optional<WiFiUsdConfig> GetWiFiUsdConfig() { return std::nullopt; }
/// Override only on adapters whose technology is BluetoothChannelSounding.
/// Default returns std::nullopt.
virtual std::optional<BltcsConfig> GetBltcsConfig() { return std::nullopt; }
protected:
Callback * mCallback = nullptr;
};
} // namespace ProximityRanging
} // namespace Clusters
} // namespace app
} // namespace chip