blob: 8b96e53d705c494ca1c88b5c88f243f485db88d2 [file] [log] [blame]
// 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_