blob: 4356ba9aa146ead28d1ce9ec40c4870fa9f742cf [file] [log] [blame]
/*
*
* Copyright (c) 2023 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 "BLEAdvertisingArbiter.h"
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <system/SystemError.h>
namespace chip {
namespace DeviceLayer {
namespace BLEAdvertisingArbiter {
namespace {
// List of advertising requests ordered by priority
sys_slist_t sRequests;
bool sIsInitialized = false;
uint8_t sBtId = 0;
// Cast an intrusive list node to the containing request object
const BLEAdvertisingArbiter::Request & ToRequest(const sys_snode_t * node)
{
return *static_cast<const BLEAdvertisingArbiter::Request *>(node);
}
// Notify application about stopped advertising if the callback has been provided
void NotifyAdvertisingStopped(const sys_snode_t * node)
{
VerifyOrReturn(node);
const Request & request = ToRequest(node);
if (request.onStopped != nullptr)
{
request.onStopped();
}
}
// Restart advertising using the top-priority request
CHIP_ERROR RestartAdvertising()
{
// Note: bt_le_adv_stop() returns success when the advertising was not started
ReturnErrorOnFailure(System::MapErrorZephyr(bt_le_adv_stop()));
ReturnErrorCodeIf(sys_slist_is_empty(&sRequests), CHIP_NO_ERROR);
const Request & top = ToRequest(sys_slist_peek_head(&sRequests));
bt_le_adv_param params = BT_LE_ADV_PARAM_INIT(top.options, top.minInterval, top.maxInterval, nullptr);
params.id = sBtId;
const int result = bt_le_adv_start(&params, top.advertisingData.data(), top.advertisingData.size(), top.scanResponseData.data(),
top.scanResponseData.size());
if (top.onStarted != nullptr)
{
top.onStarted(result);
}
return System::MapErrorZephyr(result);
}
} // namespace
CHIP_ERROR Init(uint8_t btId)
{
if (sIsInitialized)
{
return CHIP_ERROR_INCORRECT_STATE;
}
sBtId = btId;
sIsInitialized = true;
return CHIP_NO_ERROR;
}
CHIP_ERROR InsertRequest(Request & request)
{
if (!sIsInitialized)
{
return CHIP_ERROR_INCORRECT_STATE;
}
CancelRequest(request);
sys_snode_t * prev = nullptr;
sys_snode_t * node = nullptr;
// Find position of the request in the list that preserves ordering by priority
SYS_SLIST_FOR_EACH_NODE(&sRequests, node)
{
if (request.priority < ToRequest(node).priority)
{
break;
}
prev = node;
}
if (prev == nullptr)
{
NotifyAdvertisingStopped(sys_slist_peek_head(&sRequests));
sys_slist_prepend(&sRequests, &request);
}
else
{
sys_slist_insert(&sRequests, prev, &request);
}
// If the request is top-priority, restart the advertising
if (sys_slist_peek_head(&sRequests) == &request)
{
return RestartAdvertising();
}
return CHIP_NO_ERROR;
}
void CancelRequest(Request & request)
{
if (!sIsInitialized)
{
return;
}
const bool isTopPriority = (sys_slist_peek_head(&sRequests) == &request);
VerifyOrReturn(sys_slist_find_and_remove(&sRequests, &request));
// If cancelled request was top-priority, restart the advertising.
if (isTopPriority)
{
RestartAdvertising();
}
}
} // namespace BLEAdvertisingArbiter
} // namespace DeviceLayer
} // namespace chip