blob: f5e92b1d6491d914773e7488aae87a655663dfa3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/command_handler.h"
namespace bt::l2cap::internal {
bool CommandHandler::Response::ParseReject(const ByteBuffer& rej_payload_buf) {
if (rej_payload_buf.size() < sizeof(CommandRejectPayload)) {
bt_log(DEBUG,
"l2cap",
"cmd: ignoring malformed Command Reject, size %zu (expected >= %zu)",
rej_payload_buf.size(),
sizeof(CommandRejectPayload));
return false;
}
reject_reason_ = static_cast<RejectReason>(
le16toh(rej_payload_buf.ReadMember<&CommandRejectPayload::reason>()));
if (reject_reason() == RejectReason::kInvalidCID) {
if (rej_payload_buf.size() - sizeof(CommandRejectPayload) <
sizeof(InvalidCIDPayload)) {
bt_log(DEBUG,
"l2cap",
"cmd: ignoring malformed Command Reject Invalid Channel ID, size "
"%zu (expected %zu)",
rej_payload_buf.size(),
sizeof(CommandRejectPayload) + sizeof(InvalidCIDPayload));
return false;
}
const auto& invalid_cid_payload =
rej_payload_buf.view(sizeof(CommandRejectPayload))
.To<InvalidCIDPayload>();
remote_cid_ = le16toh(invalid_cid_payload.src_cid);
local_cid_ = le16toh(invalid_cid_payload.dst_cid);
}
return true;
}
bool CommandHandler::DisconnectionResponse::Decode(
const ByteBuffer& payload_buf) {
const auto disconn_rsp_payload = payload_buf.To<PayloadT>();
local_cid_ = le16toh(disconn_rsp_payload.src_cid);
remote_cid_ = le16toh(disconn_rsp_payload.dst_cid);
return true;
}
CommandHandler::Responder::Responder(SignalingChannel::Responder* sig_responder,
ChannelId local_cid,
ChannelId remote_cid)
: sig_responder_(sig_responder),
local_cid_(local_cid),
remote_cid_(remote_cid) {}
void CommandHandler::Responder::RejectNotUnderstood() {
sig_responder_->RejectNotUnderstood();
}
void CommandHandler::Responder::RejectInvalidChannelId() {
sig_responder_->RejectInvalidChannelId(local_cid(), remote_cid());
}
bool CommandHandler::SendDisconnectionRequest(
ChannelId remote_cid,
ChannelId local_cid,
DisconnectionResponseCallback cb) {
auto on_discon_rsp =
BuildResponseHandler<DisconnectionResponse>(std::move(cb));
DisconnectionRequestPayload payload = {htole16(remote_cid),
htole16(local_cid)};
return sig()->SendRequest(kDisconnectionRequest,
BufferView(&payload, sizeof(payload)),
std::move(on_discon_rsp));
}
void CommandHandler::ServeDisconnectionRequest(
DisconnectionRequestCallback cb) {
auto on_discon_req = [cb = std::move(cb)](
const ByteBuffer& request_payload,
SignalingChannel::Responder* sig_responder) {
if (request_payload.size() != sizeof(DisconnectionRequestPayload)) {
bt_log(DEBUG,
"l2cap",
"cmd: rejecting malformed Disconnection Request, size %zu",
request_payload.size());
sig_responder->RejectNotUnderstood();
return;
}
const auto& discon_req = request_payload.To<DisconnectionRequestPayload>();
const ChannelId local_cid = le16toh(discon_req.dst_cid);
const ChannelId remote_cid = le16toh(discon_req.src_cid);
DisconnectionResponder responder(sig_responder, local_cid, remote_cid);
cb(local_cid, remote_cid, &responder);
};
sig()->ServeRequest(kDisconnectionRequest, std::move(on_discon_req));
}
CommandHandler::CommandHandler(SignalingChannelInterface* sig,
fit::closure request_fail_callback)
: sig_(sig), request_fail_callback_(std::move(request_fail_callback)) {
BT_ASSERT(sig_);
}
} // namespace bt::l2cap::internal