| // Copyright 2018 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/l2cap/bredr_command_handler.h" |
| |
| #include <endian.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/packet_view.h" |
| |
| namespace bt { |
| namespace l2cap { |
| namespace internal { |
| |
| bool BrEdrCommandHandler::Response::ParseReject( |
| const ByteBuffer& rej_payload_buf) { |
| 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) < 4) { |
| bt_log( |
| ERROR, "l2cap-bredr", |
| "cmd: ignoring malformed Command Reject Invalid Channel ID, size %zu", |
| rej_payload_buf.size()); |
| return false; |
| } |
| |
| remote_cid_ = (rej_payload.data[1] << 8) + rej_payload.data[0]; |
| local_cid_ = (rej_payload.data[3] << 8) + rej_payload.data[2]; |
| } |
| return true; |
| } |
| |
| void BrEdrCommandHandler::ConnectionResponse::Decode( |
| const ByteBuffer& payload_buf) { |
| auto& conn_rsp_payload = payload_buf.As<PayloadT>(); |
| remote_cid_ = letoh16(conn_rsp_payload.dst_cid); |
| local_cid_ = letoh16(conn_rsp_payload.src_cid); |
| result_ = static_cast<ConnectionResult>(letoh16(conn_rsp_payload.result)); |
| conn_status_ = |
| static_cast<ConnectionStatus>(letoh16(conn_rsp_payload.status)); |
| } |
| |
| void BrEdrCommandHandler::ConfigurationResponse::Decode( |
| const ByteBuffer& payload_buf) { |
| PacketView<PayloadT> config_rsp(&payload_buf, |
| payload_buf.size() - sizeof(PayloadT)); |
| local_cid_ = letoh16(config_rsp.header().src_cid); |
| flags_ = letoh16(config_rsp.header().flags); |
| result_ = |
| static_cast<ConfigurationResult>(letoh16(config_rsp.header().result)); |
| options_ = config_rsp.payload_data().view(); |
| } |
| |
| void BrEdrCommandHandler::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); |
| } |
| |
| void BrEdrCommandHandler::InformationResponse::Decode( |
| const ByteBuffer& payload_buf) { |
| // TODO(BT-356): Implement Information Response decoding |
| } |
| |
| BrEdrCommandHandler::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 BrEdrCommandHandler::Responder::RejectNotUnderstood() { |
| sig_responder_->RejectNotUnderstood(); |
| } |
| |
| void BrEdrCommandHandler::Responder::RejectInvalidChannelId() { |
| sig_responder_->RejectInvalidChannelId(local_cid(), remote_cid()); |
| } |
| |
| BrEdrCommandHandler::ConnectionResponder::ConnectionResponder( |
| SignalingChannel::Responder* sig_responder, ChannelId remote_cid) |
| : Responder(sig_responder, kInvalidChannelId, remote_cid) {} |
| |
| void BrEdrCommandHandler::ConnectionResponder::Send(ChannelId local_cid, |
| ConnectionResult result, |
| ConnectionStatus status) { |
| ConnectionResponsePayload conn_rsp = { |
| htole16(local_cid), htole16(remote_cid()), |
| static_cast<ConnectionResult>(htole16(result)), |
| static_cast<ConnectionStatus>(htole16(status))}; |
| sig_responder_->Send(BufferView(&conn_rsp, sizeof(conn_rsp))); |
| } |
| |
| BrEdrCommandHandler::ConfigurationResponder::ConfigurationResponder( |
| SignalingChannel::Responder* sig_responder, ChannelId local_cid) |
| : Responder(sig_responder, local_cid) {} |
| |
| void BrEdrCommandHandler::ConfigurationResponder::Send( |
| ChannelId remote_cid, uint16_t flags, ConfigurationResult result, |
| const ByteBuffer& data) { |
| DynamicByteBuffer config_rsp_buf(sizeof(ConfigurationResponsePayload) + |
| data.size()); |
| MutablePacketView<ConfigurationResponsePayload> config_rsp(&config_rsp_buf, |
| data.size()); |
| config_rsp.mutable_header()->src_cid = htole16(remote_cid); |
| config_rsp.mutable_header()->flags = htole16(flags); |
| config_rsp.mutable_header()->result = |
| static_cast<ConfigurationResult>(htole16(result)); |
| config_rsp.mutable_payload_data().Write(data); |
| sig_responder_->Send(config_rsp.data()); |
| } |
| |
| BrEdrCommandHandler::DisconnectionResponder::DisconnectionResponder( |
| SignalingChannel::Responder* sig_responder, ChannelId local_cid, |
| ChannelId remote_cid) |
| : Responder(sig_responder, local_cid, remote_cid) {} |
| |
| void BrEdrCommandHandler::DisconnectionResponder::Send() { |
| DisconnectionResponsePayload discon_rsp = {htole16(local_cid()), |
| htole16(remote_cid())}; |
| sig_responder_->Send(BufferView(&discon_rsp, sizeof(discon_rsp))); |
| } |
| |
| BrEdrCommandHandler::InformationResponder::InformationResponder( |
| SignalingChannel::Responder* sig_responder, InformationType type) |
| : Responder(sig_responder), type_(type) {} |
| |
| void BrEdrCommandHandler::InformationResponder::SendNotSupported() { |
| Send(InformationResult::kNotSupported, BufferView()); |
| } |
| |
| void BrEdrCommandHandler::InformationResponder::SendConnectionlessMtu( |
| uint16_t mtu) { |
| mtu = htole16(mtu); |
| Send(InformationResult::kSuccess, BufferView(&mtu, sizeof(mtu))); |
| } |
| |
| void BrEdrCommandHandler::InformationResponder::SendExtendedFeaturesSupported( |
| ExtendedFeatures extended_features) { |
| extended_features = htole32(extended_features); |
| Send(InformationResult::kSuccess, |
| BufferView(&extended_features, sizeof(extended_features))); |
| } |
| |
| void BrEdrCommandHandler::InformationResponder::SendFixedChannelsSupported( |
| FixedChannelsSupported channels_supported) { |
| channels_supported = htole64(channels_supported); |
| Send(InformationResult::kSuccess, |
| BufferView(&channels_supported, sizeof(channels_supported))); |
| } |
| |
| void BrEdrCommandHandler::InformationResponder::Send(InformationResult result, |
| const ByteBuffer& data) { |
| constexpr size_t kMaxPayloadLength = |
| sizeof(InformationResponsePayload) + sizeof(uint64_t); |
| StaticByteBuffer<kMaxPayloadLength> info_rsp_buf; |
| MutablePacketView<InformationResponsePayload> info_rsp_view(&info_rsp_buf, |
| data.size()); |
| |
| info_rsp_view.mutable_header()->type = |
| static_cast<InformationType>(htole16(type_)); |
| info_rsp_view.mutable_header()->result = |
| static_cast<InformationResult>(htole16(result)); |
| info_rsp_view.mutable_payload_data().Write(data); |
| sig_responder_->Send(info_rsp_view.data()); |
| } |
| |
| BrEdrCommandHandler::BrEdrCommandHandler(SignalingChannelInterface* sig) |
| : sig_(sig) { |
| ZX_DEBUG_ASSERT(sig_); |
| } |
| |
| bool BrEdrCommandHandler::SendConnectionRequest(uint16_t psm, |
| ChannelId local_cid, |
| ConnectionResponseCallback cb) { |
| auto on_conn_rsp = BuildResponseHandler<ConnectionResponse>(std::move(cb)); |
| |
| ConnectionRequestPayload payload = {htole16(psm), htole16(local_cid)}; |
| return sig_->SendRequest(kConnectionRequest, |
| BufferView(&payload, sizeof(payload)), |
| std::move(on_conn_rsp)); |
| } |
| |
| bool BrEdrCommandHandler::SendConfigurationRequest( |
| ChannelId remote_cid, uint16_t flags, const ByteBuffer& options, |
| ConfigurationResponseCallback cb) { |
| auto on_config_rsp = |
| BuildResponseHandler<ConfigurationResponse>(std::move(cb)); |
| |
| DynamicByteBuffer config_req_buf(sizeof(ConfigurationRequestPayload) + |
| options.size()); |
| MutablePacketView<ConfigurationRequestPayload> config_req(&config_req_buf, |
| options.size()); |
| config_req.mutable_header()->dst_cid = htole16(remote_cid); |
| config_req.mutable_header()->flags = htole16(flags); |
| config_req.mutable_payload_data().Write(options); |
| return sig_->SendRequest(kConfigurationRequest, config_req_buf, |
| std::move(on_config_rsp)); |
| } |
| |
| bool BrEdrCommandHandler::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)); |
| } |
| |
| bool BrEdrCommandHandler::SendInformationRequest( |
| InformationType type, InformationResponseCallback cb) { |
| // TODO(BT-356): Implement requesting remote features and fixed channels |
| bt_log(ERROR, "l2cap-bredr", "cmd: Information Request not sent"); |
| return false; |
| } |
| |
| void BrEdrCommandHandler::ServeConnectionRequest(ConnectionRequestCallback cb) { |
| auto on_conn_req = [cb = std::move(cb)]( |
| const ByteBuffer& request_payload, |
| SignalingChannel::Responder* sig_responder) { |
| if (request_payload.size() != sizeof(ConnectionRequestPayload)) { |
| bt_log(TRACE, "l2cap-bredr", |
| "cmd: rejecting malformed Connection Request, size %zu", |
| request_payload.size()); |
| sig_responder->RejectNotUnderstood(); |
| return; |
| } |
| |
| const auto& conn_req = request_payload.As<ConnectionRequestPayload>(); |
| const PSM psm = letoh16(conn_req.psm); |
| const ChannelId remote_cid = letoh16(conn_req.src_cid); |
| |
| ConnectionResponder responder(sig_responder, remote_cid); |
| |
| // v5.0 Vol 3, Part A, Sec 4.2: PSMs shall be odd and the least significant |
| // bit of the most significant byte shall be zero |
| if (((psm & 0x0001) != 0x0001) || ((psm & 0x0100) != 0x0000)) { |
| bt_log(TRACE, "l2cap-bredr", |
| "Rejecting connection for invalid PSM %#.4x from channel %#.4x", |
| psm, remote_cid); |
| responder.Send(kInvalidChannelId, ConnectionResult::kPSMNotSupported, |
| ConnectionStatus::kNoInfoAvailable); |
| return; |
| } |
| |
| // Check that source channel ID is in range (v5.0 Vol 3, Part A, Sec 2.1) |
| if (remote_cid < kFirstDynamicChannelId) { |
| bt_log(TRACE, "l2cap-bredr", |
| "Rejecting connection for PSM %#.4x from invalid channel %#.4x", |
| psm, remote_cid); |
| responder.Send(kInvalidChannelId, ConnectionResult::kInvalidSourceCID, |
| ConnectionStatus::kNoInfoAvailable); |
| return; |
| } |
| |
| cb(psm, remote_cid, &responder); |
| }; |
| |
| sig_->ServeRequest(kConnectionRequest, std::move(on_conn_req)); |
| } |
| |
| void BrEdrCommandHandler::ServeConfigurationRequest( |
| ConfigurationRequestCallback cb) { |
| auto on_config_req = [cb = std::move(cb)]( |
| const ByteBuffer& request_payload, |
| SignalingChannel::Responder* sig_responder) { |
| if (request_payload.size() < sizeof(ConfigurationRequestPayload)) { |
| bt_log(TRACE, "l2cap-bredr", |
| "cmd: rejecting malformed Configuration Request, size %zu", |
| request_payload.size()); |
| sig_responder->RejectNotUnderstood(); |
| return; |
| } |
| |
| PacketView<ConfigurationRequestPayload> config_req( |
| &request_payload, |
| request_payload.size() - sizeof(ConfigurationRequestPayload)); |
| const auto local_cid = |
| static_cast<ChannelId>(letoh16(config_req.header().dst_cid)); |
| const uint16_t flags = letoh16(config_req.header().flags); |
| ConfigurationResponder responder(sig_responder, local_cid); |
| cb(local_cid, flags, config_req.payload_data(), &responder); |
| }; |
| |
| sig_->ServeRequest(kConfigurationRequest, std::move(on_config_req)); |
| } |
| |
| void BrEdrCommandHandler::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(TRACE, "l2cap-bredr", |
| "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)); |
| } |
| |
| void BrEdrCommandHandler::ServeInformationRequest( |
| InformationRequestCallback cb) { |
| auto on_info_req = [cb = std::move(cb)]( |
| const ByteBuffer& request_payload, |
| SignalingChannel::Responder* sig_responder) { |
| if (request_payload.size() != sizeof(InformationRequestPayload)) { |
| bt_log(TRACE, "l2cap-bredr", |
| "cmd: rejecting malformed Information Request, size %zu", |
| request_payload.size()); |
| sig_responder->RejectNotUnderstood(); |
| return; |
| } |
| |
| const auto& info_req = request_payload.As<InformationRequestPayload>(); |
| const auto type = static_cast<InformationType>(letoh16(info_req.type)); |
| InformationResponder responder(sig_responder, type); |
| cb(type, &responder); |
| }; |
| |
| sig_->ServeRequest(kInformationRequest, std::move(on_info_req)); |
| } |
| |
| template <class ResponseT, typename CallbackT> |
| SignalingChannel::ResponseHandler BrEdrCommandHandler::BuildResponseHandler( |
| CallbackT rsp_cb) { |
| return [rsp_cb = std::move(rsp_cb)](Status status, |
| const ByteBuffer& rsp_payload) { |
| ResponseT rsp(status); |
| if (status == Status::kReject) { |
| if (!rsp.ParseReject(rsp_payload)) { |
| return false; |
| } |
| return rsp_cb(rsp); |
| } |
| |
| if (rsp_payload.size() < sizeof(typename ResponseT::PayloadT)) { |
| bt_log(TRACE, "l2cap-bredr", "cmd: ignoring malformed \"%s\", size %zu", |
| ResponseT::kName, rsp_payload.size()); |
| return false; |
| } |
| |
| rsp.Decode(rsp_payload); |
| return rsp_cb(rsp); |
| }; |
| } |
| |
| } // namespace internal |
| } // namespace l2cap |
| } // namespace bt |