blob: ec08fee272c74fa055e81211eb3698e46cf5fcd0 [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 "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;
}
auto& rej_payload = rej_payload_buf.As<CommandRejectPayload>();
reject_reason_ = static_cast<RejectReason>(letoh16(rej_payload.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;
}
auto& invalid_cid_payload =
rej_payload_buf.view(sizeof(CommandRejectPayload)).As<InvalidCIDPayload>();
remote_cid_ = letoh16(invalid_cid_payload.src_cid);
local_cid_ = letoh16(invalid_cid_payload.dst_cid);
}
return true;
}
bool CommandHandler::DisconnectionResponse::Decode(const ByteBuffer& payload_buf) {
auto& disconn_rsp_payload = payload_buf.As<PayloadT>();
local_cid_ = letoh16(disconn_rsp_payload.src_cid);
remote_cid_ = letoh16(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.As<DisconnectionRequestPayload>();
const ChannelId local_cid = letoh16(discon_req.dst_cid);
const ChannelId remote_cid = letoh16(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)) {
ZX_ASSERT(sig_);
}
} // namespace bt::l2cap::internal