| // 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. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_BREDR_DYNAMIC_CHANNEL_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_BREDR_DYNAMIC_CHANNEL_H_ |
| |
| #include <lib/fit/function.h> |
| |
| #include <unordered_map> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/bredr_command_handler.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/dynamic_channel_registry.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/signaling_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h" |
| |
| namespace bt::l2cap::internal { |
| |
| // Implements factories for BR/EDR dynamic channels and dispatches incoming |
| // signaling channel requests to the corresponding channels by local ID. |
| // |
| // Must be run only on the L2CAP thread. |
| class BrEdrDynamicChannelRegistry final : public DynamicChannelRegistry { |
| public: |
| BrEdrDynamicChannelRegistry(SignalingChannelInterface* sig, DynamicChannelCallback close_cb, |
| ServiceRequestCallback service_request_cb, bool random_channel_ids); |
| ~BrEdrDynamicChannelRegistry() override = default; |
| |
| std::optional<ExtendedFeatures> extended_features() { return extended_features_; }; |
| |
| private: |
| // DynamicChannelRegistry override |
| DynamicChannelPtr MakeOutbound(PSM psm, ChannelId local_cid, ChannelParameters params) override; |
| DynamicChannelPtr MakeInbound(PSM psm, ChannelId local_cid, ChannelId remote_cid, |
| ChannelParameters params) override; |
| |
| // Signaling channel request handlers |
| void OnRxConnReq(PSM psm, ChannelId remote_cid, |
| BrEdrCommandHandler::ConnectionResponder* responder); |
| void OnRxConfigReq(ChannelId local_cid, uint16_t flags, ChannelConfiguration config, |
| BrEdrCommandHandler::ConfigurationResponder* responder); |
| void OnRxDisconReq(ChannelId local_cid, ChannelId remote_cid, |
| BrEdrCommandHandler::DisconnectionResponder* responder); |
| void OnRxInfoReq(InformationType type, BrEdrCommandHandler::InformationResponder* responder); |
| |
| // Signaling channel response handlers |
| void OnRxExtendedFeaturesInfoRsp(const BrEdrCommandHandler::InformationResponse& rsp); |
| |
| // Send extended features information request. |
| // TODO(fxbug.dev/929): Send fixed channels information request. |
| void SendInformationRequests(); |
| |
| // If an extended features information response has been received, returns the value of the ERTM |
| // bit in the peer's feature mask. |
| std::optional<bool> PeerSupportsERTM() const; |
| |
| using State = uint8_t; |
| enum StateBit : State { |
| // Extended Features Information Request (transmitted from local to remote) |
| kExtendedFeaturesSent = (1 << 0), |
| |
| // Extended Features Information Response (transmitted from remote to local) |
| kExtendedFeaturesReceived = (1 << 1), |
| }; |
| |
| // Bit field assembled using the bit masks above. |
| State state_; |
| |
| SignalingChannelInterface* const sig_; |
| |
| std::optional<ExtendedFeatures> extended_features_; |
| }; |
| |
| class BrEdrDynamicChannel; |
| using BrEdrDynamicChannelPtr = std::unique_ptr<BrEdrDynamicChannel>; |
| |
| // Creates, configures, and tears down dynamic channels using the BR/EDR |
| // signaling channel. The lifetime of this object matches that of the channel |
| // itself: created in order to start an outbound channel or in response to an |
| // inbound channel request, then destroyed immediately after the channel is |
| // closed. This is intended to be created and owned by |
| // BrEdrDynamicChannelRegistry. |
| // |
| // This implements the state machine described by v5.0 Vol 3 Part A Sec 6. The |
| // state of OPEN ("user data transfer state") matches the implementation of the |
| // |DynamicChannel::IsOpen()|. |
| // |
| // Channel Configuration design: |
| // Implementation-defined behavior: |
| // * Inbound and outbound configuration requests/responses are exchanged simultaneously |
| // * If the desired channel mode is ERTM, the configuration request is not sent until the extended |
| // features mask is received from the peer. |
| // * If the peer doesn't support ERTM, the local device will negotiate Basic Mode instead of ERTM |
| // * after both configuration requests have been accepted, if they are inconsistent, the channel |
| // is disconnected (this can happen if the peer doesn't follow the spec) |
| // Configuration Request Handling: |
| // * when the peer requests an MTU below the minumum, send an Unacceptable Parameters response |
| // suggesting the minimum MTU. |
| // * allow peer to send a maximum of 2 configuration requests with undesired channel modes |
| // before disconnecting |
| // * reject all channel modes other than Basic Mode and ERTM |
| // Negative Configuration Response Handling: |
| // A maximum of 2 negotiation attempts will be made before disconnecting, according to the |
| // following rules: |
| // * if the response does not contain the Retransmission & Flow Control option, disconnect |
| // * when the response specifies a different channel mode than the peer sent in a configuration |
| // request, disconnect |
| // * when the response rejected Basic Mode, disconnect |
| // * otherwise, send a second configuration request with Basic Mode |
| // |
| // Must be run only on the L2CAP thread. |
| class BrEdrDynamicChannel final : public DynamicChannel { |
| public: |
| using ResponseHandlerAction = SignalingChannel::ResponseHandlerAction; |
| |
| static BrEdrDynamicChannelPtr MakeOutbound(DynamicChannelRegistry* registry, |
| SignalingChannelInterface* signaling_channel, PSM psm, |
| ChannelId local_cid, ChannelParameters params, |
| std::optional<bool> peer_supports_ertm); |
| |
| static BrEdrDynamicChannelPtr MakeInbound(DynamicChannelRegistry* registry, |
| SignalingChannelInterface* signaling_channel, PSM psm, |
| ChannelId local_cid, ChannelId remote_cid, |
| ChannelParameters params, |
| std::optional<bool> peer_supports_ertm); |
| |
| // DynamicChannel overrides |
| ~BrEdrDynamicChannel() override = default; |
| |
| void Open(fit::closure open_cb) override; |
| |
| // Mark this channel as closed and disconnected. Send a Disconnection Request |
| // to the peer if possible (peer had sent an ID for its endpoint). |done_cb| |
| // will be called when Disconnection Response is received or if channel is |
| // already not connected. |
| void Disconnect(DisconnectDoneCallback done_cb) override; |
| |
| bool IsConnected() const override; |
| bool IsOpen() const override; |
| |
| // Must not be called until channel is open. |
| ChannelInfo info() const override; |
| |
| // Inbound request handlers. Request must have a destination channel ID that |
| // matches this instance's |local_cid|. |
| void OnRxConfigReq(uint16_t flags, ChannelConfiguration config, |
| BrEdrCommandHandler::ConfigurationResponder* responder); |
| void OnRxDisconReq(BrEdrCommandHandler::DisconnectionResponder* responder); |
| |
| // Called when the peer indicates whether it supports Enhanced Retransmission Mode. |
| // Kicks off the configuration process if the preferred channel mode is ERTM. |
| void SetEnhancedRetransmissionSupport(bool supported); |
| |
| // Reply with affirmative connection response and begin configuration. |
| void CompleteInboundConnection(BrEdrCommandHandler::ConnectionResponder* responder); |
| |
| // Contains options configured by remote configuration requests (Core Spec v5.1, Vol 3, Part A, |
| // Sections 5 and 7.1.1). |
| const ChannelConfiguration& remote_config() const { return remote_config_; } |
| |
| // Contains options configured by local configuration requests (Core Spec v5.1, Vol 3, Part A, |
| // Sections 5 and 7.1.2). |
| const ChannelConfiguration& local_config() const { return local_config_; } |
| |
| private: |
| // The channel configuration state is described in v5.0 Vol 3 Part A Sec 6 (in |
| // particular in Fig. 6.2) as having numerous substates in order to capture |
| // the different orders in which configuration packets may be transmitted. It |
| // is implemented here as a bitfield where bits are set as each packet is |
| // transmitted. |
| // |
| // The initial state for a channel prior to any signaling packets transmitted |
| // is 0. |
| using State = uint8_t; |
| enum StateBit : State { |
| // Connection Req (transmitted in either direction) |
| kConnRequested = (1 << 0), |
| |
| // Connection Rsp (transmitted in opposite direction of Connection Req) |
| kConnResponded = (1 << 1), |
| |
| // Configuration Req (transmitted from local to remote) |
| kLocalConfigSent = (1 << 2), |
| |
| // Configuration Rsp (successful; transmitted from remote to local) |
| kLocalConfigAccepted = (1 << 3), |
| |
| // Configuration Req (transmitted from remote to local) |
| kRemoteConfigReceived = (1 << 4), |
| |
| // Configuration Rsp (successful; transmitted from local to remote) |
| kRemoteConfigAccepted = (1 << 5), |
| |
| // Disconnection Req (transmitted in either direction) |
| kDisconnected = (1 << 6), |
| }; |
| |
| // TODO(fxbug.dev/996): Add Extended Flow Specification steps (exchange & |
| // controller configuration) |
| |
| BrEdrDynamicChannel(DynamicChannelRegistry* registry, |
| SignalingChannelInterface* signaling_channel, PSM psm, ChannelId local_cid, |
| ChannelId remote_cid, ChannelParameters params, |
| std::optional<bool> peer_supports_ertm); |
| |
| // Deliver the result of channel connection and configuration to the |Open| |
| // originator. Can be called multiple times but only the first invocation |
| // passes the result. |
| void PassOpenResult(); |
| |
| // Error during channel connection or configuration (before it is open). |
| // Deliver the error open result to the |Open| originator. Disconnect the |
| // remote endpoint of the channel if possible. If the channel is already open, |
| // use |Disconnect| instead. |
| void PassOpenError(); |
| |
| // Overwrites the local configuration's MTU and Retransmission & Flow Control options using |
| // ChannelParameters provided by the local service and the peer's support for Enhanced |
| // Retransmission Mode (set using SetEnhancedRetransmissionSupport). |
| void UpdateLocalConfigForErtm(); |
| |
| // Returns the maximum outbound SDU size based on service preference provided in |
| // ChannelParameters, specification limits, and ERTM transmission constraints. |
| uint16_t CalculateLocalMtu() const; |
| |
| // Returns true if the local service prefers ERTM and the peer has indicated support for ERTM. |
| bool ShouldRequestEnhancedRetransmission() const; |
| |
| // Returns true if the preferred channel parameters require waiting for an extended features |
| // information response and the response has not yet been received. Must be false before sending |
| // local config. |
| bool IsWaitingForPeerErtmSupport(); |
| |
| // Begin the local channel Configuration Request flow if it has not yet |
| // happened. The channel must not be waiting for the extended features info response. |
| void TrySendLocalConfig(); |
| |
| // Send local configuration request. |
| void SendLocalConfig(); |
| |
| // Returns true if both the remote and local configs have been accepted. |
| bool BothConfigsAccepted() const; |
| |
| // Returns true if negotiated channel modes are consistent. Must not be called until after both |
| // configs have been accepted (|BothConfigsAccepted()| is true). |
| [[nodiscard]] bool AcceptedChannelModesAreConsistent() const; |
| |
| // Checks options in a configuration request for unacceptable MTU and Retransmission and Flow |
| // Control options. Returns a configuration object where for each unacceptable option, there |
| // is a corresponding option with a value that would have been accepted if sent in the |
| // original request. |
| [[nodiscard]] ChannelConfiguration CheckForUnacceptableConfigReqOptions( |
| const ChannelConfiguration& config) const; |
| |
| // Checks parameters for Enhanced Retransmission Mode in the Retransmission & Flow Control and |
| // if there are any unacceptable values, returns an option containing values that would have been |
| // accepted. Otherwise, returns std::nullopt. |
| [[nodiscard]] std::optional<ChannelConfiguration::RetransmissionAndFlowControlOption> |
| CheckForUnacceptableErtmOptions(const ChannelConfiguration& config) const; |
| |
| // Try to recover from a configuration response with the "Unacceptable Parameters" result. |
| // Returns true if the negative reponse could be recovered from, and false otherwise (in which |
| // case an error should be reported). |
| [[nodiscard]] bool TryRecoverFromUnacceptableParametersConfigRsp( |
| const ChannelConfiguration& config); |
| |
| // Response handlers for outbound requests |
| ResponseHandlerAction OnRxConnRsp(const BrEdrCommandHandler::ConnectionResponse& rsp); |
| ResponseHandlerAction OnRxConfigRsp(const BrEdrCommandHandler::ConfigurationResponse& rsp); |
| |
| SignalingChannelInterface* const signaling_channel_; |
| |
| // Preferred parameters from the local service. |
| const ChannelParameters parameters_; |
| |
| // Bit field assembled using the bit masks above. When zero, it represents a |
| // closed (i.e. not yet open) channel. |
| State state_; |
| |
| // This shall be reset to nullptr after invocation to enforce its single-use |
| // semantics. See |DynamicChannel::Open| for details. |
| fit::closure open_result_cb_; |
| |
| // Support for ERTM is indicated in the peer's extended features mask, received in the extended |
| // features information response. Since the response may not yet have been received when this |
| // channel is created, this value may be assigned in either the constructor or in the |
| // |SetEnhancedRetransmissionSupport| callback. This will be set to false if the peer rejects ERTM |
| // during negotiation and set to true if the peer requests ERTM before responding to the extended |
| // features information request. |
| std::optional<bool> peer_supports_ertm_; |
| |
| // Contains options configured by remote configuration requests (Core Spec v5.1, Vol 3, Part A, |
| // Sections 5 and 7.1.1). |
| ChannelConfiguration remote_config_; |
| |
| // Each peer configuration request may be split into multiple requests with the continuation flag |
| // (C) set to 1. This variable accumulates the options received in each request until C is 0. This |
| // is not simply done with |remote_config_| because the previous value of |remote_config_| may be |
| // accessed during this process, or the final request may be rejected. This variable is nullopt |
| // when no sequence of continuation requests is being processed. |
| std::optional<ChannelConfiguration> remote_config_accum_; |
| |
| // Contains options configured by local configuration requests (Core Spec v5.1, Vol 3, Part A, |
| // Sections 5 and 7.1.2). |
| ChannelConfiguration local_config_; |
| |
| fxl::WeakPtrFactory<BrEdrDynamicChannel> weak_ptr_factory_; |
| }; |
| |
| } // namespace bt::l2cap::internal |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_L2CAP_BREDR_DYNAMIC_CHANNEL_H_ |