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

Commit 1c513bc7 authored by Myles Watson's avatar Myles Watson Committed by Gerrit Code Review
Browse files

Merge "PDL: Add Dynamic Audio Buffer VSCs" into main

parents bfd5c9bb 97337c62
Loading
Loading
Loading
Loading
+167 −50
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "hci/event_checkers.h"
#include "hci/hci_layer.h"
#include "hci_controller_generated.h"
#include "os/log.h"
#include "os/metrics.h"
#include "os/system_properties.h"
#include "sysprops/sysprops_module.h"
@@ -196,9 +197,16 @@ struct Controller::impl {
    // Skip vendor capabilities check if configured.
    if (os::GetSystemPropertyBool(
            kPropertyVendorCapabilitiesEnabled, kDefaultVendorCapabilitiesEnabled)) {
      // More commands can be enqueued from le_get_vendor_capabilities_handler
      std::promise<void> vendor_promise;
      auto vendor_future = vendor_promise.get_future();
      hci_->EnqueueCommand(
          LeGetVendorCapabilitiesBuilder::Create(),
          handler->BindOnceOn(this, &Controller::impl::le_get_vendor_capabilities_handler));
          handler->BindOnceOn(
              this,
              &Controller::impl::le_get_vendor_capabilities_handler,
              std::move(vendor_promise)));
      vendor_future.wait();
    } else {
      vendor_capabilities_.is_supported_ = 0x00;
    }
@@ -531,7 +539,8 @@ struct Controller::impl {
    le_periodic_advertiser_list_size_ = complete_view.GetPeriodicAdvertiserListSize();
  }

  void le_get_vendor_capabilities_handler(CommandCompleteView view) {
  void le_get_vendor_capabilities_handler(
      std::promise<void> vendor_promise, CommandCompleteView view) {
    auto complete_view = LeGetVendorCapabilitiesCompleteView::Create(view);

    vendor_capabilities_.is_supported_ = 0x00;
@@ -551,7 +560,10 @@ struct Controller::impl {
    vendor_capabilities_.a2dp_source_offload_capability_mask_ = 0x00;
    vendor_capabilities_.bluetooth_quality_report_support_ = 0x00;

    if (complete_view.IsValid()) {
    if (!complete_view.IsValid()) {
      vendor_promise.set_value();
      return;
    }
    vendor_capabilities_.is_supported_ = 0x01;

    // v0.55
@@ -559,20 +571,25 @@ struct Controller::impl {
    vendor_capabilities_.max_advt_instances_ = base_vendor_capabilities.max_advt_instances_;
    vendor_capabilities_.offloaded_resolution_of_private_address_ =
        base_vendor_capabilities.offloaded_resolution_of_private_address_;
      vendor_capabilities_.total_scan_results_storage_ = base_vendor_capabilities.total_scan_results_storage_;
    vendor_capabilities_.total_scan_results_storage_ =
        base_vendor_capabilities.total_scan_results_storage_;
    vendor_capabilities_.max_irk_list_sz_ = base_vendor_capabilities.max_irk_list_sz_;
    vendor_capabilities_.filtering_support_ = base_vendor_capabilities.filtering_support_;
    vendor_capabilities_.max_filter_ = base_vendor_capabilities.max_filter_;
      vendor_capabilities_.activity_energy_info_support_ = base_vendor_capabilities.activity_energy_info_support_;
    vendor_capabilities_.activity_energy_info_support_ =
        base_vendor_capabilities.activity_energy_info_support_;

    if (complete_view.GetPayload().size() == 0) {
      vendor_capabilities_.version_supported_ = 55;
      vendor_promise.set_value();
      return;
    }

    // v0.95
    auto v95 = LeGetVendorCapabilitiesComplete095View::Create(complete_view);
    if (!v95.IsValid()) {
        LOG_ERROR("invalid data for hci requirements v0.95");
      LOG_INFO("invalid data for hci requirements v0.95");
      vendor_promise.set_value();
      return;
    }
    vendor_capabilities_.version_supported_ = v95.GetVersionSupported();
@@ -580,29 +597,108 @@ struct Controller::impl {
    vendor_capabilities_.extended_scan_support_ = v95.GetExtendedScanSupport();
    vendor_capabilities_.debug_logging_supported_ = v95.GetDebugLoggingSupported();
    if (vendor_capabilities_.version_supported_ <= 95 || complete_view.GetPayload().size() == 0) {
      vendor_promise.set_value();
      return;
    }

    // v0.96
    auto v96 = LeGetVendorCapabilitiesComplete096View::Create(v95);
    if (!v96.IsValid()) {
        LOG_ERROR("invalid data for hci requirements v0.96");
      LOG_INFO("invalid data for hci requirements v0.96");
      vendor_promise.set_value();
      return;
    }
      vendor_capabilities_.le_address_generation_offloading_support_ = v96.GetLeAddressGenerationOffloadingSupport();
    vendor_capabilities_.le_address_generation_offloading_support_ =
        v96.GetLeAddressGenerationOffloadingSupport();
    if (vendor_capabilities_.version_supported_ <= 96 || complete_view.GetPayload().size() == 0) {
      vendor_promise.set_value();
      return;
    }

    // v0.98
    auto v98 = LeGetVendorCapabilitiesComplete098View::Create(v96);
    if (!v98.IsValid()) {
        LOG_ERROR("invalid data for hci requirements v0.98");
      LOG_INFO("invalid data for hci requirements v0.98");
      vendor_promise.set_value();
      return;
    }
      vendor_capabilities_.a2dp_source_offload_capability_mask_ = v98.GetA2dpSourceOffloadCapabilityMask();
    vendor_capabilities_.a2dp_source_offload_capability_mask_ =
        v98.GetA2dpSourceOffloadCapabilityMask();
    vendor_capabilities_.bluetooth_quality_report_support_ = v98.GetBluetoothQualityReportSupport();

    // v1.03
    auto v103 = LeGetVendorCapabilitiesComplete103View::Create(v98);
    if (!v103.IsValid()) {
      LOG_INFO("invalid data for hci requirements v1.03");
      vendor_promise.set_value();
      return;
    }
    vendor_capabilities_.dynamic_audio_buffer_support_ = v103.GetDynamicAudioBufferSupport();

    if (vendor_capabilities_.dynamic_audio_buffer_support_ == 0) {
      vendor_promise.set_value();
      return;
    }
    hci_->EnqueueCommand(
        DabGetAudioBufferTimeCapabilityBuilder::Create(),
        module_.GetHandler()->BindOnceOn(
            this,
            &Controller::impl::le_get_dynamic_audio_buffer_support_handler,
            std::move(vendor_promise)));
  }

  void le_get_dynamic_audio_buffer_support_handler(
      std::promise<void> vendor_promise, CommandCompleteView view) {
    vendor_promise.set_value();
    auto dab_complete_view = DynamicAudioBufferCompleteView::Create(view);
    if (!dab_complete_view.IsValid()) {
      LOG_WARN("Invalid command complete");
      return;
    }

    if (dab_complete_view.GetStatus() != ErrorCode::SUCCESS) {
      LOG_WARN("Unexpected error code %s", ErrorCodeText(dab_complete_view.GetStatus()).c_str());
      return;
    }

    auto complete_view = DabGetAudioBufferTimeCapabilityCompleteView::Create(dab_complete_view);
    if (!complete_view.IsValid()) {
      LOG_WARN("Invalid get complete");
      return;
    }
    dab_supported_codecs_ = complete_view.GetAudioCodecTypeSupported();
    dab_codec_capabilities_ = complete_view.GetAudioCodecCapabilities();
  }

  void set_controller_dab_audio_buffer_time_complete(CommandCompleteView complete) {
    auto dab_complete = DynamicAudioBufferCompleteView::Create(complete);
    if (!dab_complete.IsValid()) {
      LOG_WARN("Invalid command complete");
      return;
    }

    if (dab_complete.GetStatus() != ErrorCode::SUCCESS) {
      LOG_WARN("Unexpected return code %s", ErrorCodeText(dab_complete.GetStatus()).c_str());
      return;
    }

    auto dab_set_complete = DabSetAudioBufferTimeCompleteView::Create(dab_complete);

    if (!dab_set_complete.IsValid()) {
      LOG_WARN("Invalid set complete");
      return;
    }

    LOG_INFO(
        "Configured Media Tx Buffer, time returned = %d",
        dab_set_complete.GetCurrentBufferTimeMs());
  }

  void set_controller_dab_audio_buffer_time(uint16_t buffer_time_ms) {
    hci_->EnqueueCommand(
        DabSetAudioBufferTimeBuilder::Create(buffer_time_ms),
        module_.GetHandler()->BindOnceOn(
            this, &Controller::impl::set_controller_dab_audio_buffer_time_complete));
  }

  void set_event_mask(uint64_t event_mask) {
@@ -1033,6 +1129,8 @@ struct Controller::impl {
        return vendor_capabilities_.a2dp_source_offload_capability_mask_ != 0x00;
      case OpCode::CONTROLLER_BQR:
        return vendor_capabilities_.bluetooth_quality_report_support_ == 0x01;
      case OpCode::DYNAMIC_AUDIO_BUFFER:
        return vendor_capabilities_.dynamic_audio_buffer_support_ > 0x00;
      // Before MSFT extension is fully supported, return false for the following MSFT_OPCODE_XXXX for now.
      case OpCode::MSFT_OPCODE_INTEL:
        return false;
@@ -1095,6 +1193,8 @@ struct Controller::impl {
  uint8_t le_number_supported_advertising_sets_{};
  uint8_t le_periodic_advertiser_list_size_{};
  VendorCapabilities vendor_capabilities_{};
  uint32_t dab_supported_codecs_{};
  std::array<DynamicAudioBufferCodecCapability, 32> dab_codec_capabilities_{};
};  // namespace hci

Controller::Controller() : impl_(std::make_unique<impl>(*this)) {}
@@ -1365,6 +1465,23 @@ Controller::VendorCapabilities Controller::GetVendorCapabilities() const {
  return impl_->vendor_capabilities_;
}

uint32_t Controller::GetDabSupportedCodecs() const {
  return impl_->dab_supported_codecs_;
}

const std::array<DynamicAudioBufferCodecCapability, 32>& Controller::GetDabCodecCapabilities()
    const {
  return impl_->dab_codec_capabilities_;
}

void Controller::SetDabAudioBufferTime(uint16_t buffer_time_ms) {
  if (impl_->vendor_capabilities_.dynamic_audio_buffer_support_ == 0) {
    LOG_WARN("Dynamic Audio Buffer not supported");
    return;
  }
  impl_->set_controller_dab_audio_buffer_time(buffer_time_ms);
}

uint8_t Controller::GetLePeriodicAdvertiserListSize() const {
  return impl_->le_periodic_advertiser_list_size_;
}
+6 −0
Original line number Diff line number Diff line
@@ -203,10 +203,16 @@ class Controller : public Module {
    uint8_t le_address_generation_offloading_support_;
    uint32_t a2dp_source_offload_capability_mask_;
    uint8_t bluetooth_quality_report_support_;
    uint32_t dynamic_audio_buffer_support_;
  };

  virtual VendorCapabilities GetVendorCapabilities() const;

  virtual uint32_t GetDabSupportedCodecs() const;
  virtual const std::array<DynamicAudioBufferCodecCapability, 32>& GetDabCodecCapabilities() const;

  virtual void SetDabAudioBufferTime(uint16_t buffer_time_ms);

  virtual bool IsSupported(OpCode op_code) const;

  static const ModuleFactory Factory;
+113 −17
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ constexpr uint16_t kCredits1 = 0x78;
constexpr uint16_t kHandle2 = 0x456;
constexpr uint16_t kCredits2 = 0x9a;
constexpr uint64_t kRandomNumber = 0x123456789abcdef0;
/*sbc_supported= 1, aac_supported= 1, aptx_supported= 0, aptx_hd_supported= 0, ldac_supported= 1 */
constexpr uint32_t kDynamicAudioBufferSupport = 0x13;
uint16_t feature_spec_version = 55;
constexpr char title[] = "hci_controller_test";

@@ -171,7 +173,8 @@ class HciLayerFakeForController : public HciLayerFake {
        event_builder =
            LeReadNumberOfSupportedAdvertisingSetsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0xF0);
      } break;
      case (OpCode::LE_GET_VENDOR_CAPABILITIES): {
      case (OpCode::LE_GET_VENDOR_CAPABILITIES):
        if (vendor_capabilities_ == nullptr) {
          BaseVendorCapabilities base_vendor_capabilities;
          base_vendor_capabilities.max_advt_instances_ = 0x10;
          base_vendor_capabilities.offloaded_resolution_of_private_address_ = 0x01;
@@ -183,12 +186,37 @@ class HciLayerFakeForController : public HciLayerFake {

          auto payload = std::make_unique<RawBuilder>();
          if (feature_spec_version > 55) {
          std::vector<uint8_t> payload_bytes = {0x20, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00};
            std::vector<uint8_t> payload_bytes = {
                0x20, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00};
            payload->AddOctets2(feature_spec_version);
            payload->AddOctets(payload_bytes);
          }
          event_builder = LeGetVendorCapabilitiesCompleteBuilder::Create(
              num_packets, ErrorCode::SUCCESS, base_vendor_capabilities, std::move(payload));
        } else {
          event_builder = std::move(vendor_capabilities_);
          vendor_capabilities_.reset();
        }
        break;
      case (OpCode::DYNAMIC_AUDIO_BUFFER): {
        auto dab_command =
            DynamicAudioBufferView::CreateOptional(VendorCommandView::Create(command));
        if (dab_command->GetDabCommand() == DabCommand::GET_AUDIO_BUFFER_TIME_CAPABILITY) {
          std::array<DynamicAudioBufferCodecCapability, 32> capabilities{};
          capabilities[0] =
              DynamicAudioBufferCodecCapability(0x123, 0x103, 0x1234);  // sbc_capabilities
          capabilities[1] =
              DynamicAudioBufferCodecCapability(0x223, 0x123, 0x2340);  // aac_capabilities
          capabilities[4] =
              DynamicAudioBufferCodecCapability(0x323, 0x223, 0x3456);  // ldac_capabilities
          event_builder = DabGetAudioBufferTimeCapabilityCompleteBuilder::Create(
              1, ErrorCode::SUCCESS, kDynamicAudioBufferSupport, capabilities);
        } else {
          auto set_command = DabSetAudioBufferTimeView::CreateOptional(*dab_command);
          dynamic_audio_buffer_time = set_command->GetBufferTimeMs();
          event_builder = DabSetAudioBufferTimeCompleteBuilder::Create(
              1, ErrorCode::SUCCESS, dynamic_audio_buffer_time);
        }
      } break;
      case (OpCode::SET_EVENT_MASK): {
        auto view = SetEventMaskView::Create(command);
@@ -240,12 +268,14 @@ class HciLayerFakeForController : public HciLayerFake {
    IncomingEvent(NumberOfCompletedPacketsBuilder::Create(completed_packets));
  }

  std::unique_ptr<EventBuilder> vendor_capabilities_ = nullptr;
  constexpr static uint16_t acl_data_packet_length = 1024;
  constexpr static uint8_t synchronous_data_packet_length = 60;
  constexpr static uint16_t total_num_acl_data_packets = 10;
  constexpr static uint16_t total_num_synchronous_data_packets = 12;
  uint64_t event_mask = 0;
  uint64_t le_event_mask = 0;
  uint16_t dynamic_audio_buffer_time = 0;
};

class ControllerTest : public ::testing::Test {
@@ -254,6 +284,8 @@ class ControllerTest : public ::testing::Test {
    feature_spec_version = feature_spec_version_;
    bluetooth::common::InitFlags::SetAllForTesting();
    test_hci_layer_ = new HciLayerFakeForController;
    test_hci_layer_->vendor_capabilities_ = std::move(vendor_capabilities_);
    vendor_capabilities_.reset();
    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
    client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
    fake_registry_.Start<Controller>(&thread_);
@@ -270,6 +302,7 @@ class ControllerTest : public ::testing::Test {
  Controller* controller_ = nullptr;
  os::Handler* client_handler_ = nullptr;
  uint16_t feature_spec_version_ = 98;
  std::unique_ptr<EventBuilder> vendor_capabilities_ = nullptr;
};
}  // namespace

@@ -297,6 +330,35 @@ class Controller096Test : public ControllerTest {
  }
};

class Controller103Test : public ControllerTest {
 protected:
  void SetUp() override {
    feature_spec_version_ = 0x100 + 0x03;
    BaseVendorCapabilities base_vendor_capabilities;
    base_vendor_capabilities.max_advt_instances_ = 0x10;
    base_vendor_capabilities.offloaded_resolution_of_private_address_ = 0x01;
    base_vendor_capabilities.total_scan_results_storage_ = 0x2800;
    base_vendor_capabilities.max_irk_list_sz_ = 0x20;
    base_vendor_capabilities.filtering_support_ = 0x01;
    base_vendor_capabilities.max_filter_ = 0x10;
    base_vendor_capabilities.activity_energy_info_support_ = 0x01;
    vendor_capabilities_ = LeGetVendorCapabilitiesComplete103Builder::Create(
        1,
        ErrorCode::SUCCESS,
        base_vendor_capabilities,
        feature_spec_version_,
        0x102,
        /*extended_scan_support=*/1,
        /*debug_logging_supported=*/1,
        /*le_address_generation_offloading_support=*/0,
        /*a2dp_source_offload_capability_mask=*/0x4,
        /*bluetooth_quality_report_support=*/1,
        kDynamicAudioBufferSupport,
        std::make_unique<RawBuilder>());
    ControllerTest::SetUp();
  }
};

TEST_F(ControllerTest, startup_teardown) {}

TEST_F(ControllerTest, read_controller_info) {
@@ -430,6 +492,40 @@ TEST_F(ControllerTest, feature_spec_version_098_test) {
  ASSERT_TRUE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
}

TEST_F(ControllerTest, feature_spec_version_098_no_dab_test) {
  ASSERT_FALSE(controller_->IsSupported(OpCode::DYNAMIC_AUDIO_BUFFER));
}

TEST_F(ControllerTest, set_dynamic_audio_buffer_time) {
  controller_->SetDabAudioBufferTime(123);
  thread_.GetReactor()->WaitForIdle(std::chrono::seconds(1));
  ASSERT_EQ(0, test_hci_layer_->dynamic_audio_buffer_time);
}

TEST_F(Controller103Test, feature_spec_version_103_dab_test) {
  ASSERT_EQ(controller_->GetVendorCapabilities().version_supported_, 0x100 + 3);
  ASSERT_TRUE(controller_->IsSupported(OpCode::LE_MULTI_ADVT));
  ASSERT_TRUE(controller_->IsSupported(OpCode::CONTROLLER_DEBUG_INFO));
  ASSERT_TRUE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
  ASSERT_TRUE(controller_->IsSupported(OpCode::DYNAMIC_AUDIO_BUFFER));
  ASSERT_EQ(controller_->GetDabSupportedCodecs(), kDynamicAudioBufferSupport);
  for (size_t bit = 0; bit < 32; bit++) {
    if (kDynamicAudioBufferSupport & (1u << bit)) {
      ASSERT_GT(controller_->GetDabCodecCapabilities()[bit].maximum_time_ms_, 0) << " bit " << bit;
    } else {
      ASSERT_EQ(controller_->GetDabCodecCapabilities()[bit].maximum_time_ms_, 0);
      ASSERT_EQ(controller_->GetDabCodecCapabilities()[bit].minimum_time_ms_, 0);
      ASSERT_EQ(controller_->GetDabCodecCapabilities()[bit].default_time_ms_, 0);
    }
  }
}

TEST_F(Controller103Test, set_dynamic_audio_buffer_time) {
  controller_->SetDabAudioBufferTime(123);
  thread_.GetReactor()->WaitForIdle(std::chrono::seconds(1));
  ASSERT_EQ(123, test_hci_layer_->dynamic_audio_buffer_time);
}

std::promise<void> credits1_set;
std::promise<void> credits2_set;

+67 −2
Original line number Diff line number Diff line
@@ -456,6 +456,7 @@ enum OpCode : 16 {
  CONTROLLER_DEBUG_INFO = 0xFD5B,
  CONTROLLER_A2DP_OPCODE = 0xFD5D,
  CONTROLLER_BQR = 0xFD5E,
  DYNAMIC_AUDIO_BUFFER = 0xFD5F,
  // MSFT_OPCODE_xxxx below are needed for the tests.
  MSFT_OPCODE_MEDIATEK = 0xFD30,
  MSFT_OPCODE_QUALCOMM = 0xFD70,
@@ -5311,7 +5312,17 @@ packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 {

packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 {
  a2dp_source_offload_capability_mask: 32,
  bluetooth_quality_report_support: 8
  bluetooth_quality_report_support: 8,
  _payload_,
}

packet LeGetVendorCapabilitiesComplete103 : LeGetVendorCapabilitiesComplete098 {
  dynamic_audio_buffer_support : 32,
  _payload_,
}

test LeGetVendorCapabilitiesComplete103 {
  "\x0e\x1c\x01\x53\xfd\x00\x10\x01\x00\x28\x00\x01\x3e\x01\x03\x01\x14\x00\x01\x01\x00\x23\x00\x00\x00\x01\x23\x00\x00\x00",
}

enum SubOcf : 8 {
@@ -5780,6 +5791,60 @@ test ControllerBqrComplete {
  "\x0e\x08\x01\x5e\xfd\x00\x1f\x00\x07\x00",
}

enum DabCommand : 8 {
  GET_AUDIO_BUFFER_TIME_CAPABILITY = 0x01,
  SET_AUDIO_BUFFER_TIME = 0x02,
}

packet DynamicAudioBuffer : VendorCommand (op_code = DYNAMIC_AUDIO_BUFFER) {
  dab_command : DabCommand,
  _body_,
}

packet DynamicAudioBufferComplete : CommandComplete (command_op_code = DYNAMIC_AUDIO_BUFFER) {
  status : ErrorCode,
  dab_command : DabCommand,
  _body_,
}

packet DabGetAudioBufferTimeCapability : DynamicAudioBuffer (dab_command = GET_AUDIO_BUFFER_TIME_CAPABILITY) {
}

test DabGetAudioBufferTimeCapability {
  "\x5f\xfd\x01\x01",
}

struct DynamicAudioBufferCodecCapability {
  default_time_ms : 16,
  maximum_time_ms : 16,
  minimum_time_ms : 16,
}

packet DabGetAudioBufferTimeCapabilityComplete : DynamicAudioBufferComplete (dab_command = GET_AUDIO_BUFFER_TIME_CAPABILITY) {
  audio_codec_type_supported : 32,
  audio_codec_capabilities : DynamicAudioBufferCodecCapability[32],
}

test DabGetAudioBufferTimeCapabilityComplete {
  "\x0e\xc9\x01\x5f\xfd\x00\x01\x03\x00\x00\x00\xf4\x01\xf4\x01\x64\x00\xf4\x01\xf4\x01\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
}

packet DabSetAudioBufferTime : DynamicAudioBuffer(dab_command = SET_AUDIO_BUFFER_TIME) {
  buffer_time_ms : 16, // Requested audio buffer time for the currently used codec.
}

test DabSetAudioBufferTime {
  "\x5f\xfd\x03\x02\x23\x01",
}

packet DabSetAudioBufferTimeComplete : DynamicAudioBufferComplete (dab_command = SET_AUDIO_BUFFER_TIME) {
  current_buffer_time_ms : 16,
}

test DabSetAudioBufferTimeComplete {
  "\x0e\x07\x01\x5f\xfd\x00\x02\x23\x01",
}

// HCI Event Packets

packet InquiryComplete : Event (event_code = INQUIRY_COMPLETE) {