blob: 6188740d40d506186abbe5e96f69a2b8d2eb1f7e [file]
// Copyright 2025 The Pigweed 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
//
// https://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 "pw_bluetooth_proxy/config.h"
#if PW_BLUETOOTH_PROXY_ASYNC != 0
#include "pw_assert/check.h"
#include "pw_bluetooth_proxy/internal/generic_l2cap_channel_async.h"
#include "pw_bluetooth_proxy/internal/l2cap_channel_manager.h"
#include "pw_bluetooth_proxy/internal/l2cap_channel_manager_async.h"
namespace pw::bluetooth::proxy::internal {
GenericL2capChannelImpl::GenericL2capChannelImpl(L2capChannel& channel)
: GenericL2capChannelImpl(channel, channel.l2cap_channel_manager_.impl()) {}
GenericL2capChannelImpl::GenericL2capChannelImpl(
L2capChannel& channel, L2capChannelManagerImpl& manager)
: channel_(&channel),
dispatcher_(&manager.dispatcher()),
dispatcher_thread_id_(manager.dispatcher_thread_id()) {}
GenericL2capChannelImpl& GenericL2capChannelImpl::operator=(
GenericL2capChannelImpl&& other) {
if (this == &other) {
return *this;
}
channel_ = std::exchange(other.channel_, nullptr);
dispatcher_ = std::exchange(other.dispatcher_, nullptr);
dispatcher_thread_id_ = std::move(other.dispatcher_thread_id_);
request_handle_ = std::move(other.request_handle_);
request_sender_ = std::move(other.request_sender_);
payload_sender_ = std::move(other.payload_sender_);
send_reservation_ = std::move(other.send_reservation_);
return *this;
}
GenericL2capChannelImpl::~GenericL2capChannelImpl() {
if (request_handle_.is_open()) {
request_handle_.Close();
}
}
Status GenericL2capChannelImpl::Init() {
PW_TRY(channel_->impl().Connect(request_handle_, request_sender_));
channel_->impl().Connect(payload_sender_);
return OkStatus();
}
void GenericL2capChannelImpl::Start() { channel_->Start(); }
StatusWithMultiBuf GenericL2capChannelImpl::Write(
multibuf::MultiBuf&& payload) {
if (auto status = IsWriteAvailable(); !status.ok()) {
return {status, std::move(payload)};
}
send_reservation_->Commit(std::move(payload));
send_reservation_.reset();
return {OkStatus(), std::nullopt};
}
Status GenericL2capChannelImpl::IsWriteAvailable() {
if (!payload_sender_.is_open()) {
return Status::FailedPrecondition();
}
if (send_reservation_.has_value()) {
return OkStatus();
}
auto result = payload_sender_.TryReserveSend();
if (result.ok()) {
send_reservation_ = std::move(*result);
return OkStatus();
}
if (result.status() != Status::Unavailable()) {
return result.status();
}
if (this_thread::get_id() == dispatcher_thread_id_) {
channel_->impl().NotifyOnDequeue();
} else {
Request request;
request.type = Request::Type::kNotifyOnDequeue;
PW_TRY(Send(std::move(request)));
}
return Status::Unavailable();
}
Status GenericL2capChannelImpl::SendAdditionalRxCredits(
uint16_t additional_rx_credits) {
if (!request_handle_.is_open()) {
return Status::FailedPrecondition();
}
if (this_thread::get_id() == dispatcher_thread_id_) {
return channel_->SendAdditionalRxCredits(additional_rx_credits);
}
Request request;
request.type = Request::Type::kSendAdditionalRxCredits;
request.additional_rx_credits = additional_rx_credits;
return Send(std::move(request));
}
void GenericL2capChannelImpl::Stop() {
if (this_thread::get_id() == dispatcher_thread_id_) {
return channel_->Stop();
}
Request request;
request.type = Request::Type::kStop;
Send(std::move(request)).IgnoreError();
}
void GenericL2capChannelImpl::Close() {
if (this_thread::get_id() == dispatcher_thread_id_) {
return channel_->Close();
}
Request request;
request.type = Request::Type::kClose;
Send(std::move(request)).IgnoreError();
}
L2capChannel* GenericL2capChannelImpl::InternalForTesting() {
return request_handle_.is_open() ? channel_ : nullptr;
}
Status GenericL2capChannelImpl::Send(Request&& request) const {
PW_TRY(request_sender_.BlockingSend(*dispatcher_, std::move(request)));
return OkStatus();
}
} // namespace pw::bluetooth::proxy::internal
#endif // PW_BLUETOOTH_PROXY_ASYNC