Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 501e52fe authored by Henri Chataing's avatar Henri Chataing Committed by Gerrit Code Review
Browse files

Merge "RootCanal: Implement LMP feature validation"

parents 47bf64cd 86b34156
Loading
Loading
Loading
Loading
+325 −14
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include "controller_properties.h"

#include <inttypes.h>
#include <json/json.h>

#include <fstream>
@@ -86,21 +87,10 @@ static constexpr uint64_t Page0LmpFeatures() {
  return value;
}

static constexpr uint64_t Page1LmpFeatures() {
  LMPFeaturesPage1Bits features[] = {
      LMPFeaturesPage1Bits::SIMULTANEOUS_LE_AND_BR_HOST,
  };

  uint64_t value = 0;
  for (auto feature : features) {
    value |= static_cast<uint64_t>(feature);
  }
  return value;
}

static constexpr uint64_t Page2LmpFeatures() {
  LMPFeaturesPage2Bits features[] = {
      LMPFeaturesPage2Bits::SECURE_CONNECTIONS_CONTROLLER_SUPPORT,
      LMPFeaturesPage2Bits::PING,
  };

  uint64_t value = 0;
@@ -236,8 +226,7 @@ static void ParseHex64(Json::Value value, uint64_t* field) {
}

ControllerProperties::ControllerProperties(const std::string& file_name)
    : lmp_features(
          {Page0LmpFeatures(), Page1LmpFeatures(), Page2LmpFeatures()}),
    : lmp_features({Page0LmpFeatures(), 0, Page2LmpFeatures()}),
      le_features(LlFeatures()) {
  // Set support for all HCI commands by default.
  // The controller will update the mask with its implemented commands
@@ -251,6 +240,13 @@ ControllerProperties::ControllerProperties(const std::string& file_name)
    supported_commands[i] = 0x00;
  }

  if (!CheckSupportedFeatures()) {
    LOG_INFO(
        "Warning: initial LMP and/or LE are not consistent. Please make sure"
        " that the features are correct w.r.t. the rules described"
        " in Vol 2, Part C 3.5 Feature requirements");
  }

  if (file_name.empty()) {
    return;
  }
@@ -329,6 +325,15 @@ ControllerProperties::ControllerProperties(const std::string& file_name)

  this->hci_version = static_cast<HciVersion>(hci_version);
  this->lmp_version = static_cast<LmpVersion>(lmp_version);

  if (!CheckSupportedFeatures()) {
    LOG_INFO(
        "Warning: the LMP and/or LE are not consistent. Please make sure"
        " that the features are correct w.r.t. the rules described"
        " in Vol 2, Part C 3.5 Feature requirements");
  } else {
    LOG_INFO("LMP and LE features successfully validated");
  }
}

void ControllerProperties::SetSupportedCommands(
@@ -338,4 +343,310 @@ void ControllerProperties::SetSupportedCommands(
  }
}

bool ControllerProperties::CheckSupportedFeatures() const {
  // Vol 2, Part C § 3.3 Feature mask definition.
  // Check for reserved or deprecated feature bits.
  //
  // Note: the specification for v1.0 and v1.1 is no longer available for
  // download, the reserved feature bits are copied over from v1.2.
  uint64_t lmp_page_0_reserved_bits = 0;
  uint64_t lmp_page_2_reserved_bits = 0;
  switch (lmp_version) {
    case bluetooth::hci::LmpVersion::V_1_0B:
      lmp_page_0_reserved_bits = UINT64_C(0x7fffe7e407000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_1_1:
      lmp_page_0_reserved_bits = UINT64_C(0x7fffe7e407000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_1_2:
      lmp_page_0_reserved_bits = UINT64_C(0x7fffe7e407000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_2_0:
      lmp_page_0_reserved_bits = UINT64_C(0x7fff066401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_2_1:
      lmp_page_0_reserved_bits = UINT64_C(0x7c86006401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_3_0:
      lmp_page_0_reserved_bits = UINT64_C(0x7886006401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_4_0:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xffffffffffffffff);
      break;
    case bluetooth::hci::LmpVersion::V_4_1:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff480);
      break;
    case bluetooth::hci::LmpVersion::V_4_2:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000000);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff480);
      break;
    case bluetooth::hci::LmpVersion::V_5_0:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000100);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff480);
      break;
    case bluetooth::hci::LmpVersion::V_5_1:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000100);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff080);
      break;
    case bluetooth::hci::LmpVersion::V_5_2:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000100);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff080);
      break;
    case bluetooth::hci::LmpVersion::V_5_3:
    default:
      lmp_page_0_reserved_bits = UINT64_C(0x7884000401000100);
      lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff080);
      break;
  };

  if ((lmp_page_0_reserved_bits & lmp_features[0]) != 0) {
    LOG_INFO("The page 0 feature bits 0x%016" PRIx64
             " are reserved in the specification %s",
             lmp_page_0_reserved_bits & lmp_features[0],
             LmpVersionText(lmp_version).c_str());
    return false;
  }

  if ((lmp_page_2_reserved_bits & lmp_features[2]) != 0) {
    LOG_INFO("The page 2 feature bits 0x%016" PRIx64
             " are reserved in the specification %s",
             lmp_page_2_reserved_bits & lmp_features[2],
             LmpVersionText(lmp_version).c_str());
    return false;
  }

  // Vol 2, Part C § 3.5 Feature requirements.
  // RootCanal always support BR/EDR mode, this function implements
  // the feature requirements from the subsection 1. Devices supporting BR/EDR.
  //
  // Note: the feature requirements were introduced in version v5.1 of the
  // specification, for previous versions it is assumed that the same
  // requirements apply for the subset of defined feature bits.

  // The features listed in Table 3.5 are mandatory in this version of the
  // specification (see Section 3.1) and these feature bits shall be set.
  if (!SupportsLMPFeature(LMPFeaturesPage0Bits::ENCRYPTION) ||
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::SECURE_SIMPLE_PAIRING_CONTROLLER) ||
      !SupportsLMPFeature(LMPFeaturesPage0Bits::ENCAPSULATED_PDU)) {
    LOG_INFO("Table 3.5 validation failed");
    return false;
  }

  // The features listed in Table 3.6 are forbidden in this version of the
  // specification and these feature bits shall not be set.
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::BR_EDR_NOT_SUPPORTED)) {
    LOG_INFO("Table 3.6 validation failed");
    return false;
  }

  // For each row of Table 3.7, either every feature named in that row shall be
  // supported or none of the features named in that row shall be supported.
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::SNIFF_MODE) !=
      SupportsLMPFeature(LMPFeaturesPage0Bits::SNIFF_SUBRATING)) {
    LOG_INFO("Table 3.7 validation failed");
    return false;
  }

  // For each row of Table 3.8, not more than one feature in that row shall be
  // supported.
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::BROADCAST_ENCRYPTION) &&
      SupportsLMPFeature(LMPFeaturesPage2Bits::COARSE_CLOCK_ADJUSTMENT)) {
    LOG_INFO("Table 3.8 validation failed");
    return false;
  }

  // For each row of Table 3.9, if the feature named in the first column is
  // supported then the feature named in the second column shall be supported.
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::ROLE_SWITCH) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SLOT_OFFSET)) {
    LOG_INFO("Table 3.9 validation failed; expected Slot Offset");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::HV2_PACKETS) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK)) {
    LOG_INFO("Table 3.9 validation failed; expected Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::HV3_PACKETS) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK)) {
    LOG_INFO("Table 3.9 validation failed; expected Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::M_LAW_LOG_SYNCHRONOUS_DATA) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Sco Link or Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::A_LAW_LOG_SYNCHRONOUS_DATA) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Sco Link or Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::CVSD_SYNCHRONOUS_DATA) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Sco Link or Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::TRANSPARENT_SYNCHRONOUS_DATA) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Sco Link or Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_3_MB_S_MODE) &&
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_2_MB_S_MODE)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Enhanced Data Rate ACL 2Mb/s "
        "mode");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::EV4_PACKETS) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO("Table 3.9 validation failed; expected Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::EV5_PACKETS) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO("Table 3.9 validation failed; expected Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CLASSIFICATION_PERIPHERAL) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CAPABLE_PERIPHERAL)) {
    LOG_INFO("Table 3.9 validation failed; expected AFH Capable Peripheral");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::LMP_3_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS) &&
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_2_MB_S_MODE)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Enhanced Data Rate ACL 2Mb/s "
        "mode");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::LMP_5_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS) &&
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_2_MB_S_MODE)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Enhanced Data Rate ACL 2Mb/s "
        "mode");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CLASSIFICATION_CENTRAL) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CAPABLE_CENTRAL)) {
    LOG_INFO("Table 3.9 validation failed; expected AFH Capable Central");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO("Table 3.9 validation failed; expected Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_3_MB_S_MODE) &&
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Enhanced Data Rate eSCO 2Mb/s "
        "mode");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::LMP_3_SLOT_ENHANCED_DATA_RATE_ESCO_PACKETS) &&
      !SupportsLMPFeature(
          LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Enhanced Data Rate eSCO 2Mb/s "
        "mode");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_INQUIRY_RESPONSE) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::RSSI_WITH_INQUIRY_RESULTS)) {
    LOG_INFO("Table 3.9 validation failed; expected RSSI with Inquiry Results");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage0Bits::SIMULTANEOUS_LE_AND_BR_CONTROLLER) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::LE_SUPPORTED_CONTROLLER)) {
    LOG_INFO("Table 3.9 validation failed; expected LE Supported (Controller)");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::ERRONEOUS_DATA_REPORTING) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::SCO_LINK) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::EXTENDED_SCO_LINK)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Sco Link or Extended Sco Link");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage0Bits::ENHANCED_POWER_CONTROL) &&
      (!SupportsLMPFeature(LMPFeaturesPage0Bits::POWER_CONTROL_REQUESTS) ||
       !SupportsLMPFeature(LMPFeaturesPage0Bits::POWER_CONTROL))) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Power Control Request and Power "
        "Control");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage2Bits::
              CONNECTIONLESS_PERIPHERAL_BROADCAST_TRANSMITTER_OPERATION) &&
      !SupportsLMPFeature(LMPFeaturesPage2Bits::SYNCHRONIZATION_TRAIN)) {
    LOG_INFO("Table 3.9 validation failed; expected Synchronization Train");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage2Bits::
              CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVER_OPERATION) &&
      !SupportsLMPFeature(LMPFeaturesPage2Bits::SYNCHRONIZATION_SCAN)) {
    LOG_INFO("Table 3.9 validation failed; expected Synchronization Scan");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage2Bits::GENERALIZED_INTERLACED_SCAN) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::INTERLACED_INQUIRY_SCAN) &&
      !SupportsLMPFeature(LMPFeaturesPage0Bits::INTERLACED_PAGE_SCAN)) {
    LOG_INFO(
        "Table 3.9 validation failed; expected Interlaced Inquiry Scan or "
        "Interlaced Page Scan");
    return false;
  }
  if (SupportsLMPFeature(LMPFeaturesPage2Bits::COARSE_CLOCK_ADJUSTMENT) &&
      (!SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CAPABLE_PERIPHERAL) ||
       !SupportsLMPFeature(LMPFeaturesPage0Bits::AFH_CAPABLE_CENTRAL) ||
       !SupportsLMPFeature(LMPFeaturesPage2Bits::SYNCHRONIZATION_TRAIN) ||
       !SupportsLMPFeature(LMPFeaturesPage2Bits::SYNCHRONIZATION_SCAN))) {
    LOG_INFO(
        "Table 3.9 validation failed; expected AFH Capable Central/Peripheral "
        "and Synchronization Train/Scan");
    return false;
  }
  if (SupportsLMPFeature(
          LMPFeaturesPage2Bits::SECURE_CONNECTIONS_CONTROLLER_SUPPORT) &&
      (!SupportsLMPFeature(LMPFeaturesPage0Bits::PAUSE_ENCRYPTION) ||
       !SupportsLMPFeature(LMPFeaturesPage2Bits::PING))) {
    LOG_INFO("Table 3.9 validation failed; expected Pause Encryption and Ping");
    return false;
  }

  return true;
}

}  // namespace rootcanal
+11 −0
Original line number Diff line number Diff line
@@ -47,6 +47,9 @@ struct ControllerProperties {
  // file or all 1s.
  void SetSupportedCommands(std::array<uint8_t, 64> supported_commands);

  // Check if the feature masks are valid according to the specification.
  bool CheckSupportedFeatures() const;

  // Local Version Information (Vol 4, Part E § 7.4.1).
  HciVersion hci_version{HciVersion::V_5_3};
  LmpVersion lmp_version{LmpVersion::V_5_3};
@@ -108,6 +111,14 @@ struct ControllerProperties {
  // Vendor Information.
  // Provide parameters returned by vendor specific commands.
  std::vector<uint8_t> le_vendor_capabilities{};

  bool SupportsLMPFeature(bluetooth::hci::LMPFeaturesPage0Bits bit) const {
    return (lmp_features[0] & static_cast<uint64_t>(bit)) != 0;
  }

  bool SupportsLMPFeature(bluetooth::hci::LMPFeaturesPage2Bits bit) const {
    return (lmp_features[2] & static_cast<uint64_t>(bit)) != 0;
  }
};

}  // namespace rootcanal
+7 −0
Original line number Diff line number Diff line
@@ -1274,6 +1274,9 @@ void LinkLayerController::SetSecureSimplePairingSupport(bool enable) {
}

void LinkLayerController::SetLeHostSupport(bool enable) {
  // TODO: Vol 2, Part C § 3.5 Feature requirements.
  // (65) LE Supported (Host)             implies
  //    (38) LE Supported (Controller)
  uint64_t bit = 0x2;
  le_host_support_ = enable;
  if (enable) {
@@ -1284,6 +1287,10 @@ void LinkLayerController::SetLeHostSupport(bool enable) {
}

void LinkLayerController::SetSecureConnectionsSupport(bool enable) {
  // TODO: Vol 2, Part C § 3.5 Feature requirements.
  // (67) Secure Connections (Host Support)           implies
  //    (64) Secure Simple Pairing (Host Support)     and
  //    (136) Secure Connections (Controller Support)
  uint64_t bit = 0x8;
  secure_connections_host_support_ = enable;
  if (enable) {