| /* |
| * |
| * 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 contains implementations for the CallbacksMgr class. The object of this |
| * class will be used by Controller applications to interact with ZCL messages. |
| * This class provide mechanism to store callbacks for global message dispatching |
| * across the ZCL stack. |
| */ |
| |
| #include "CHIPDeviceCallbacksMgr.h" |
| |
| #include <inttypes.h> |
| #include <lib/core/CHIPCore.h> |
| |
| namespace chip { |
| namespace app { |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::AddResponseCallback(NodeId nodeId, uint8_t sequenceNumber, |
| Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback, TLVDataFilter filter) |
| { |
| VerifyOrReturnError(onSuccessCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(onFailureCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ResponseCallbackInfo info = { nodeId, sequenceNumber }; |
| static_assert(sizeof(onSuccessCallback->mInfo) >= sizeof(info), "Callback info too large"); |
| memcpy(&onSuccessCallback->mInfo, &info, sizeof(info)); |
| memcpy(&onFailureCallback->mInfo, &info, sizeof(info)); |
| |
| // If some callbacks have already been registered for the same ResponseCallbackInfo, it usually means that the response |
| // has not been received for a previous command with the same sequenceNumber. Cancel the previously registered callbacks. |
| CancelCallback(info, mResponsesSuccess); |
| CancelCallback(info, mResponsesFailure); |
| PopResponseFilter(info, nullptr); |
| |
| if (filter != nullptr) |
| { |
| ReturnErrorOnFailure(AddResponseFilter(info, filter)); |
| } |
| mResponsesSuccess.Enqueue(onSuccessCallback); |
| mResponsesFailure.Enqueue(onFailureCallback); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::CancelResponseCallback(NodeId nodeId, uint8_t sequenceNumber) |
| { |
| ResponseCallbackInfo info = { nodeId, sequenceNumber }; |
| CancelCallback(info, mResponsesSuccess); |
| CancelCallback(info, mResponsesFailure); |
| PopResponseFilter(info, nullptr); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::AddResponseFilter(const ResponseCallbackInfo & info, TLVDataFilter filter) |
| { |
| constexpr ResponseCallbackInfo kEmptyInfo{ kPlaceholderNodeId, 0 }; |
| |
| for (size_t i = 0; i < kTLVFilterPoolSize; i++) |
| { |
| if (mTLVFilterPool[i].info == kEmptyInfo) |
| { |
| mTLVFilterPool[i].info = info; |
| mTLVFilterPool[i].filter = filter; |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::PopResponseFilter(const ResponseCallbackInfo & info, TLVDataFilter * outFilter) |
| { |
| for (size_t i = 0; i < kTLVFilterPoolSize; i++) |
| { |
| if (mTLVFilterPool[i].info == info) |
| { |
| if (outFilter != nullptr) |
| { |
| *outFilter = mTLVFilterPool[i].filter; |
| } |
| mTLVFilterPool[i].info = ResponseCallbackInfo{ kPlaceholderNodeId, 0 }; |
| mTLVFilterPool[i].filter = nullptr; |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| return CHIP_ERROR_KEY_NOT_FOUND; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::GetResponseCallback(NodeId nodeId, uint8_t sequenceNumber, |
| Callback::Cancelable ** onSuccessCallback, |
| Callback::Cancelable ** onFailureCallback, TLVDataFilter * outFilter) |
| { |
| ResponseCallbackInfo info = { nodeId, sequenceNumber }; |
| |
| ReturnErrorOnFailure(GetCallback(info, mResponsesSuccess, onSuccessCallback)); |
| (*onSuccessCallback)->Cancel(); |
| |
| ReturnErrorOnFailure(GetCallback(info, mResponsesFailure, onFailureCallback)); |
| (*onFailureCallback)->Cancel(); |
| |
| if (outFilter == nullptr) |
| { |
| PopResponseFilter(info, nullptr); |
| } |
| else |
| { |
| ReturnErrorOnFailure(PopResponseFilter(info, outFilter)); |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::SetSubscribeFilter(const ReportCallbackInfo & info, TLVDataFilter filter) |
| { |
| constexpr ReportCallbackInfo kEmptyInfo{ kPlaceholderNodeId, 0, 0, 0 }; |
| |
| for (size_t i = 0; i < kTLVFilterPoolSize; i++) |
| { |
| if (mReportFilterPool[i].info == info) |
| { |
| mReportFilterPool[i].filter = filter; |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| for (size_t i = 0; i < kTLVFilterPoolSize; i++) |
| { |
| if (mReportFilterPool[i].info == kEmptyInfo) |
| { |
| mReportFilterPool[i].info = info; |
| mReportFilterPool[i].filter = filter; |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::GetSubscribeFilter(const ReportCallbackInfo & info, TLVDataFilter * outFilter) |
| { |
| for (size_t i = 0; i < kTLVFilterPoolSize; i++) |
| { |
| if (mReportFilterPool[i].info == info) |
| { |
| if (outFilter != nullptr) |
| { |
| *outFilter = mReportFilterPool[i].filter; |
| } |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| return CHIP_ERROR_KEY_NOT_FOUND; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::AddReportCallback(NodeId nodeId, EndpointId endpointId, ClusterId clusterId, |
| AttributeId attributeId, Callback::Cancelable * onReportCallback, |
| TLVDataFilter filter) |
| { |
| VerifyOrReturnError(onReportCallback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(filter != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ReportCallbackInfo info = { nodeId, endpointId, clusterId, attributeId }; |
| static_assert(sizeof(onReportCallback->mInfo) >= sizeof(info), "Callback info too large"); |
| memcpy(&onReportCallback->mInfo, &info, sizeof(info)); |
| |
| // If a callback has already been registered for the same ReportCallbackInfo, let's cancel it. |
| CancelCallback(info, mReports); |
| |
| ReturnErrorOnFailure(SetSubscribeFilter(info, filter)); |
| |
| mReports.Enqueue(onReportCallback); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR CHIPDeviceCallbacksMgr::GetReportCallback(NodeId nodeId, EndpointId endpointId, ClusterId clusterId, |
| AttributeId attributeId, Callback::Cancelable ** onReportCallback, |
| TLVDataFilter * outFilter) |
| { |
| ReportCallbackInfo info = { nodeId, endpointId, clusterId, attributeId }; |
| |
| ReturnErrorOnFailure(GetCallback(info, mReports, onReportCallback)); |
| ReturnErrorOnFailure(GetSubscribeFilter(info, outFilter)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace app |
| } // namespace chip |