/* * Copyright 2015 The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "controller_properties.h" #include #include #include #include #include #include "log.h" namespace rootcanal { using namespace bluetooth::hci; static constexpr uint64_t Page0LmpFeatures() { LMPFeaturesPage0Bits features[] = { LMPFeaturesPage0Bits::LMP_3_SLOT_PACKETS, LMPFeaturesPage0Bits::LMP_5_SLOT_PACKETS, LMPFeaturesPage0Bits::ENCRYPTION, LMPFeaturesPage0Bits::SLOT_OFFSET, LMPFeaturesPage0Bits::TIMING_ACCURACY, LMPFeaturesPage0Bits::ROLE_SWITCH, LMPFeaturesPage0Bits::HOLD_MODE, LMPFeaturesPage0Bits::SNIFF_MODE, LMPFeaturesPage0Bits::POWER_CONTROL_REQUESTS, LMPFeaturesPage0Bits::CHANNEL_QUALITY_DRIVEN_DATA_RATE, LMPFeaturesPage0Bits::SCO_LINK, LMPFeaturesPage0Bits::HV2_PACKETS, LMPFeaturesPage0Bits::HV3_PACKETS, LMPFeaturesPage0Bits::M_LAW_LOG_SYNCHRONOUS_DATA, LMPFeaturesPage0Bits::A_LAW_LOG_SYNCHRONOUS_DATA, LMPFeaturesPage0Bits::CVSD_SYNCHRONOUS_DATA, LMPFeaturesPage0Bits::PAGING_PARAMETER_NEGOTIATION, LMPFeaturesPage0Bits::POWER_CONTROL, LMPFeaturesPage0Bits::TRANSPARENT_SYNCHRONOUS_DATA, LMPFeaturesPage0Bits::BROADCAST_ENCRYPTION, LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_2_MB_S_MODE, LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ACL_3_MB_S_MODE, LMPFeaturesPage0Bits::ENHANCED_INQUIRY_SCAN, LMPFeaturesPage0Bits::INTERLACED_INQUIRY_SCAN, LMPFeaturesPage0Bits::INTERLACED_PAGE_SCAN, LMPFeaturesPage0Bits::RSSI_WITH_INQUIRY_RESULTS, LMPFeaturesPage0Bits::EXTENDED_SCO_LINK, LMPFeaturesPage0Bits::EV4_PACKETS, LMPFeaturesPage0Bits::EV5_PACKETS, LMPFeaturesPage0Bits::AFH_CAPABLE_PERIPHERAL, LMPFeaturesPage0Bits::AFH_CLASSIFICATION_PERIPHERAL, LMPFeaturesPage0Bits::LE_SUPPORTED_CONTROLLER, LMPFeaturesPage0Bits::LMP_3_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS, LMPFeaturesPage0Bits::LMP_5_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS, LMPFeaturesPage0Bits::SNIFF_SUBRATING, LMPFeaturesPage0Bits::PAUSE_ENCRYPTION, LMPFeaturesPage0Bits::AFH_CAPABLE_CENTRAL, LMPFeaturesPage0Bits::AFH_CLASSIFICATION_CENTRAL, LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE, LMPFeaturesPage0Bits::ENHANCED_DATA_RATE_ESCO_3_MB_S_MODE, LMPFeaturesPage0Bits::LMP_3_SLOT_ENHANCED_DATA_RATE_ESCO_PACKETS, LMPFeaturesPage0Bits::EXTENDED_INQUIRY_RESPONSE, LMPFeaturesPage0Bits::SIMULTANEOUS_LE_AND_BR_CONTROLLER, LMPFeaturesPage0Bits::SECURE_SIMPLE_PAIRING_CONTROLLER, LMPFeaturesPage0Bits::ENCAPSULATED_PDU, LMPFeaturesPage0Bits::HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT, LMPFeaturesPage0Bits::VARIABLE_INQUIRY_TX_POWER_LEVEL, LMPFeaturesPage0Bits::ENHANCED_POWER_CONTROL, LMPFeaturesPage0Bits::EXTENDED_FEATURES}; uint64_t value = 0; for (auto feature : features) { value |= static_cast(feature); } return value; } static constexpr uint64_t Page2LmpFeatures() { LMPFeaturesPage2Bits features[] = { LMPFeaturesPage2Bits::SECURE_CONNECTIONS_CONTROLLER_SUPPORT, LMPFeaturesPage2Bits::PING, }; uint64_t value = 0; for (auto feature : features) { value |= static_cast(feature); } return value; } static constexpr uint64_t LlFeatures() { LLFeaturesBits features[] = { LLFeaturesBits::LE_ENCRYPTION, LLFeaturesBits::CONNECTION_PARAMETERS_REQUEST_PROCEDURE, LLFeaturesBits::EXTENDED_REJECT_INDICATION, LLFeaturesBits::PERIPHERAL_INITIATED_FEATURES_EXCHANGE, LLFeaturesBits::LE_PING, LLFeaturesBits::EXTENDED_SCANNER_FILTER_POLICIES, LLFeaturesBits::LE_EXTENDED_ADVERTISING, // TODO: breaks AVD boot tests with LE audio // LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_CENTRAL, // LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL, }; uint64_t value = 0; for (auto feature : features) { value |= static_cast(feature); } return value; } template static bool ParseUint(Json::Value root, std::string field_name, T& output_value) { T max_value = std::numeric_limits::max(); Json::Value value = root[field_name]; if (value.isString()) { unsigned long long parsed_value = std::stoull(value.asString(), nullptr, 0); if (parsed_value > max_value) { LOG_INFO("invalid value for %s is discarded: %llu > %llu", field_name.c_str(), parsed_value, static_cast(max_value)); return false; } else { output_value = static_cast(parsed_value); return true; } } return false; } template static bool ParseUintArray(Json::Value root, std::string field_name, std::array& output_value) { T max_value = std::numeric_limits::max(); Json::Value value = root[field_name]; if (value.empty()) { return false; } if (!value.isArray()) { LOG_INFO("invalid value for %s is discarded: not an array", field_name.c_str()); return false; } if (value.size() != N) { LOG_INFO( "invalid value for %s is discarded: incorrect size %u, expected %zu", field_name.c_str(), value.size(), N); return false; } for (size_t n = 0; n < N; n++) { unsigned long long parsed_value = std::stoull(value[static_cast(n)].asString(), nullptr, 0); if (parsed_value > max_value) { LOG_INFO("invalid value for %s[%zu] is discarded: %llu > %llu", field_name.c_str(), n, parsed_value, static_cast(max_value)); } else { output_value[n] = parsed_value; } } return false; } template static bool ParseUintVector(Json::Value root, std::string field_name, std::vector& output_value) { T max_value = std::numeric_limits::max(); Json::Value value = root[field_name]; if (value.empty()) { return false; } if (!value.isArray()) { LOG_INFO("invalid value for %s is discarded: not an array", field_name.c_str()); return false; } output_value.clear(); for (size_t n = 0; n < value.size(); n++) { unsigned long long parsed_value = std::stoull(value[static_cast(n)].asString(), nullptr, 0); if (parsed_value > max_value) { LOG_INFO("invalid value for %s[%zu] is discarded: %llu > %llu", field_name.c_str(), n, parsed_value, static_cast(max_value)); } else { output_value.push_back(parsed_value); } } return false; } static void ParseHex64(Json::Value value, uint64_t* field) { if (value.isString()) { size_t end_char = 0; uint64_t parsed = std::stoll(value.asString(), &end_char, 16); if (end_char > 0) { *field = parsed; } } } ControllerProperties::ControllerProperties(const std::string& file_name) : 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 // after the creation of the properties. for (int i = 0; i < 47; i++) { supported_commands[i] = 0xff; } // Mark reserved commands as unsupported. for (int i = 47; i < 64; i++) { 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; } LOG_INFO("Reading controller properties from %s.", file_name.c_str()); std::ifstream file(file_name); Json::Value root; Json::CharReaderBuilder builder; std::string errs; if (!Json::parseFromStream(builder, file, &root, &errs)) { LOG_ERROR("Error reading controller properties from file: %s error: %s", file_name.c_str(), errs.c_str()); return; } // Legacy configuration options. ParseUint(root, "AclDataPacketSize", acl_data_packet_length); ParseUint(root, "ScoDataPacketSize", sco_data_packet_length); ParseUint(root, "NumAclDataPackets", total_num_acl_data_packets); ParseUint(root, "NumScoDataPackets", total_num_sco_data_packets); uint8_t hci_version = static_cast(this->hci_version); uint8_t lmp_version = static_cast(this->lmp_version); ParseUint(root, "Version", hci_version); ParseUint(root, "Revision", hci_subversion); ParseUint(root, "LmpPalVersion", lmp_version); ParseUint(root, "LmpPalSubversion", lmp_subversion); ParseUint(root, "ManufacturerName", company_identifier); ParseHex64(root["LeSupportedFeatures"], &le_features); // Configuration options. ParseUint(root, "hci_version", hci_version); ParseUint(root, "lmp_version", lmp_version); ParseUint(root, "hci_subversion", hci_subversion); ParseUint(root, "lmp_subversion", lmp_subversion); ParseUint(root, "company_identifier", company_identifier); ParseUintArray(root, "supported_commands", supported_commands); ParseUintArray(root, "lmp_features", lmp_features); ParseUint(root, "le_features", le_features); ParseUint(root, "acl_data_packet_length", acl_data_packet_length); ParseUint(root, "sco_data_packet_length ", sco_data_packet_length); ParseUint(root, "total_num_acl_data_packets ", total_num_acl_data_packets); ParseUint(root, "total_num_sco_data_packets ", total_num_sco_data_packets); ParseUint(root, "le_acl_data_packet_length ", le_acl_data_packet_length); ParseUint(root, "iso_data_packet_length ", iso_data_packet_length); ParseUint(root, "total_num_le_acl_data_packets ", total_num_le_acl_data_packets); ParseUint(root, "total_num_iso_data_packets ", total_num_iso_data_packets); ParseUint(root, "num_supported_iac", num_supported_iac); ParseUint(root, "le_advertising_physical_channel_tx_power", le_advertising_physical_channel_tx_power); ParseUintArray(root, "lmp_features", lmp_features); ParseUintVector(root, "supported_standard_codecs", supported_standard_codecs); ParseUintVector(root, "supported_vendor_specific_codecs", supported_vendor_specific_codecs); ParseUint(root, "le_filter_accept_list_size", le_filter_accept_list_size); ParseUint(root, "le_resolving_list_size", le_resolving_list_size); ParseUint(root, "le_supported_states", le_supported_states); ParseUint(root, "le_max_advertising_data_length", le_max_advertising_data_length); ParseUint(root, "le_num_supported_advertising_sets", le_num_supported_advertising_sets); ParseUintVector(root, "le_vendor_capabilities", le_vendor_capabilities); this->hci_version = static_cast(hci_version); this->lmp_version = static_cast(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( std::array supported_commands) { for (size_t i = 0; i < this->supported_commands.size(); i++) { this->supported_commands[i] &= supported_commands[i]; } } 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