/* * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace chip; // TODO: Need to make it so that declarations of things that don't depend on generated files are not intermixed in af.h with // dependencies on generated files, so we don't have to re-declare things here. // Note: Some of the generated files that depended by af.h are gen_config.h and gen_tokens.h typedef uint8_t EmberAfClusterMask; extern uint16_t emberAfEndpointCount(); extern uint16_t emberAfIndexFromEndpoint(EndpointId endpoint); extern uint8_t emberAfClusterCount(EndpointId endpoint, bool server); extern uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster); extern uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster, chip::AttributeId attributeId); extern chip::EndpointId emberAfEndpointFromIndex(uint16_t index); extern Optional emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server); extern Optional emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster, uint16_t attributeIndex); extern uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask); extern bool emberAfEndpointIndexIsEnabled(uint16_t index); namespace chip { namespace app { AttributePathExpandIteratorEmber::AttributePathExpandIteratorEmber(DataModel::Provider *, SingleLinkedListNode * aAttributePath) : mpAttributePath(aAttributePath) { // Reset iterator state mEndpointIndex = UINT16_MAX; mClusterIndex = UINT8_MAX; mAttributeIndex = UINT16_MAX; static_assert(std::numeric_limits::max() >= ArraySize(GlobalAttributesNotInMetadata), "Our index won't be able to hold the value we need to hold."); static_assert(std::is_same::value, "If this changes audit all uses where we set to UINT8_MAX"); mGlobalAttributeIndex = UINT8_MAX; // Make the iterator ready to emit the first valid path in the list. Next(); } void AttributePathExpandIteratorEmber::PrepareEndpointIndexRange(const AttributePathParams & aAttributePath) { if (aAttributePath.HasWildcardEndpointId()) { mEndpointIndex = 0; mEndEndpointIndex = emberAfEndpointCount(); } else { mEndpointIndex = emberAfIndexFromEndpoint(aAttributePath.mEndpointId); // If the given cluster id does not exist on the given endpoint, it will return uint16(0xFFFF), then endEndpointIndex // will be 0, means we should iterate a null endpoint set (skip it). mEndEndpointIndex = static_cast(mEndpointIndex + 1); } } void AttributePathExpandIteratorEmber::PrepareClusterIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId) { if (aAttributePath.HasWildcardClusterId()) { mClusterIndex = 0; mEndClusterIndex = emberAfClusterCount(aEndpointId, true /* server */); } else { mClusterIndex = emberAfClusterIndex(aEndpointId, aAttributePath.mClusterId, CLUSTER_MASK_SERVER); // If the given cluster id does not exist on the given endpoint, it will return uint8(0xFF), then endClusterIndex // will be 0, means we should iterate a null cluster set (skip it). mEndClusterIndex = static_cast(mClusterIndex + 1); } } void AttributePathExpandIteratorEmber::PrepareAttributeIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId, ClusterId aClusterId) { if (aAttributePath.HasWildcardAttributeId()) { mAttributeIndex = 0; mEndAttributeIndex = emberAfGetServerAttributeCount(aEndpointId, aClusterId); mGlobalAttributeIndex = 0; mGlobalAttributeEndIndex = ArraySize(GlobalAttributesNotInMetadata); } else { mAttributeIndex = emberAfGetServerAttributeIndexByAttributeId(aEndpointId, aClusterId, aAttributePath.mAttributeId); // If the given attribute id does not exist on the given endpoint, it will return uint16(0xFFFF), then endAttributeIndex // will be 0, means we should iterate a null attribute set (skip it). mEndAttributeIndex = static_cast(mAttributeIndex + 1); if (mAttributeIndex == UINT16_MAX) { // Check whether this is a non-metadata global attribute. // // Default to the max value, which will correspond (after we add 1 // and overflow to 0 for the max index) to us not going through // non-metadata global attributes for this attribute. mGlobalAttributeIndex = UINT8_MAX; static_assert(ArraySize(GlobalAttributesNotInMetadata) <= UINT8_MAX, "Iterating over at most 256 array entries"); const uint8_t arraySize = static_cast(ArraySize(GlobalAttributesNotInMetadata)); for (uint8_t idx = 0; idx < arraySize; ++idx) { if (GlobalAttributesNotInMetadata[idx] == aAttributePath.mAttributeId) { mGlobalAttributeIndex = idx; break; } } mGlobalAttributeEndIndex = static_cast(mGlobalAttributeIndex + 1); } else { mGlobalAttributeIndex = UINT8_MAX; mGlobalAttributeEndIndex = 0; } } } void AttributePathExpandIteratorEmber::ResetCurrentCluster() { // If this is a null iterator, or the attribute id of current cluster info is not a wildcard attribute id, then this function // will do nothing, since we won't be expanding the wildcard attribute ids under a cluster. VerifyOrReturn(mpAttributePath != nullptr && mpAttributePath->mValue.HasWildcardAttributeId()); // Otherwise, we will reset the index for iterating the attributes, so we report the attributes for this cluster again. This // will ensure that the client sees a coherent view of the cluster from the reports generated by a single (wildcard) attribute // path in the request. // // Note that when Next() returns, we must be in one of the following states: // - This is not a wildcard path // - We just expanded some attribute id field // - We have exhausted all paths // Only the second case will happen here since the above check will fail for 1 and 3, so the following Next() call must result // in a valid path, which is the first attribute id we will emit for the current cluster. mAttributeIndex = UINT16_MAX; mGlobalAttributeIndex = UINT8_MAX; Next(); } bool AttributePathExpandIteratorEmber::Next() { for (; mpAttributePath != nullptr; (mpAttributePath = mpAttributePath->mpNext, mEndpointIndex = UINT16_MAX)) { mOutputPath.mExpanded = mpAttributePath->mValue.IsWildcardPath(); if (mEndpointIndex == UINT16_MAX) { // Special case: If this is a concrete path, we just return its value as-is. if (!mpAttributePath->mValue.IsWildcardPath()) { mOutputPath.mEndpointId = mpAttributePath->mValue.mEndpointId; mOutputPath.mClusterId = mpAttributePath->mValue.mClusterId; mOutputPath.mAttributeId = mpAttributePath->mValue.mAttributeId; // Prepare for next iteration mEndpointIndex = mEndEndpointIndex = 0; return true; } PrepareEndpointIndexRange(mpAttributePath->mValue); mClusterIndex = UINT8_MAX; } for (; mEndpointIndex < mEndEndpointIndex; (mEndpointIndex++, mClusterIndex = UINT8_MAX, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX)) { if (!emberAfEndpointIndexIsEnabled(mEndpointIndex)) { // Not an enabled endpoint; skip it. continue; } EndpointId endpointId = emberAfEndpointFromIndex(mEndpointIndex); if (mClusterIndex == UINT8_MAX) { PrepareClusterIndexRange(mpAttributePath->mValue, endpointId); mAttributeIndex = UINT16_MAX; mGlobalAttributeIndex = UINT8_MAX; } for (; mClusterIndex < mEndClusterIndex; (mClusterIndex++, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX)) { // emberAfGetNthClusterId must return a valid cluster id here since we have verified the mClusterIndex does // not exceed the mEndClusterIndex. ClusterId clusterId = emberAfGetNthClusterId(endpointId, mClusterIndex, true /* server */).Value(); if (mAttributeIndex == UINT16_MAX && mGlobalAttributeIndex == UINT8_MAX) { PrepareAttributeIndexRange(mpAttributePath->mValue, endpointId, clusterId); } if (mAttributeIndex < mEndAttributeIndex) { // GetServerAttributeIdByIdex must return a valid attribute here since we have verified the mAttributeIndex does // not exceed the mEndAttributeIndex. mOutputPath.mAttributeId = emberAfGetServerAttributeIdByIndex(endpointId, clusterId, mAttributeIndex).Value(); mOutputPath.mClusterId = clusterId; mOutputPath.mEndpointId = endpointId; mAttributeIndex++; // We found a valid attribute path, now return and increase the attribute index for next iteration. // Return true will skip the increment of mClusterIndex, mEndpointIndex and mpAttributePath. return true; } if (mGlobalAttributeIndex < mGlobalAttributeEndIndex) { // Return a path pointing to the next global attribute. mOutputPath.mAttributeId = GlobalAttributesNotInMetadata[mGlobalAttributeIndex]; mOutputPath.mClusterId = clusterId; mOutputPath.mEndpointId = endpointId; mGlobalAttributeIndex++; return true; } // We have exhausted all attributes of this cluster, continue iterating over attributes of next cluster. } // We have exhausted all clusters of this endpoint, continue iterating over clusters of next endpoint. } // We have exhausted all endpoints in this cluster info, continue iterating over next cluster info item. } // Reset to default, invalid value. mOutputPath = ConcreteReadAttributePath(); return false; } } // namespace app } // namespace chip