// 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 "frames.h"

#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/slab_allocator.h"

#include "rfcomm.h"

namespace bt {
namespace rfcomm {

namespace {

// FCS utilites. These come from GSM Annex B.
// CRC table for calculating FCS. GSM B.3.5.
const uint8_t crctable[] = {
    0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C,
    0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
    0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 0x38, 0xA9, 0xDB, 0x4A,
    0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
    0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58,
    0x2D, 0xBC, 0xCE, 0x5F, 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
    0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 0x6C, 0xFD, 0x8F, 0x1E,
    0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
    0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34,
    0x41, 0xD0, 0xA2, 0x33, 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
    0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 0xE0, 0x71, 0x03, 0x92,
    0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
    0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80,
    0xF5, 0x64, 0x16, 0x87, 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
    0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 0xC4, 0x55, 0x27, 0xB6,
    0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
    0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC,
    0x99, 0x08, 0x7A, 0xEB, 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
    0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 0xA8, 0x39, 0x4B, 0xDA,
    0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
    0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8,
    0xBD, 0x2C, 0x5E, 0xCF};

// FCS calculation function from GSM B.3.3.
uint8_t CalculateFCS(const common::ByteBuffer& p) {
  uint8_t fcs = 0xFF;
  size_t offset = 0;
  size_t len = p.size();
  while (len--) {
    fcs = crctable[fcs ^ p[offset++]];
  }
  /* Ones compliment */
  return (0xFF - fcs);
}

// FCS checking function from GSM B.3.4.
bool CheckFCS(uint8_t received_fcs, const common::ByteBuffer& p) {
  uint8_t fcs = 0xFF;
  size_t offset = 0;
  size_t len = p.size();
  while (len--) {
    fcs = crctable[fcs ^ p[offset++]];
  }
  fcs = crctable[fcs ^ received_fcs];
  return fcs == 0xCF;
}

constexpr uint8_t kControlMask = 0b11101111;
constexpr size_t kCRShift = 1;
constexpr size_t kPFShift = 4;
constexpr uint8_t kCRMask = 1 << kCRShift;
constexpr uint8_t kPFMask = 1 << kPFShift;
constexpr uint8_t kEAMask = 0b00000001;
constexpr size_t kAddressIndex = 0;
constexpr size_t kControlIndex = 1;
constexpr size_t kLengthIndex = 2;
constexpr size_t kLengthFirstOctetShift = 1;
constexpr size_t kLengthSecondOctetShift = 7;
constexpr size_t kDLCIShift = 2;
// Address field, control field, length field, FCS field. See RFCOMM 5.2.6.
constexpr size_t kMinimumFrameSize = 4;

// The length field in RFCOMM frames and multiplexer control commands can be
// extended from one octet to two. This function checks whether two octets are
// needed.
bool TwoOctetLength(size_t length) { return length > kMaxSingleOctetLength; }

// Logic for determining whether a frame will have a credit octet based on
// characteristics of the frame and of the Session forming the frame. See RFCOMM
// 6.5.2.
//
// |credit_based_flow| indicates whether credit-based flow control is turned on
// in this RFCOMM session.
inline bool FrameHasCreditOctet(bool credit_based_flow, FrameType frame_type,
                                bool poll_final) {
  return credit_based_flow &&
         (frame_type == FrameType::kUnnumberedInfoHeaderCheck) &&
         poll_final == true;
}

}  // namespace

Frame::Frame(Role role, CommandResponse command_response, DLCI dlci,
             uint8_t control, bool poll_final)
    : role_(role),
      command_response_(command_response),
      dlci_(dlci),
      control_(control),
      poll_final_(poll_final) {}

// To parse an RFCOMM frame, we must parse all the way until the end to check
// the FCS octet. This means there wouldn't be much benefit in adding Parse
// functions for each Frame subclass, as Frame::Parse will already parse all of
// the needed information.
std::unique_ptr<Frame> Frame::Parse(bool credit_based_flow, Role role,
                                    const common::ByteBuffer& buffer) {
  if (buffer.size() < kMinimumFrameSize) {
    bt_log(TRACE, "rfcomm",
           "buffer size of %zu is less than minimum frame size (%zu)",
           buffer.size(), kMinimumFrameSize);
    return nullptr;
  }

  // Parse the frame type and DLCI first and then determine if we can parse the
  // frame. We can parse the frame if the multiplexer is started, or if the
  // multiplexer isn't started and it's a valid pre-mux-startup frame.
  DLCI dlci = buffer[kAddressIndex] >> kDLCIShift;
  FrameType frame_type = (FrameType)(buffer[kControlIndex] & kControlMask);

  if (!IsMultiplexerStarted(role) && !IsMuxStartupFrame(frame_type, dlci)) {
    bt_log(TRACE, "rfcomm", "frame type %u before mux start",
           static_cast<uint8_t>(frame_type));
    return nullptr;
  }

  bool cr_bit = buffer[kAddressIndex] & kCRMask;
  // See table 1 in 5.2.1.2, which describes exactly how the C/R bit is
  // interpreted.
  CommandResponse command_response;
  if (IsMultiplexerStarted(role)) {
    // See table 1 in 5.2.1.2, which describes exactly how the C/R bit is
    // interpreted.
    command_response = (role == Role::kInitiator && cr_bit) ||
                               (role == Role::kResponder && !cr_bit)
                           ? CommandResponse::kCommand
                           : CommandResponse::kResponse;
  } else {
    // This is not defined explicitly in the spec. If the multiplexer isn't
    // started, we assume the frame has the role the sender would take if
    // multiplexer startup succeeds.
    switch (frame_type) {
      case FrameType::kSetAsynchronousBalancedMode:
        command_response =
            cr_bit ? CommandResponse::kCommand : CommandResponse::kResponse;
        break;
      case FrameType::kDisconnectedMode:
      case FrameType::kUnnumberedAcknowledgement:
        command_response =
            cr_bit ? CommandResponse::kResponse : CommandResponse::kCommand;
        break;
      default:
        // TODO(armansito): Add a unit test for this case.
        bt_log(ERROR, "rfcomm", "malformed frame type: %u",
               static_cast<unsigned int>(frame_type));
        return nullptr;
    }
  }
  bool poll_final = buffer[kControlIndex] & kPFMask;

  InformationLength length = buffer[kLengthIndex] >> kLengthFirstOctetShift;
  bool two_octet_length = !(buffer[kLengthIndex] & kEAMask);
  if (two_octet_length) {
    if (buffer.size() <= kLengthIndex + 1) {
      bt_log(WARN, "rfcomm", "buffer ended unexpectedly while parsing length");
      return nullptr;
    }
    length |= buffer[kLengthIndex + 1] << kLengthSecondOctetShift;
  }

  uint8_t credits = 0;
  bool has_credit_octet =
      FrameHasCreditOctet(credit_based_flow, frame_type, poll_final);
  if (has_credit_octet) {
    size_t credit_index = kLengthIndex + (two_octet_length ? 2 : 1);
    if (buffer.size() <= credit_index) {
      bt_log(WARN, "rfcomm", "buffer ended unexpectedly while parsing credits");
      return nullptr;
    }
    credits = buffer[credit_index];
  }

  // Address and control, one or two length octets, plus a possible credits
  // octet.
  size_t header_size =
      2 + (two_octet_length ? 2 : 1) + (has_credit_octet ? 1 : 0);

  // Check the FCS before we do any allocation or copying.
  size_t fcs_index = header_size + length;
  if (buffer.size() <= fcs_index) {
    bt_log(WARN, "rfcomm", "buffer ended unexpectedly while parsing FCS");
    return nullptr;
  }
  uint8_t fcs = buffer[fcs_index];
  size_t num_octets =
      frame_type == FrameType::kUnnumberedInfoHeaderCheck ? 2 : 3;
  if (!CheckFCS(fcs, buffer.view(0, num_octets))) {
    bt_log(WARN, "rfcomm", "FCS check failed");
    return nullptr;
  }

  if (frame_type == FrameType::kUnnumberedInfoHeaderCheck) {
    // Determine whether to parse a MuxControlFrame or a UserDataFrame based on
    // the DLCI.
    if (dlci == kMuxControlDLCI) {
      std::unique_ptr<MuxCommand> mux_command =
          MuxCommand::Parse(buffer.view(header_size, length));
      if (!mux_command) {
        bt_log(WARN, "rfcomm", "unable to parse mux command");
        return nullptr;
      }
      auto mux_command_frame = std::make_unique<MuxCommandFrame>(
          role, credit_based_flow, std::move(mux_command));
      mux_command_frame->set_credits(credits);
      return mux_command_frame;
    } else if (IsUserDLCI(dlci)) {
      common::MutableByteBufferPtr information;
      if (length > 0) {
        information = common::NewSlabBuffer(length);
        buffer.Copy(information.get(), header_size, length);
      }
      auto user_data_frame = std::make_unique<UserDataFrame>(
          role, credit_based_flow, dlci, std::move(information));
      user_data_frame->set_credits(credits);
      return user_data_frame;
    } else {
      bt_log(WARN, "rfcomm",
             "Parsed DLCI %u is not a valid multiplexer or user data channel",
             dlci);
      return nullptr;
    }
  } else {
    return std::make_unique<Frame>(role, command_response, dlci,
                                   uint8_t(frame_type), poll_final);
  }
}

// Write this RFCOMM frame into a buffer.
void Frame::Write(common::MutableBufferView buffer) const {
  ZX_DEBUG_ASSERT(buffer.size() >= header_size());

  // Writes address, control, and length octets.
  WriteHeader(buffer);

  // Begin writing after header.
  size_t offset = header_size();
  ZX_DEBUG_ASSERT(buffer.size() > offset);

  // FCS is calculated on address and control fields for UIH frames, and
  // calculated on address, control, and length fields for all other frames.
  size_t num_octets =
      (FrameType)control_ == FrameType::kUnnumberedInfoHeaderCheck ? 2 : 3;
  uint8_t fcs = CalculateFCS(buffer.view(0, num_octets));
  buffer[offset] = fcs;
}

void Frame::WriteHeader(common::MutableBufferView buffer) const {
  ZX_DEBUG_ASSERT(buffer.size() >= header_size());

  size_t offset = 0;

  uint8_t address = dlci_ << kDLCIShift;
  // Set EA bit (DLCI/address is always 1 octet in length)
  address |= kEAMask;
  // Set C/R bit
  uint8_t command_response_bit;
  if (role_ == Role::kInitiator || role_ == Role::kResponder) {
    // GSM Table 1.
    command_response_bit = (role_ == Role::kInitiator &&
                            command_response_ == CommandResponse::kCommand) ||
                           (role_ == Role::kResponder &&
                            command_response_ == CommandResponse::kResponse);
  } else if (role_ == Role::kUnassigned || role_ == Role::kNegotiating) {
    // There are only specific frames which can be encoded when the multiplexer
    // is not yet started.
    ZX_DEBUG_ASSERT(IsMuxStartupFrame(FrameType(control_), dlci_));

    // TODO(gusss): the spec does not say how we encode the C/R bit when the
    // multiplexer is not yet started.
    // Set the C/R bit as if startup succeeds (see GSM table 1)
    //  - SABM (command) frames become initiator, C/R = 1
    //  - UA (response) frames become responder, C/R = 1
    //  - DM (response) frames come from would-be responder, C/R = 1
    command_response_bit = 1;
  } else {
    ZX_PANIC("unexpected role while writing frame: %u",
             static_cast<unsigned int>(role_));
    return;
  }

  address &= ~kCRMask;
  address |= command_response_bit << kCRShift;
  buffer[offset] = address;
  ++offset;

  uint8_t control = control_;
  control &= ~kPFMask;
  control |= (poll_final_ ? 1 : 0) << kPFShift;
  buffer[offset] = control;
  ++offset;

  uint8_t length_octet = 0;
  const InformationLength length_val = length();
  bool two_octet_length = TwoOctetLength(length_val);
  // Set E/A bit.
  length_octet |= two_octet_length ? 0 : 1;
  length_octet |= length_val << kLengthFirstOctetShift;
  buffer[offset] = length_octet;
  ++offset;
  if (two_octet_length) {
    length_octet = length_val >> kLengthSecondOctetShift;
    buffer[offset] = length_octet;
    ++offset;
  }
}

size_t Frame::header_size() const {
  // Address, control, and one or two length octets.
  return sizeof(uint8_t) + sizeof(uint8_t) +
         sizeof(uint8_t) * (TwoOctetLength(length()) ? 2 : 1);
}

MuxCommandFrame* Frame::AsMuxCommandFrame() {
  ZX_DEBUG_ASSERT(dlci() == 0);
  return static_cast<MuxCommandFrame*>(this);
}

UserDataFrame* Frame::AsUserDataFrame() {
  ZX_DEBUG_ASSERT(IsUserDLCI(dlci()));
  return static_cast<UserDataFrame*>(this);
}

UnnumberedInfoHeaderCheckFrame* Frame::AsUnnumberedInfoHeaderCheckFrame() {
  FrameType type = static_cast<FrameType>(control());
  ZX_DEBUG_ASSERT(type == FrameType::kUnnumberedInfoHeaderCheck);
  return static_cast<UnnumberedInfoHeaderCheckFrame*>(this);
}

DisconnectCommand::DisconnectCommand(Role role, DLCI dlci)
    : Frame(role,
            // DISC frames are always commands.
            CommandResponse::kCommand, dlci, uint8_t(FrameType::kDisconnect),
            // Poll bit is always 1. See GSM 5.4.4.1.
            true) {}

SetAsynchronousBalancedModeCommand::SetAsynchronousBalancedModeCommand(
    Role role, DLCI dlci)
    : Frame(role,
            // SABM frames are always commands.
            CommandResponse::kCommand, dlci,
            uint8_t(FrameType::kSetAsynchronousBalancedMode),
            // Poll bit is always 1. See GSM 5.4.4.1.
            true) {}

UnnumberedAcknowledgementResponse::UnnumberedAcknowledgementResponse(Role role,
                                                                     DLCI dlci)
    : Frame(role,
            // UA frames are always responses.
            CommandResponse::kResponse, dlci,
            uint8_t(FrameType::kUnnumberedAcknowledgement),
            // Final bit is always 1. See GSM 5.4.4.2.
            true) {}

DisconnectedModeResponse::DisconnectedModeResponse(Role role, DLCI dlci)
    : Frame(role,
            // DM frames are always responses.
            CommandResponse::kResponse, dlci,
            uint8_t(FrameType::kDisconnectedMode),
            // Our implementation will always set the final bit to 1. The final
            // bit in a DM response must be 1 when responding negatively to a
            // SABM command (GSM 5.4.1). In all other cases, the P/F bit setting
            // is unspecified and does not matter for DM frames. See, for
            // example, GSM 5.4.4.2: "If an unsolicited DM response is received
            // then the frame shall be processed irrespective of the P/F
            // setting."
            true) {}

UnnumberedInfoHeaderCheckFrame::UnnumberedInfoHeaderCheckFrame(
    Role role, bool credit_based_flow, DLCI dlci)
    : Frame(role,
            // UIH frames are always commands.
            CommandResponse::kCommand, dlci,
            uint8_t(FrameType::kUnnumberedInfoHeaderCheck),
            // P/F bit initially 0, as there should be no credits field (credits
            // initialized to 0).
            false),
      credit_based_flow_(credit_based_flow),
      // Initially no credits.
      credits_(0) {}

void UnnumberedInfoHeaderCheckFrame::set_credits(uint8_t credits) {
  // Can't set credits on a frame if credit-based flow is off.
  if (!credit_based_flow_)
    return;

  credits_ = credits;
  // P/F is used to indicate the presence of a credits octet. We turn on the
  // credits octet if there are credits.
  poll_final_ = credits != 0;
}

size_t UnnumberedInfoHeaderCheckFrame::header_size() const {
  return Frame::header_size()  // Address, control, length
         + sizeof(uint8_t) * (has_credit_octet() ? 1 : 0);  // Credits
}

void UnnumberedInfoHeaderCheckFrame::WriteHeader(
    common::MutableBufferView buffer) const {
  ZX_DEBUG_ASSERT(buffer.size() >= header_size());

  // Write address, control, length
  Frame::WriteHeader(buffer);

  // Write credit octet if it exists.
  if (has_credit_octet()) {
    size_t offset = Frame::header_size();
    buffer[offset] = credits_;
  }
}

UserDataFrame::UserDataFrame(Role role, bool credit_based_flow, DLCI dlci,
                             common::ByteBufferPtr information)
    : UnnumberedInfoHeaderCheckFrame(role, credit_based_flow, dlci),
      information_(std::move(information)) {}

void UserDataFrame::Write(common::MutableBufferView buffer) const {
  ZX_DEBUG_ASSERT(buffer.size() >= written_size());

  WriteHeader(buffer);
  size_t offset = header_size();

  if (information_) {
    buffer.Write(*information_, offset);
    offset += information_->size();
  }

  // FCS is always calculated over first two octets for UIH frames.
  buffer[offset] = CalculateFCS(buffer.view(0, 2));
}

size_t UserDataFrame::written_size() const {
  return UnnumberedInfoHeaderCheckFrame::header_size() +
         length()            // Information
         + sizeof(uint8_t);  // FCS
}

common::ByteBufferPtr UserDataFrame::TakeInformation() {
  return std::move(information_);
}

MuxCommandFrame::MuxCommandFrame(Role role, bool credit_based_flow,
                                 std::unique_ptr<MuxCommand> mux_command)
    : UnnumberedInfoHeaderCheckFrame(role, credit_based_flow, kMuxControlDLCI),
      mux_command_(std::move(mux_command)) {}

void MuxCommandFrame::Write(common::MutableBufferView buffer) const {
  ZX_DEBUG_ASSERT(buffer.size() >= written_size());

  WriteHeader(buffer);

  size_t offset = header_size();
  mux_command_->Write(buffer.mutable_view(offset));
  offset += mux_command_->written_size();

  // FCS is always calculated over first two octets for UIH frames.
  buffer[offset] = CalculateFCS(buffer.view(0, 2));
}

size_t MuxCommandFrame::written_size() const {
  return UnnumberedInfoHeaderCheckFrame::header_size() +
         +mux_command_->written_size()  // MuxCommand
         + sizeof(uint8_t);             // FCS
}

std::unique_ptr<MuxCommand> MuxCommandFrame::TakeMuxCommand() {
  return std::move(mux_command_);
}

}  // namespace rfcomm
}  // namespace bt
