blob: 30590bc4be051e3ce0aca16df7c3d08b86524723 [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.
#pragma once
#include <cstddef>
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "rfcomm.h"
namespace btlib {
namespace rfcomm {
// Defined in GSM 5.4.6.3.*.
// clang-format off
enum class MuxCommandType : uint8_t {
kDLCParameterNegotiation = 0b10000000,
kTestCommand = 0b00100000,
kFlowControlOnCommand = 0b10100000,
kFlowControlOffCommand = 0b01100000,
kModemStatusCommand = 0b11100000,
kNonSupportedCommandResponse = 0b00010000,
kRemotePortNegotiationCommand = 0b10010000,
kRemoteLineStatusCommand = 0b01010000
};
// clang-format on
// A MuxCommand represents an RFCOMM multiplexer command which is ready to be
// sent or which has been parsed from a ByteBuffer. Specifically, each
// MuxCommand can be seen as a collection of fields and their accessors, plus a
// function Write which knows how to write these fields into a buffer
// in the format defined by the RFCOMM/GSM specs.
//
// MuxCommands are used in two ways:
// 1. Parsing multiplexer commands out of a received buffer and reading their
// fields
// 2. Constructing multiplexer commands and writing them into a buffer to send
//
// To use MuxCommands in the first way, use MuxCommand::Parse() on a received
// buffer. Check the type using command_type(), and cast the returned pointer
// to the appropriate subclass.
//
// TODO(gusss): this downcasting mechanism is non-ideal -- it is potentially
// very bug-prone. NET-1224 tracks the improvement of this mechanism.
//
// To use MuxCommands in the second way, construct the specific MuxCommand
// subtype which you are trying to send, allocate a buffer (using written_size()
// to calculate the needed size), and write into the buffer using Write().
//
// Supported commands are listed in RFCOMM 4.3.
class MuxCommand {
public:
// Parses a buffer and constructs the appropriate subclass of MuxCommand.
// Users of Parse should read the resulting command type and cast the pointer
// to the appropriate MuxCommand subclass.
static std::unique_ptr<MuxCommand> Parse(const common::ByteBuffer& buffer);
virtual ~MuxCommand() = default;
// Writes this MuxCommand into |buffer|. |buffer| must be at least the size
// indicated by written_size().
virtual void Write(common::MutableBufferView buffer) const = 0;
// The amount of space this command takes up when written. This is used when
// allocating space for an RFCOMM frame which will hold a multiplexer command.
virtual size_t written_size() const = 0;
inline MuxCommandType command_type() const { return command_type_; }
// The multiplexer-command-level C/R setting. This setting is described in GSM
// 5.4.6.2.
inline CommandResponse command_response() const { return command_response_; }
protected:
MuxCommand(MuxCommandType command_type, CommandResponse command_response);
// Forms the type field octet for this multiplexer command by ORing:
// - the EA bit (which is always 1 for GSM/RFCOMM -- see GSM 5.4.6.1)
// - the C/R bit (whose value is defined in 5.4.6.2), and
// - the type octet for the command in question (see 5.4.6.3.*).
inline uint8_t type_field_octet() const {
return 1 | ((command_response_ == CommandResponse::kCommand ? 1 : 0) << 1) |
(uint8_t)command_type_;
}
MuxCommandType command_type_;
CommandResponse command_response_;
};
// This command is used to test the connection between two peers. The command
// contains an attached test pattern, which the peer must repeat back in full.
// See GSM 5.4.6.3.4.
//
// Multiplexer frames can encode an arbitrary number of length octets. In our
// implementation, the length of the pattern contained within the test command
// is limited by the system's max value of size_t.
class TestCommand : public MuxCommand {
public:
TestCommand(CommandResponse command_response,
const common::ByteBuffer& test_pattern);
// Returns nullptr if parse fails. |command_response| and |length| are
// parameters which have already been parsed from |buffer|.
static std::unique_ptr<TestCommand> Parse(CommandResponse command_response,
size_t length,
const common::ByteBuffer& buffer);
inline common::BufferView test_pattern() const {
return test_pattern_.view();
}
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
private:
common::DynamicByteBuffer test_pattern_;
};
// This command is sent when our device is able to begin receiving messages. See
// GSM 5.4.6.3.5.
class FlowControlOnCommand : public MuxCommand {
public:
explicit FlowControlOnCommand(CommandResponse command_response);
// Returns nullptr if parse fails. |command_response| is a parameter which has
// already been parsed from |buffer|.
static std::unique_ptr<FlowControlOnCommand> Parse(
CommandResponse command_response);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
};
// This command is sent when our device is not able to receive messages. See
// GSM 5.4.6.3.6.
class FlowControlOffCommand : public MuxCommand {
public:
explicit FlowControlOffCommand(CommandResponse command_response);
// Returns nullptr if parse fails. |command_response| is a parameter which has
// already been parsed from |buffer|.
static std::unique_ptr<FlowControlOffCommand> Parse(
CommandResponse command_response);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
};
// These signals map to various V.24 signals as described in GSM tables 6 and 7.
struct ModemStatusCommandSignals {
bool flow_control;
bool ready_to_communicate;
bool ready_to_receive;
bool incoming_call;
bool data_valid;
};
using BreakValue = uint8_t;
constexpr BreakValue kMinBreakValue = 0b0000;
constexpr BreakValue kMaxBreakValue = 0b1111;
constexpr BreakValue kDefaultInvalidBreakValue = 0xFF;
// This command conveys the virtual V.24 signals. See GSM 5.4.6.3.7.
class ModemStatusCommand : public MuxCommand {
public:
ModemStatusCommand(CommandResponse command_response, DLCI dlci,
ModemStatusCommandSignals signals,
BreakValue break_value = kDefaultInvalidBreakValue);
// Returns nullptr if parse fails. |command_response| and |length| are
// parameters which have already been parsed from |buffer|.
static std::unique_ptr<ModemStatusCommand> Parse(
CommandResponse command_response, size_t length,
const common::ByteBuffer& buffer);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
inline DLCI dlci() const { return dlci_; }
inline ModemStatusCommandSignals signals() const { return signals_; }
// We only include a break signal if the break value is valid.
bool has_break_signal() const {
return break_value_ >= kMinBreakValue && break_value_ <= kMaxBreakValue;
}
inline BreakValue break_value() const { return break_value_; }
private:
DLCI dlci_;
ModemStatusCommandSignals signals_;
BreakValue break_value_;
};
// GSM table 12
// clang-format off
enum class Baud : uint8_t {
k2400 = 0,
k4800 = 1,
k7200 = 2,
k9600 = 3,
k19200 = 4,
k38400 = 5,
k57600 = 6,
k115200 = 7,
k230400 = 8
};
// clang-format on
constexpr Baud kDefaultBaud = Baud::k9600;
// GSM 5.4.6.3.9 (below table 12)
enum class DataBits : uint8_t {
k5Bits = 0,
k6Bits = 2,
k7Bits = 1,
k8Bits = 3
};
constexpr DataBits kDefaultDataBits = DataBits::k8Bits;
// GSM 5.4.6.3.9 (below table 12)
// clang-format off
enum class StopBits : bool {
k1Bit = 0,
k1AndHalfBits = 1
};
// clang-format on
constexpr StopBits kDefaultStopBits = StopBits::k1Bit;
// GSM 5.4.6.3.9 (below table 12)
// clang-format off
enum class ParityType : uint8_t {
kOdd = 0,
kMark = 1,
kEven = 2,
kSpace = 3
};
// clang-format on
// No default is defined in the spec, so we choose even arbitrarily.
constexpr ParityType kDefaultParityType = ParityType::kEven;
// Whether parity is on or off by default. This is not defined in the spec.
constexpr bool kDefaultParity = true;
// GSM 5.4.6.3.9 (below table 12)
// clang-format off
enum FlowControlFlags : uint8_t {
kXonXoffInputFlag = 1 << 0,
kXonXoffOutputFlag = 1 << 1,
kRTRInputFlag = 1 << 2,
kRTROutputFlag = 1 << 3,
kRTCInputFlag = 1 << 4,
kRTCOutputFlag = 1 << 5
};
// clang-format on
using FlowControlFlagsBitfield = uint8_t;
// GSM 5.4.6.3.9 (below table 12)
constexpr FlowControlFlagsBitfield kDefaultFlowControlFlags = 0;
struct RemotePortNegotiationParams {
Baud baud;
DataBits data_bits;
StopBits stop_bits;
bool parity;
ParityType parity_type;
FlowControlFlagsBitfield flow_control;
uint8_t xon_character;
uint8_t xoff_character;
};
// GSM 5.4.6.3.9 (below table 12)
constexpr uint8_t kDefaultXONCharacter = 0x01; // DC1
constexpr uint8_t kDefaultXOFFCharacter = 0x03; // DC3
constexpr RemotePortNegotiationParams kDefaultRemotePortNegotiationParams = {
kDefaultBaud, kDefaultDataBits, kDefaultStopBits,
kDefaultParity, kDefaultParityType, kDefaultFlowControlFlags,
kDefaultXONCharacter, kDefaultXOFFCharacter};
// GSM 5.4.6.3.9 (below table 12)
// clang-format off
enum RemotePortNegotiationMask : uint16_t {
kXonXoffInput = 1 << 0,
kXonXoffOutput = 1 << 1,
kRTRInput = 1 << 2,
kRTROutput = 1 << 3,
kRTCInput = 1 << 4,
kRTCOutput = 1 << 5,
kBitRate = 1 << 8,
kDataBits = 1 << 9,
kStopBits = 1 << 10,
kParity = 1 << 11,
kParityType = 1 << 12,
kXONCharacter = 1 << 13,
kXOFFCharacter = 1 << 14
};
// clang-format on
using RemotePortNegotiationMaskBitfield = uint16_t;
// This default is not defined in the spec.
constexpr RemotePortNegotiationMaskBitfield
kDefaultRemotePortNegotiationMaskBitfield = 0;
// This command is used to negotiate port settings such as baud rate. See GSM
// 5.4.6.3.9.
class RemotePortNegotiationCommand : public MuxCommand {
public:
// Creates an RPN command with one value octet. This type of RPN command is
// used to request the remote port settings.
RemotePortNegotiationCommand(CommandResponse command_response, DLCI dlci);
// Creates an RPN command with eight value octets. This type of RPN command is
// used to negotiate port settings.
RemotePortNegotiationCommand(CommandResponse command_response, DLCI dlci,
RemotePortNegotiationParams params,
RemotePortNegotiationMaskBitfield mask);
// Returns nullptr if parse fails. |command_response| and |length| are
// parameters which have already been parsed from |buffer|.
static std::unique_ptr<RemotePortNegotiationCommand> Parse(
CommandResponse command_response, size_t length,
const common::ByteBuffer& buffer);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
// The DLCI which this RPN command is negotiating over.
inline DLCI dlci() const { return dlci_; }
// The Remote Port Negotiation parameters. These are described in detaul in
// GSM 5.4.6.3.9.
inline RemotePortNegotiationParams params() const { return params_; }
// The mask used to indicate which parameters are being negotiated.
inline RemotePortNegotiationMaskBitfield mask() const { return mask_; }
private:
// Indicates whether this is the short (1 value octet) or long (8 value
// octets) version of the RPN command.
bool short_RPN_command_;
DLCI dlci_;
RemotePortNegotiationParams params_;
RemotePortNegotiationMaskBitfield mask_;
};
// GSM 5.4.6.3.10 (below table 15)
// clang-format off
enum class LineError : uint8_t {
kOverrunError = (1 << 0),
kParityError = (1 << 1),
kFramingError = (1 << 2)
};
// clang-format on
// This command is used to convey changes in the status of the line. See GSM
// 5.4.6.3.10.
class RemoteLineStatusCommand : public MuxCommand {
public:
RemoteLineStatusCommand(CommandResponse command_response, DLCI dlci,
bool error_occurred, LineError error);
// Returns nullptr if parse fails. |command_response| is a parameter which has
// already been parsed from |buffer|.
static std::unique_ptr<RemoteLineStatusCommand> Parse(
CommandResponse command_response, const common::ByteBuffer& buffer);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
// The DLCI which this command pertains to.
inline DLCI dlci() const { return dlci_; }
// Whether or not this frame contains an error.
inline bool error_occurred() const { return error_occurred_; }
// The type of error which occured. See GSM 5.4.6.3.10 for an explanation of
// the errors.
inline LineError error() const { return error_; }
private:
DLCI dlci_;
bool error_occurred_;
LineError error_;
};
// RFCOMM table 5.3.
enum class CreditBasedFlowHandshake : uint8_t {
kUnsupported = 0x0,
kSupportedRequest = 0xF,
kSupportedResponse = 0xE
};
using Priority = uint8_t;
constexpr Priority kMinPriority = 0;
constexpr Priority kMaxPriority = 63;
using InitialCredits = uint8_t;
constexpr InitialCredits kMinInitialCredits = 0;
constexpr InitialCredits kMaxInitialCredits = 7;
// See GSM 5.4.6.3.1 and the modifications presented in RFCOMM 5.5.3.
struct ParameterNegotiationParams {
// 6 bits wide.
DLCI dlci;
CreditBasedFlowHandshake credit_based_flow_handshake;
Priority priority;
uint16_t maximum_frame_size;
InitialCredits initial_credits;
};
// This command is used prior to opening a DLC. It is used to set up the
// parameters of the DLC. See GSM 5.4.6.3.1 and the modifications described in
// RFCOMM 5.5.3.
class DLCParameterNegotiationCommand : public MuxCommand {
public:
DLCParameterNegotiationCommand(CommandResponse command_response,
ParameterNegotiationParams params);
// Returns nullptr if parse fails. |command_response| is a parameter which has
// already been parsed from |buffer|.
static std::unique_ptr<DLCParameterNegotiationCommand> Parse(
CommandResponse command_response, const common::ByteBuffer& buffer);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
inline ParameterNegotiationParams params() const { return params_; }
private:
ParameterNegotiationParams params_;
};
// This response is sent when we receive a command which we do not recognize or
// support. See GSM 5.4.6.3.8.
class NonSupportedCommandResponse : public MuxCommand {
public:
// Note that NSC is always a response.
// |incoming_non_supported_command| is 6 bits.
NonSupportedCommandResponse(CommandResponse incoming_command_response,
uint8_t incoming_non_supported_command);
// Returns nullptr if parse fails. |command_response| is a parameter which has
// already been parsed from |buffer|.
static std::unique_ptr<NonSupportedCommandResponse> Parse(
CommandResponse command_response, const common::ByteBuffer& buffer);
// MuxCommand overrides
void Write(common::MutableBufferView buffer) const override;
size_t written_size() const override;
inline CommandResponse incoming_command_response() const {
return incoming_command_response_;
}
inline uint8_t incoming_non_supported_command() const {
return incoming_non_supported_command_;
}
private:
CommandResponse incoming_command_response_;
uint8_t incoming_non_supported_command_;
};
} // namespace rfcomm
} // namespace btlib