| // Copyright 2017 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 "le_signaling_channel.h" |
| |
| #include "channel.h" |
| #include "logical_link.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| |
| namespace bt { |
| namespace l2cap { |
| namespace internal { |
| |
| LESignalingChannel::LESignalingChannel(fbl::RefPtr<Channel> chan, |
| hci::Connection::Role role) |
| : SignalingChannel(std::move(chan), role) { |
| set_mtu(kMinLEMTU); |
| } |
| |
| bool LESignalingChannel::SendRequest(CommandCode req_code, |
| const ByteBuffer& payload, |
| ResponseHandler cb) { |
| // TODO(NET-1093): Reuse BrEdrSignalingChannel's implementation. |
| bt_log(WARN, "l2cap-le", "sig: SendRequest not implemented yet"); |
| return false; |
| } |
| |
| void LESignalingChannel::ServeRequest(CommandCode req_code, |
| RequestDelegate cb) { |
| bt_log(WARN, "l2cap-le", "sig: ServeRequest not implemented yet"); |
| } |
| |
| void LESignalingChannel::OnConnParamUpdateReceived( |
| const SignalingPacket& packet) { |
| // Only a LE slave can send this command. "If an LE slave Host receives a |
| // Connection Parameter Update Request packet it shall respond with a Command |
| // Reject Packet [...]" (v5.0, Vol 3, Part A, Section 4.20). |
| if (role() == hci::Connection::Role::kSlave) { |
| bt_log(TRACE, "l2cap-le", |
| "sig: rejecting conn. param. update request from master"); |
| SendCommandReject(packet.header().id, RejectReason::kNotUnderstood, |
| BufferView()); |
| return; |
| } |
| |
| if (packet.payload_size() != |
| sizeof(ConnectionParameterUpdateRequestPayload)) { |
| bt_log(TRACE, "l2cap-le", "sig: malformed request received"); |
| SendCommandReject(packet.header().id, RejectReason::kNotUnderstood, |
| BufferView()); |
| return; |
| } |
| |
| const auto& payload = |
| packet.payload<ConnectionParameterUpdateRequestPayload>(); |
| |
| // Reject the connection parameters if they are outside the ranges allowed by |
| // the HCI specification (see HCI_LE_Connection_Update command - v5.0, Vol 2, |
| // Part E, Section 7.8.18). |
| bool reject = false; |
| hci::LEPreferredConnectionParameters params( |
| le16toh(payload.interval_min), le16toh(payload.interval_max), |
| le16toh(payload.slave_latency), le16toh(payload.timeout_multiplier)); |
| |
| if (params.min_interval() > params.max_interval()) { |
| bt_log(TRACE, "l2cap-le", "sig: conn. min interval larger than max"); |
| reject = true; |
| } else if (params.min_interval() < hci::kLEConnectionIntervalMin) { |
| bt_log(TRACE, "l2cap-le", |
| "sig: conn. min interval outside allowed range: %#.4x", |
| params.min_interval()); |
| reject = true; |
| } else if (params.max_interval() > hci::kLEConnectionIntervalMax) { |
| bt_log(TRACE, "l2cap-le", |
| "sig: conn. max interval outside allowed range: %#.4x", |
| params.max_interval()); |
| reject = true; |
| } else if (params.max_latency() > hci::kLEConnectionLatencyMax) { |
| bt_log(TRACE, "l2cap-le", "sig: conn. slave latency too large: %#.4x", |
| params.max_latency()); |
| reject = true; |
| } else if (params.supervision_timeout() < |
| hci::kLEConnectionSupervisionTimeoutMin || |
| params.supervision_timeout() > |
| hci::kLEConnectionSupervisionTimeoutMax) { |
| bt_log(TRACE, "l2cap-le", |
| "sig: conn supv. timeout outside allowed range: %#.4x", |
| params.supervision_timeout()); |
| reject = true; |
| } |
| |
| ConnectionParameterUpdateResult result = |
| reject ? ConnectionParameterUpdateResult::kRejected |
| : ConnectionParameterUpdateResult::kAccepted; |
| ConnectionParameterUpdateResponsePayload rsp; |
| rsp.result = static_cast<ConnectionParameterUpdateResult>(htole16(result)); |
| SendPacket(kConnectionParameterUpdateResponse, packet.header().id, |
| BufferView(&rsp, sizeof(rsp))); |
| |
| if (!reject && dispatcher_) { |
| async::PostTask(dispatcher_, [cb = conn_param_update_cb_.share(), |
| params = std::move(params)]() mutable { |
| cb(std::move(params)); |
| }); |
| } |
| } |
| |
| void LESignalingChannel::DecodeRxUnit(ByteBufferPtr sdu, |
| const SignalingPacketHandler& cb) { |
| // "[O]nly one command per C-frame shall be sent over [the LE] Fixed Channel" |
| // (v5.0, Vol 3, Part A, Section 4). |
| ZX_DEBUG_ASSERT(sdu); |
| if (sdu->size() < sizeof(CommandHeader)) { |
| bt_log(TRACE, "l2cap-le", "sig: dropped malformed LE signaling packet"); |
| return; |
| } |
| |
| SignalingPacket packet(sdu.get()); |
| uint16_t expected_payload_length = le16toh(packet.header().length); |
| if (expected_payload_length != sdu->size() - sizeof(CommandHeader)) { |
| bt_log(TRACE, "l2cap-le", |
| "sig: packet size mismatch (expected: %u, recv: %zu); drop", |
| expected_payload_length, sdu->size() - sizeof(CommandHeader)); |
| SendCommandReject(packet.header().id, RejectReason::kNotUnderstood, |
| BufferView()); |
| return; |
| } |
| |
| cb(SignalingPacket(sdu.get(), expected_payload_length)); |
| } |
| |
| bool LESignalingChannel::HandlePacket(const SignalingPacket& packet) { |
| switch (packet.header().code) { |
| case kConnectionParameterUpdateRequest: |
| OnConnParamUpdateReceived(packet); |
| return true; |
| default: |
| bt_log(TRACE, "l2cap-le", "sig: unsupported code %#.2x", |
| packet.header().code); |
| break; |
| } |
| |
| return false; |
| } |
| |
| } // namespace internal |
| } // namespace l2cap |
| } // namespace bt |