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

Commit b8350f1a authored by Henri Chataing's avatar Henri Chataing
Browse files

RootCanal: Implement basic support for LE 2M and LE Coded PHYs

The LE ACL connections will always use the LE 1M PHY, but
the associated HCI commands are still implemented.

Test: m root-canal
Bug: 275847929
Change-Id: I0545d4e0ea667e362267f1fa666087e4f2c1dfb8
parent e65f667e
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -106,9 +106,11 @@ static constexpr uint64_t LlFeatures() {
      LLFeaturesBits::EXTENDED_REJECT_INDICATION,
      LLFeaturesBits::PERIPHERAL_INITIATED_FEATURES_EXCHANGE,
      LLFeaturesBits::LE_PING,

      // LLFeaturesBits::LL_PRIVACY,
      LLFeaturesBits::EXTENDED_SCANNER_FILTER_POLICIES,
      LLFeaturesBits::LE_2M_PHY, LLFeaturesBits::LE_CODED_PHY,
      LLFeaturesBits::LE_EXTENDED_ADVERTISING,
      // LLFeaturesBits::LE_PERIODIC_ADVERTISING,

      // TODO: breaks AVD boot tests with LE audio
      // LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_CENTRAL,
@@ -326,10 +328,8 @@ static std::array<uint8_t, 64> SupportedCommands() {
      // OpCodeIndex::LE_READ_LOCAL_RESOLVABLE_ADDRESS,
      // OpCodeIndex::LE_SET_ADDRESS_RESOLUTION_ENABLE,
      // OpCodeIndex::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT,
      OpCodeIndex::LE_READ_MAXIMUM_DATA_LENGTH,
      // OpCodeIndex::LE_READ_PHY,
      // OpCodeIndex::LE_SET_DEFAULT_PHY,
      // OpCodeIndex::LE_SET_PHY,
      OpCodeIndex::LE_READ_MAXIMUM_DATA_LENGTH, OpCodeIndex::LE_READ_PHY,
      OpCodeIndex::LE_SET_DEFAULT_PHY, OpCodeIndex::LE_SET_PHY,
      // OpCodeIndex::LE_RECEIVER_TEST_V2,
      // OpCodeIndex::LE_TRANSMITTER_TEST_V2,
      OpCodeIndex::LE_SET_ADVERTISING_SET_RANDOM_ADDRESS,
+13 −0
Original line number Diff line number Diff line
@@ -137,6 +137,19 @@ struct ControllerProperties {
    return (supported_commands[index / 10] & (UINT64_C(1) << (index % 10))) !=
           0;
  }

  /// Return a bit mask with all supported PHYs
  /// (0b001 = LE_1M, 0b010 = LE_2M, 0b100 = LE_CODED).
  uint8_t LeSupportedPhys() const {
    uint8_t supported_phys = 0x1;  // LE_1M is always supported.
    if (SupportsLLFeature(bluetooth::hci::LLFeaturesBits::LE_2M_PHY)) {
      supported_phys |= 0x2;
    }
    if (SupportsLLFeature(bluetooth::hci::LLFeaturesBits::LE_CODED_PHY)) {
      supported_phys |= 0x4;
    }
    return supported_phys;
  }
};

}  // namespace rootcanal
+45 −3
Original line number Diff line number Diff line
@@ -2150,6 +2150,48 @@ void DualModeController::LeReadMaximumDataLength(CommandView command) {
      kNumCommandPackets, ErrorCode::SUCCESS, data_length));
}

void DualModeController::LeReadPhy(CommandView command) {
  auto command_view = gd_hci::LeReadPhyView::Create(
      gd_hci::LeConnectionManagementCommandView::Create(
          gd_hci::AclCommandView::Create(command)));
  ASSERT(command_view.IsValid());
  uint16_t connection_handle = command_view.GetConnectionHandle();
  bluetooth::hci::PhyType tx_phy{};
  bluetooth::hci::PhyType rx_phy{};
  ErrorCode status =
      link_layer_controller_.LeReadPhy(connection_handle, &tx_phy, &rx_phy);
  send_event_(bluetooth::hci::LeReadPhyCompleteBuilder::Create(
      kNumCommandPackets, status, connection_handle, tx_phy, rx_phy));
}

void DualModeController::LeSetDefaultPhy(CommandView command) {
  auto command_view = gd_hci::LeSetDefaultPhyView::Create(
      gd_hci::LeConnectionManagementCommandView::Create(
          gd_hci::AclCommandView::Create(command)));
  ASSERT(command_view.IsValid());
  ErrorCode status = link_layer_controller_.LeSetDefaultPhy(
      command_view.GetAllPhysNoTransmitPreference(),
      command_view.GetAllPhysNoReceivePreference(),
      command_view.GetTxPhysBitmask(), command_view.GetRxPhysBitmask());
  send_event_(bluetooth::hci::LeSetDefaultPhyCompleteBuilder::Create(
      kNumCommandPackets, status));
}

void DualModeController::LeSetPhy(CommandView command) {
  auto command_view = gd_hci::LeSetPhyView::Create(
      gd_hci::LeConnectionManagementCommandView::Create(
          gd_hci::AclCommandView::Create(command)));
  ASSERT(command_view.IsValid());
  ErrorCode status = link_layer_controller_.LeSetPhy(
      command_view.GetConnectionHandle(),
      command_view.GetAllPhysNoTransmitPreference(),
      command_view.GetAllPhysNoReceivePreference(),
      command_view.GetTxPhysBitmask(), command_view.GetRxPhysBitmask(),
      command_view.GetPhyOptions());
  send_event_(bluetooth::hci::LeSetPhyStatusBuilder::Create(
      status, kNumCommandPackets));
}

void DualModeController::LeReadSuggestedDefaultDataLength(CommandView command) {
  auto command_view = gd_hci::LeReadSuggestedDefaultDataLengthView::Create(
      gd_hci::LeConnectionManagementCommandView::Create(
@@ -3827,9 +3869,9 @@ const std::unordered_map<OpCode, DualModeController::CommandHandler>
         &DualModeController::LeSetResolvablePrivateAddressTimeout},
        {OpCode::LE_READ_MAXIMUM_DATA_LENGTH,
         &DualModeController::LeReadMaximumDataLength},
        //{OpCode::LE_READ_PHY, &DualModeController::LeReadPhy},
        //{OpCode::LE_SET_DEFAULT_PHY, &DualModeController::LeSetDefaultPhy},
        //{OpCode::LE_SET_PHY, &DualModeController::LeSetPhy},
        {OpCode::LE_READ_PHY, &DualModeController::LeReadPhy},
        {OpCode::LE_SET_DEFAULT_PHY, &DualModeController::LeSetDefaultPhy},
        {OpCode::LE_SET_PHY, &DualModeController::LeSetPhy},
        //{OpCode::LE_RECEIVER_TEST_V2, &DualModeController::LeReceiverTestV2},
        //{OpCode::LE_TRANSMITTER_TEST_V2,
        //&DualModeController::LeTransmitterTestV2},
+4 −0
Original line number Diff line number Diff line
@@ -529,6 +529,10 @@ class DualModeController : public Device {
  // 7.8.46
  void LeReadMaximumDataLength(CommandView command);

  void LeReadPhy(CommandView command);
  void LeSetDefaultPhy(CommandView command);
  void LeSetPhy(CommandView command);

  // 7.8.52
  void LeSetAdvertisingSetRandomAddress(CommandView command);

+129 −1
Original line number Diff line number Diff line
@@ -284,6 +284,132 @@ ErrorCode LinkLayerController::LeSetResolvablePrivateAddressTimeout(
  return ErrorCode::SUCCESS;
}

// HCI LE Read Phy command (Vol 4, Part E § 7.8.47).
ErrorCode LinkLayerController::LeReadPhy(uint16_t connection_handle,
                                         bluetooth::hci::PhyType* tx_phy,
                                         bluetooth::hci::PhyType* rx_phy) {
  // Note: no documented status code for this case.
  if (!connections_.HasHandle(connection_handle) ||
      connections_.GetPhyType(connection_handle) != Phy::Type::LOW_ENERGY) {
    LOG_INFO("unknown or invalid connection handle");
    return ErrorCode::UNKNOWN_CONNECTION;
  }

  // TODO(b/275970864) save the phy in the connection state.
  *tx_phy = bluetooth::hci::PhyType::LE_1M;
  *rx_phy = bluetooth::hci::PhyType::LE_1M;
  return ErrorCode::SUCCESS;
}

// HCI LE Set Default Phy command (Vol 4, Part E § 7.8.48).
ErrorCode LinkLayerController::LeSetDefaultPhy(
    bool all_phys_no_transmit_preference, bool all_phys_no_receive_preference,
    uint8_t tx_phys, uint8_t rx_phys) {
  uint8_t supported_phys = properties_.LeSupportedPhys();

  // If the All_PHYs parameter specifies that the Host has no preference,
  // the TX_PHYs parameter shall be ignored; otherwise at least one bit shall
  // be set to 1.
  if (all_phys_no_transmit_preference) {
    tx_phys = 0x1;  // LE_1M_PHY by default.
  }
  if (tx_phys == 0) {
    LOG_INFO("TX_Phys does not configure any bit");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the All_PHYs parameter specifies that the Host has no preference,
  // the RX_PHYs parameter shall be ignored; otherwise at least one bit shall
  // be set to 1.
  if (all_phys_no_receive_preference) {
    rx_phys = 0x1;  // LE_1M_PHY by default.
  }
  if (rx_phys == 0) {
    LOG_INFO("RX_Phys does not configure any bit");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the Host sets, in the TX_PHYs or RX_PHYs parameter, a bit for a PHY that
  // the Controller does not support, including a bit that is reserved for
  // future use, the Controller shall return the error code Unsupported Feature
  // or Parameter Value (0x11).
  if ((tx_phys & ~supported_phys) != 0) {
    LOG_INFO("TX_PhyS (%x) configures unsupported or reserved bits", tx_phys);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }
  if ((rx_phys & ~supported_phys) != 0) {
    LOG_INFO("RX_PhyS (%x) configures unsupported or reserved bits", rx_phys);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }

  // TODO(b/275970864) save the phy default preference.
  return ErrorCode::SUCCESS;
}

// HCI LE Set Phy command (Vol 4, Part E § 7.8.49).
ErrorCode LinkLayerController::LeSetPhy(
    uint16_t connection_handle, bool all_phys_no_transmit_preference,
    bool all_phys_no_receive_preference, uint8_t tx_phys, uint8_t rx_phys,
    bluetooth::hci::PhyOptions phy_options) {
  uint8_t supported_phys = properties_.LeSupportedPhys();

  // Note: no documented status code for this case.
  if (!connections_.HasHandle(connection_handle) ||
      connections_.GetPhyType(connection_handle) != Phy::Type::LOW_ENERGY) {
    LOG_INFO("unknown or invalid connection handle");
    return ErrorCode::UNKNOWN_CONNECTION;
  }

  // If the All_PHYs parameter specifies that the Host has no preference,
  // the TX_PHYs parameter shall be ignored; otherwise at least one bit shall
  // be set to 1.
  if (all_phys_no_transmit_preference) {
    tx_phys = 0x1;  // LE_1M_PHY by default.
  }
  if (tx_phys == 0) {
    LOG_INFO("TX_Phys does not configure any bit");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the All_PHYs parameter specifies that the Host has no preference,
  // the RX_PHYs parameter shall be ignored; otherwise at least one bit shall
  // be set to 1.
  if (all_phys_no_receive_preference) {
    rx_phys = 0x1;  // LE_1M_PHY by default.
  }
  if (rx_phys == 0) {
    LOG_INFO("RX_Phys does not configure any bit");
    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
  }

  // If the Host sets, in the TX_PHYs or RX_PHYs parameter, a bit for a PHY that
  // the Controller does not support, including a bit that is reserved for
  // future use, the Controller shall return the error code Unsupported Feature
  // or Parameter Value (0x11).
  if ((tx_phys & ~supported_phys) != 0) {
    LOG_INFO("TX_PhyS (%x) configures unsupported or reserved bits", tx_phys);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }
  if ((rx_phys & ~supported_phys) != 0) {
    LOG_INFO("RX_PhyS (%x) configures unsupported or reserved bits", rx_phys);
    return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
  }

  // The HCI_LE_PHY_Update_Complete event shall be generated either when one
  // or both PHY changes or when the Controller determines that neither PHY
  // will change immediately.
  // TODO(b/275970864) send LL_PHY_REQ to the peer.
  ScheduleTask(0ms, [this, connection_handle] {
    send_event_(bluetooth::hci::LePhyUpdateCompleteBuilder::Create(
        ErrorCode::SUCCESS, connection_handle,
        static_cast<uint8_t>(bluetooth::hci::PhyType::LE_1M),
        static_cast<uint8_t>(bluetooth::hci::PhyType::LE_1M)));
  });

  // TODO(b/275970864) save the phy preference.
  return ErrorCode::SUCCESS;
}

// HCI LE Set Host Feature command (Vol 4, Part E § 7.8.115).
ErrorCode LinkLayerController::LeSetHostFeature(uint8_t bit_number,
                                                uint8_t bit_value) {
@@ -683,6 +809,8 @@ ErrorCode LinkLayerController::LeSetExtendedScanParameters(
    bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy,
    uint8_t scanning_phys,
    std::vector<bluetooth::hci::PhyScanParameters> scanning_phy_parameters) {
  uint8_t supported_phys = properties_.LeSupportedPhys();

  // Extended advertising commands are disallowed when legacy advertising
  // commands were used since the last reset.
  if (!SelectExtendedAdvertising()) {
@@ -702,7 +830,7 @@ ErrorCode LinkLayerController::LeSetExtendedScanParameters(
  // If the Host specifies a PHY that is not supported by the Controller,
  // including a bit that is reserved for future use, it should return the
  // error code Unsupported Feature or Parameter Value (0x11).
  if ((scanning_phys & 0xfa) != 0) {
  if ((scanning_phys & ~supported_phys) != 0) {
    LOG_INFO(
        "scanning_phys (%02x) enables PHYs that are not supported by"
        " the controller",
Loading