diff --git a/system/audio/Android.bp b/system/audio/Android.bp index c0e3119d9d1bf6179fb6115da6ae44c2b0f373d3..2fc5464fd4fac866228655a3af20b3049258c71d 100644 --- a/system/audio/Android.bp +++ b/system/audio/Android.bp @@ -14,9 +14,6 @@ cc_library_static { "asrc/asrc_resampler.cc", "asrc/asrc_tables.cc", ], - include_dirs: [ - "packages/modules/Bluetooth/system/gd", - ], shared_libs: [ "libchrome", ], @@ -44,7 +41,6 @@ cc_library_host_shared { stl: "libc++_static", include_dirs: [ "packages/modules/Bluetooth/system", - "packages/modules/Bluetooth/system/gd", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", diff --git a/system/audio/asrc/asrc_resampler.cc b/system/audio/asrc/asrc_resampler.cc index 0590b849d9f54a0a9f70dfc1cc0c7b5b7feba990..09e59584c7e8b487960f869d81eeb5e05f8474ef 100644 --- a/system/audio/asrc/asrc_resampler.cc +++ b/system/audio/asrc/asrc_resampler.cc @@ -19,15 +19,15 @@ #include <base/logging.h> #include <base/strings/stringprintf.h> +#include <algorithm> #include <cmath> #include <utility> #include "asrc_tables.h" -#include "hal/nocp_iso_clocker.h" namespace bluetooth::audio::asrc { -class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { +class SourceAudioHalAsrc::ClockRecovery : public ClockHandler { const int interval_; std::mutex mutex_; @@ -113,8 +113,7 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { } int dt_current = int(timestamp_us - link.local_time); - if (std::abs(dt_current) < std::abs(link.decim_dt[1])) - link.decim_dt[1] = dt_current; + link.decim_dt[1] = std::min(link.decim_dt[1], dt_current); if (link.local_time - link.decim_t0 < 1000 * 1000) return; @@ -237,11 +236,9 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { state_{ .link = {{.state = LinkState::RESET}, {.state = LinkState::RESET}}, .active_link_id = -1}, - reference_timing_{0, 0, 0} { - ::bluetooth::hal::NocpIsoClocker::Register(this); - } + reference_timing_{0, 0, 0} {} - ~ClockRecovery() override { ::bluetooth::hal::NocpIsoClocker::Unregister(); } + ~ClockRecovery() override {} __attribute__((no_sanitize("integer"))) uint32_t Convert( uint32_t stream_time) { @@ -466,16 +463,16 @@ inline int32_t SourceAudioHalAsrc::Resampler::Filter(const int32_t* in, #endif -SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, - int bit_depth, int interval_us, - int num_burst_buffers, - int burst_delay_ms) +SourceAudioHalAsrc::SourceAudioHalAsrc( + std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate, + int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms) : sample_rate_(sample_rate), bit_depth_(bit_depth), interval_us_(interval_us), stream_us_(0), drift_us_(0), out_counter_(0), + clock_source_(std::move(clock_source)), resampler_pos_{0, 0} { buffers_size_ = 0; @@ -509,6 +506,7 @@ SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, // when the PCM bit_depth is higher than 16 bits. clock_recovery_ = std::make_unique<ClockRecovery>(interval_us_); + clock_source_->Bind(clock_recovery_.get()); resamplers_ = std::make_unique<std::vector<Resampler>>(channels, bit_depth_); // Deduct from the PCM stream characteristics, the size of the pool buffers diff --git a/system/audio/asrc/asrc_resampler.h b/system/audio/asrc/asrc_resampler.h index ae9ea312cb2b27861359c1bace4771bb74f4e516..400c51c7be8fe3888da2a215bff98f580abaebb0 100644 --- a/system/audio/asrc/asrc_resampler.h +++ b/system/audio/asrc/asrc_resampler.h @@ -23,6 +23,19 @@ namespace bluetooth::audio::asrc { +class ClockHandler { + public: + virtual ~ClockHandler() = default; + virtual void OnEvent(uint32_t timestamp, int link_id, + int num_of_completed_packets) = 0; +}; + +class ClockSource { + public: + virtual ~ClockSource() = default; + virtual void Bind(ClockHandler*) = 0; +}; + class SourceAudioHalAsrc { public: // The Asynchronous Sample Rate Conversion (ASRC) is set up from the PCM @@ -36,9 +49,9 @@ class SourceAudioHalAsrc { // `burst_delay_ms` helps to ensure that the synchronization with the // transmission intervals is done. - SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth, - int interval_us, int num_burst_buffers = 2, - int burst_delay_ms = 500); + SourceAudioHalAsrc(std::shared_ptr<ClockSource> clock_source, int channels, + int sample_rate, int bit_depth, int interval_us, + int num_burst_buffers = 2, int burst_delay_ms = 500); ~SourceAudioHalAsrc(); @@ -74,6 +87,7 @@ class SourceAudioHalAsrc { class ClockRecovery; std::unique_ptr<ClockRecovery> clock_recovery_; + std::shared_ptr<ClockSource> clock_source_; class Resampler; std::unique_ptr<std::vector<Resampler>> resamplers_; diff --git a/system/audio/asrc/asrc_resampler_test.cc b/system/audio/asrc/asrc_resampler_test.cc index 04bf07ede89f39ceadeb473405a984100313a692..1b90e0a803180877cdd56769f4bd19c788ccbab5 100644 --- a/system/audio/asrc/asrc_resampler_test.cc +++ b/system/audio/asrc/asrc_resampler_test.cc @@ -19,17 +19,17 @@ #include <cstdio> #include <iostream> -namespace bluetooth::hal { -void NocpIsoClocker::Register(NocpIsoHandler*) {} -void NocpIsoClocker::Unregister() {} -} // namespace bluetooth::hal - namespace bluetooth::audio::asrc { +class MockClockSource : public ClockSource { + void Bind(ClockHandler*) override {} +}; + class SourceAudioHalAsrcTest : public SourceAudioHalAsrc { public: SourceAudioHalAsrcTest(int channels, int bitdepth) - : SourceAudioHalAsrc(channels, 48000, bitdepth, 10000) {} + : SourceAudioHalAsrc(std::make_unique<MockClockSource>(), channels, 48000, + bitdepth, 10000) {} template <typename T> void Resample(double ratio, const T* in, size_t in_length, size_t* in_count, diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc index a32d7b23bfa1af3c698be6b0e09dfa98de068ca2..3b28bdfc57c31d298e18e8129ba120849458bd1f 100644 --- a/system/bta/hearing_aid/hearing_aid.cc +++ b/system/bta/hearing_aid/hearing_aid.cc @@ -32,12 +32,14 @@ #include <mutex> #include <vector> +#include "audio/asrc/asrc_resampler.h" #include "bta/include/bta_gatt_api.h" #include "bta/include/bta_gatt_queue.h" #include "bta/include/bta_hearing_aid_api.h" #include "btm_iso_api.h" #include "device/include/controller.h" #include "embdrv/g722/g722_enc_dec.h" +#include "hal/link_clocker.h" #include "hardware/bt_gatt_types.h" #include "include/check.h" #include "internal_include/bt_trace.h" @@ -275,6 +277,13 @@ class HearingAidImpl : public HearingAid { const int DROP_FREQUENCY_THRESHOLD = bluetooth::common::init_flags::get_asha_packet_drop_frequency_threshold(); + // Resampler context for audio stream. + // Clock recovery uses L2CAP Flow Control Credit Ind acknowledgments + // from either the left or right connection, whichever is first + // connected. + std::shared_ptr<bluetooth::hal::L2capCreditIndEvents> asrc_clock_source; + std::unique_ptr<bluetooth::audio::asrc::SourceAudioHalAsrc> asrc; + public: ~HearingAidImpl() override = default; @@ -354,6 +363,51 @@ class HearingAidImpl : public HearingAid { } } + // Reset and configure the ASHA resampling context using the input device + // devices as reference for the BT clock estimation. + void ConfigureAsrc() { + if (!IS_FLAG_ENABLED(asha_asrc)) { + log::info("Asha resampling disabled: feature flag off"); + return; + } + + // Create a new ASRC context if required. + if (asrc == nullptr) { + asrc_clock_source = + std::make_shared<bluetooth::hal::L2capCreditIndEvents>(); + asrc = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( + asrc_clock_source, /*channels*/ 2, + /*sample_rate*/ codec_in_use == CODEC_G722_24KHZ ? 24000 : 16000, + /*bit_depth*/ 16, + /*interval_us*/ default_data_interval_ms * 1000, + /*num_burst_buffers*/ 0, + /*burst_delay*/ 0); + } + + for (auto& device : hearingDevices.devices) { + if (!device.accepting_audio) { + continue; + } + + uint16_t lcid = GAP_ConnGetL2CAPCid(device.gap_handle); + uint16_t rcid = 0; + L2CA_GetRemoteCid(lcid, &rcid); + + auto conn = btm_acl_for_bda(device.address, BT_TRANSPORT_LE); + log::info("Updating ASRC context for handle=0x{:x}, cid=0x{:x}", + conn->Handle(), rcid); + + asrc_clock_source->Update(device.isLeft(), conn->Handle(), rcid); + } + } + + // Reset the ASHA resampling context. + void ResetAsrc() { + log::info("Resetting the Asha resampling context"); + asrc_clock_source = nullptr; + asrc = nullptr; + } + uint16_t UpdateBleConnParams(const RawAddress& address) { /* List of parameters that depends on the chosen Connection Interval */ uint16_t min_ce_len = MIN_CE_LEN_20MS_CI; @@ -1171,6 +1225,10 @@ class HearingAidImpl : public HearingAid { } else { log::info("audio_running={}", audio_running); } + + // Close the ASRC context. + ResetAsrc(); + audio_running = false; stop_audio_ticks(); @@ -1213,6 +1271,9 @@ class HearingAidImpl : public HearingAid { return; } + // Open the ASRC context. + ConfigureAsrc(); + // TODO: shall we also reset the encoder ? encoder_state_release(); encoder_state_init(); @@ -1352,6 +1413,16 @@ class HearingAidImpl : public HearingAid { return diff_credit < (init_credit / 2 - 1); } + void OnAudioDataReadyResample(const std::vector<uint8_t>& data) { + if (asrc == nullptr) { + return OnAudioDataReady(data); + } + + for (auto const resampled_data : asrc->Run(data)) { + OnAudioDataReady(*resampled_data); + } + } + void OnAudioDataReady(const std::vector<uint8_t>& data) { /* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */ bool need_drop = false; @@ -1414,6 +1485,18 @@ class HearingAidImpl : public HearingAid { l2cap_flush_threshold = 1; } + // Skipping packets completely messes up the resampler context. + // The condition for skipping packets seems to be easily triggered, + // causing dropouts that could have been avoided. + // + // When the resampler is enabled, the flush threshold is set + // to the number of credits specified for the ASHA l2cap streaming + // channel. This will ensure it is only triggered in case of + // critical failure. + if (IS_FLAG_ENABLED(asha_asrc)) { + l2cap_flush_threshold = 8; + } + // TODO: monural, binarual check // divide encoded data into packets, add header, send. @@ -2069,7 +2152,7 @@ void encryption_callback(const RawAddress* address, tBT_TRANSPORT, void*, class HearingAidAudioReceiverImpl : public HearingAidAudioReceiver { public: void OnAudioDataReady(const std::vector<uint8_t>& data) override { - if (instance) instance->OnAudioDataReady(data); + if (instance) instance->OnAudioDataReadyResample(data); } void OnAudioSuspend(const std::function<void()>& stop_audio_ticks) override { if (instance) instance->OnAudioSuspend(stop_audio_ticks); diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc index f8c87c43834efd73fea8754e200c94db0535b8ae..f2e8b12d165bb01e10ba1b08ce221234ad0ef9c4 100644 --- a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc +++ b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc @@ -27,6 +27,7 @@ #include "bta/le_audio/codec_manager.h" #include "common/repeating_timer.h" #include "common/time_util.h" +#include "gd/hal/link_clocker.h" #include "os/log.h" #include "osi/include/wakelock.h" #include "stack/include/main_thread.h" @@ -248,6 +249,7 @@ void SourceImpl::StartAudioTicks() { wakelock_acquire(); if (IS_FLAG_ENABLED(leaudio_hal_client_asrc)) { asrc_ = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( + std::make_shared<bluetooth::hal::NocpIsoEvents>(), source_codec_config_.num_channels, source_codec_config_.sample_rate, source_codec_config_.bits_per_sample, source_codec_config_.data_interval_us); diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp index 27e6b6f204821a7d4c23f54112cc063cdb7f3e4a..c583cab7e44ef31fd8c6b69498d266bcf1d6b1c1 100644 --- a/system/gd/hal/Android.bp +++ b/system/gd/hal/Android.bp @@ -10,7 +10,7 @@ package { filegroup { name: "BluetoothHalSources", srcs: [ - "nocp_iso_clocker.cc", + "link_clocker.cc", "snoop_logger.cc", "snoop_logger_socket.cc", "snoop_logger_socket_thread.cc", diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn index 67034e1cc069544a3cb30d2942ea3b1dfb051149..affd15172330391f1757d305ccada27f61bb8ac9 100644 --- a/system/gd/hal/BUILD.gn +++ b/system/gd/hal/BUILD.gn @@ -16,7 +16,7 @@ source_set("BluetoothHalSources") { sources = [ - "nocp_iso_clocker.cc", + "link_clocker.cc", "snoop_logger.cc", "snoop_logger_socket.cc", "snoop_logger_socket_thread.cc", diff --git a/system/gd/hal/hci_hal_android_hidl.cc b/system/gd/hal/hci_hal_android_hidl.cc index 686b9510cf6d14fb1f47caf9bdf8770e608ab417..daef43372e140169e097e2f3e5efde017272dda0 100644 --- a/system/gd/hal/hci_hal_android_hidl.cc +++ b/system/gd/hal/hci_hal_android_hidl.cc @@ -38,7 +38,7 @@ #include "common/stop_watch.h" #include "common/strings.h" #include "hal/hci_hal.h" -#include "hal/nocp_iso_clocker.h" +#include "hal/link_clocker.h" #include "hal/snoop_logger.h" #include "os/alarm.h" #include "os/log.h" @@ -83,8 +83,8 @@ std::string GetTimerText(const char* func_name, VecType vec) { class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 { public: - InternalHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker) - : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) { + InternalHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker) + : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) { init_promise_ = new std::promise<void>(); } @@ -115,7 +115,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 { Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override { common::StopWatch stop_watch(GetTimerText(__func__, event)); std::vector<uint8_t> received_hci_packet(event.begin(), event.end()); - nocp_iso_clocker_->OnHciEvent(received_hci_packet); + link_clocker_->OnHciEvent(received_hci_packet); btsnoop_logger_->Capture( received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT); { @@ -130,6 +130,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 { Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override { common::StopWatch stop_watch(GetTimerText(__func__, data)); std::vector<uint8_t> received_hci_packet(data.begin(), data.end()); + link_clocker_->OnAclDataReceived(received_hci_packet); btsnoop_logger_->Capture( received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL); { @@ -174,7 +175,7 @@ class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 { std::promise<void>* init_promise_ = nullptr; HciHalCallbacks* callback_ = nullptr; SnoopLogger* btsnoop_logger_ = nullptr; - NocpIsoClocker* nocp_iso_clocker_ = nullptr; + LinkClocker* link_clocker_ = nullptr; }; static constexpr char kBluetoothAidlHalServiceName[] = @@ -182,8 +183,8 @@ static constexpr char kBluetoothAidlHalServiceName[] = class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks { public: - AidlHciCallbacks(SnoopLogger* btsnoop_logger, NocpIsoClocker* nocp_iso_clocker) - : btsnoop_logger_(btsnoop_logger), nocp_iso_clocker_(nocp_iso_clocker) { + AidlHciCallbacks(SnoopLogger* btsnoop_logger, LinkClocker* link_clocker) + : btsnoop_logger_(btsnoop_logger), link_clocker_(link_clocker) { init_promise_ = new std::promise<void>(); } @@ -212,7 +213,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot ::ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) override { common::StopWatch stop_watch(GetTimerText(__func__, event)); std::vector<uint8_t> received_hci_packet(event.begin(), event.end()); - nocp_iso_clocker_->OnHciEvent(received_hci_packet); + link_clocker_->OnHciEvent(received_hci_packet); btsnoop_logger_->Capture( received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT); bool sent = false; @@ -232,6 +233,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot ::ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) override { common::StopWatch stop_watch(GetTimerText(__func__, data)); std::vector<uint8_t> received_hci_packet(data.begin(), data.end()); + link_clocker_->OnAclDataReceived(received_hci_packet); btsnoop_logger_->Capture( received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL); bool sent = false; @@ -291,7 +293,7 @@ class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoot std::promise<void>* init_promise_ = nullptr; HciHalCallbacks* callback_ = nullptr; SnoopLogger* btsnoop_logger_ = nullptr; - NocpIsoClocker* nocp_iso_clocker_ = nullptr; + LinkClocker* link_clocker_ = nullptr; }; } // namespace @@ -362,7 +364,7 @@ class HciHalHidl : public HciHal { protected: void ListDependencies(ModuleList* list) const { - list->add<NocpIsoClocker>(); + list->add<LinkClocker>(); list->add<SnoopLogger>(); } @@ -374,7 +376,7 @@ class HciHalHidl : public HciHal { ASSERT(bt_hci_1_1_ == nullptr); ASSERT(aidl_hci_ == nullptr); - nocp_iso_clocker_ = GetDependency<NocpIsoClocker>(); + link_clocker_ = GetDependency<LinkClocker>(); btsnoop_logger_ = GetDependency<SnoopLogger>(); if (AServiceManager_isDeclared(kBluetoothAidlHalServiceName)) { @@ -408,7 +410,7 @@ class HciHalHidl : public HciHal { death_link == STATUS_OK, "Unable to set the death recipient for the Bluetooth HAL"); aidl_callbacks_ = - ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, nocp_iso_clocker_); + ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_, link_clocker_); aidl_hci_->initialize(aidl_callbacks_); } } @@ -448,7 +450,7 @@ class HciHalHidl : public HciHal { ASSERT(bt_hci_ != nullptr); auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0); ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL"); - hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, nocp_iso_clocker_); + hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_, link_clocker_); if (bt_hci_1_1_ != nullptr) { bt_hci_1_1_->initialize_1_1(hidl_callbacks_); @@ -507,7 +509,7 @@ class HciHalHidl : public HciHal { std::shared_ptr<AidlHciCallbacks> aidl_callbacks_; ::ndk::ScopedAIBinder_DeathRecipient aidl_death_recipient_; SnoopLogger* btsnoop_logger_; - NocpIsoClocker* nocp_iso_clocker_; + LinkClocker* link_clocker_; }; const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHidl(); }); diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc index 4c24c6b55c7676915a091e431b8292da203f6ff8..e05fda06b404f84d5d5b0943df9c3c545a4f6e70 100644 --- a/system/gd/hal/hci_hal_host.cc +++ b/system/gd/hal/hci_hal_host.cc @@ -30,8 +30,8 @@ #include "common/init_flags.h" #include "hal/hci_hal.h" +#include "hal/link_clocker.h" #include "hal/mgmt.h" -#include "hal/nocp_iso_clocker.h" #include "hal/snoop_logger.h" #include "metrics/counter_metrics.h" #include "os/log.h" @@ -285,7 +285,7 @@ class HciHalHost : public HciHal { protected: void ListDependencies(ModuleList* list) const { - list->add<NocpIsoClocker>(); + list->add<LinkClocker>(); list->add<metrics::CounterMetrics>(); list->add<SnoopLogger>(); } @@ -307,7 +307,7 @@ class HciHalHost : public HciHal { common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)), common::Bind(&HciHalHost::send_packet_ready, common::Unretained(this))); hci_incoming_thread_.GetReactor()->ModifyRegistration(reactable_, os::Reactor::REACT_ON_READ_ONLY); - nocp_iso_clocker_ = GetDependency<NocpIsoClocker>(); + link_clocker_ = GetDependency<LinkClocker>(); btsnoop_logger_ = GetDependency<SnoopLogger>(); LOG_INFO("HAL opened successfully"); } @@ -348,7 +348,7 @@ class HciHalHost : public HciHal { bluetooth::os::Reactor::Reactable* reactable_ = nullptr; std::queue<std::vector<uint8_t>> hci_outgoing_queue_; SnoopLogger* btsnoop_logger_ = nullptr; - NocpIsoClocker* nocp_iso_clocker_ = nullptr; + LinkClocker* link_clocker_ = nullptr; void write_to_fd(HciPacket packet) { // TODO: replace this with new queue when it's ready @@ -414,7 +414,7 @@ class HciHalHost : public HciHal { HciPacket receivedHciPacket; receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size); - nocp_iso_clocker_->OnHciEvent(receivedHciPacket); + link_clocker_->OnHciEvent(receivedHciPacket); btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT); { std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_); @@ -440,6 +440,7 @@ class HciHalHost : public HciHal { HciPacket receivedHciPacket; receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size); + link_clocker_->OnAclDataReceived(receivedHciPacket); btsnoop_logger_->Capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL); { std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_); diff --git a/system/gd/hal/link_clocker.cc b/system/gd/hal/link_clocker.cc new file mode 100644 index 0000000000000000000000000000000000000000..d0d09d9b3b76e1afc55686efb84c9e8b690da846 --- /dev/null +++ b/system/gd/hal/link_clocker.cc @@ -0,0 +1,228 @@ +/* + * Copyright 2023 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 "hal/link_clocker.h" + +#include <algorithm> + +namespace bluetooth::hal { + +static constexpr uint16_t kInvalidConnectionHandle = 0xFFFF; + +static class : public bluetooth::audio::asrc::ClockHandler { + void OnEvent(uint32_t, int, int) override {} +} g_empty_handler; + +static std::atomic<bluetooth::audio::asrc::ClockHandler*> g_nocp_iso_handler = &g_empty_handler; + +static struct { + std::mutex mutex; + bluetooth::audio::asrc::ClockHandler* handler; + struct { + uint16_t connection_handle; + uint16_t stream_cid; + } links[2]; +} g_credit_ind_handler = {.handler = &g_empty_handler, .links = {{}, {}}}; + +NocpIsoEvents::~NocpIsoEvents() { + g_nocp_iso_handler = &g_empty_handler; +} + +void NocpIsoEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) { + g_nocp_iso_handler = handler; +} + +L2capCreditIndEvents::~L2capCreditIndEvents() { + std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex); + g_credit_ind_handler.handler = &g_empty_handler; + g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle; + g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle; +} + +void L2capCreditIndEvents::Bind(bluetooth::audio::asrc::ClockHandler* handler) { + std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex); + g_credit_ind_handler.handler = handler; + g_credit_ind_handler.links[0].connection_handle = kInvalidConnectionHandle; + g_credit_ind_handler.links[1].connection_handle = kInvalidConnectionHandle; +} + +void L2capCreditIndEvents::Update(int link_id, uint16_t connection_handle, uint16_t stream_cid) { + std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex); + g_credit_ind_handler.links[link_id].connection_handle = connection_handle; + g_credit_ind_handler.links[link_id].stream_cid = stream_cid; +} + +LinkClocker::LinkClocker() : cig_id_(-1), cis_handle_(-1) {} + +void LinkClocker::OnHciEvent(const HciPacket& packet) { + const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062; + const int HCI_EVT_COMMAND_COMPLETE = 0x0e; + const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13; + + // HCI Event [Core 4.E.5.4.4] + // | [0] Event Code + // | [1] Parameter Total Length + // | [2+] Parameters + + if (packet.size() < 2) return; + + const uint8_t* payload = packet.data() + 2; + size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2); + + switch (packet[0]) { + // HCI Command Complete Event [Core 4.E.7.7.14] + // | [0] Num_HCI_Command_Packets, Ignored + // | [1..2] Command_Opcode, catch `HCI_LE_Set_CIG_Parameters` + // | [3+] Return Parameters + + case HCI_EVT_COMMAND_COMPLETE: { + if (payload_length < 3) return; + + int cmd_opcode = payload[1] | (payload[2] << 8); + if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return; + + const uint8_t* parameters = payload + 3; + size_t parameters_length = payload_length - 3; + + // HCI LE Set CIG Parameters return parameters [4.E.7.8.97] + // | [0] Status, 0 when OK + // | [1] CIG_ID + // | [2] CIS_Count + // | [3..4] Connection_Handle[0] + + if (parameters_length < 3) return; + + int status = parameters[0]; + int cig_id = parameters[1]; + int cis_count = parameters[2]; + + if (status != 0) return; + + if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) { + LOG_WARN("Multiple groups not supported"); + return; + } + + cig_id_ = -1; + cis_handle_ = -1; + + if (cis_count > 0 && parameters_length >= 5) { + cig_id_ = cig_id; + cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff; + } + + break; + } + + // HCI Number Of Completed Packets event [Core 4.E.7.7.19] + // | [0] Num_Handles + // | FOR each `Num_Handles` connection handles + // | | [0..1] Connection_Handle, catch the CIS Handle + // | | [2..3] Num_Completed_Packets + + case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: { + if (payload_length < 1) return; + + int i, num_handles = payload[0]; + const uint8_t* item = payload + 1; + if (payload_length < size_t(1 + 4 * num_handles)) return; + + for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_; + i++, item += 4) + ; + if (i >= num_handles) return; + + auto timestamp = std::chrono::system_clock::now().time_since_epoch(); + unsigned timestamp_us = + std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count(); + int num_of_completed_packets = item[2] | (item[3] << 8); + (*g_nocp_iso_handler).OnEvent(timestamp_us, 0, num_of_completed_packets); + + break; + } + } +} + +/// Filter received L2CAP PDUs for Credit acknowledgments for the registered +/// L2CAP channels. +void LinkClocker::OnAclDataReceived(const HciPacket& packet) { + const int L2CAP_LE_U_CID = 0x0005; + const int L2CAP_FLOW_CONTROL_CREDIT_IND = 0x16; + + // HCI ACL Data Packets [4.E.5.4.2] + // | [0..1] Handle | PBF | BC + // | [2..3] Data Total Length + // | [4+] Data + + if (packet.size() < 4) return; + + uint16_t handle = packet[0] | (packet[1] << 8); + int packet_boundary_flag = (handle >> 12) & 0x3; + handle &= 0xfff; + uint16_t data_total_length = std::min(size_t(packet[2] | (packet[3] << 8)), packet.size() - 4); + const uint8_t* data = packet.data() + 4; + + if (data_total_length < 4 || packet_boundary_flag == 0b01 || packet_boundary_flag == 0b11) return; + + // L2CAP Signalling PDU Format [3.A.4] + // | [0..1] PDU Length + // | [2..3] Channel ID + // | [4+] PDU + uint16_t pdu_length = std::min(data[0] | (data[1] << 8), data_total_length - 4); + uint16_t channel_id = data[2] | (data[3] << 8); + data += 4; + + if (channel_id != L2CAP_LE_U_CID) return; + + while (pdu_length >= 4) { + // | FOR each command in the PDU + // | | [0] Command Code + // | | [1] Command Identifier + // | | [2..3] Data Length + // | | [4+] Data + uint8_t command_code = data[0]; + uint16_t data_length = std::min(data[2] | (data[3] << 8), pdu_length - 4); + + if (command_code == L2CAP_FLOW_CONTROL_CREDIT_IND && data_length == 4) { + // | L2CAP Flow Control Credit Ind [3.A.4.24] + // | | [4..5] CID + // | | [6..7] Credits + uint16_t channel_id = data[4] | (data[5] << 8); + uint16_t credits = data[6] | (data[7] << 8); + + auto timestamp = std::chrono::system_clock::now().time_since_epoch(); + unsigned timestamp_us = + std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count(); + + { + std::lock_guard<std::mutex> guard(g_credit_ind_handler.mutex); + for (int link_id = 0; link_id < 2; link_id++) { + auto const& link = g_credit_ind_handler.links[link_id]; + if (link.connection_handle == handle && link.stream_cid == channel_id) { + g_credit_ind_handler.handler->OnEvent(timestamp_us, link_id, credits); + } + } + } + } + + data += data_length + 4; + pdu_length -= data_length + 4; + } +} + +const ModuleFactory LinkClocker::Factory = ModuleFactory([]() { return new LinkClocker(); }); + +} // namespace bluetooth::hal diff --git a/system/gd/hal/nocp_iso_clocker.h b/system/gd/hal/link_clocker.h similarity index 61% rename from system/gd/hal/nocp_iso_clocker.h rename to system/gd/hal/link_clocker.h index a04cbd13c0d98c5a7a01381849be466dafd62631..c0a7278cabdb95ac915616c7da9a8cd984461d11 100644 --- a/system/gd/hal/nocp_iso_clocker.h +++ b/system/gd/hal/link_clocker.h @@ -16,25 +16,35 @@ #pragma once +#include "audio/asrc/asrc_resampler.h" #include "hci_hal.h" #include "module.h" namespace bluetooth::hal { -class NocpIsoHandler { +class NocpIsoEvents : public bluetooth::audio::asrc::ClockSource { public: - virtual ~NocpIsoHandler() = default; - virtual void OnEvent(uint32_t timestamp_us, int link_id, int num_of_completed_packets) = 0; + NocpIsoEvents() = default; + ~NocpIsoEvents() override; + + void Bind(bluetooth::audio::asrc::ClockHandler*) override; +}; + +class L2capCreditIndEvents : public bluetooth::audio::asrc::ClockSource { + public: + L2capCreditIndEvents() {} + ~L2capCreditIndEvents() override; + + void Bind(bluetooth::audio::asrc::ClockHandler*) override; + void Update(int link_id, uint16_t connection_handle, uint16_t stream_cid); }; -class NocpIsoClocker : public ::bluetooth::Module { +class LinkClocker : public ::bluetooth::Module { public: static const ModuleFactory Factory; void OnHciEvent(const HciPacket& packet); - - static void Register(NocpIsoHandler* handler); - static void Unregister(); + void OnAclDataReceived(const HciPacket& packet); protected: void ListDependencies(ModuleList*) const override{}; @@ -42,10 +52,10 @@ class NocpIsoClocker : public ::bluetooth::Module { void Stop() override{}; std::string ToString() const override { - return std::string("NocpIsoClocker"); + return std::string("LinkClocker"); } - NocpIsoClocker(); + LinkClocker(); private: int cig_id_; diff --git a/system/gd/hal/nocp_iso_clocker.cc b/system/gd/hal/nocp_iso_clocker.cc deleted file mode 100644 index b97f515944792f47c45c945750db258db9422db9..0000000000000000000000000000000000000000 --- a/system/gd/hal/nocp_iso_clocker.cc +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2023 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 "hal/nocp_iso_clocker.h" - -namespace bluetooth::hal { - -static class : public NocpIsoHandler { - void OnEvent(uint32_t, int, int) override {} -} g_empty_handler; - -static std::atomic<NocpIsoHandler*> g_handler = &g_empty_handler; - -NocpIsoClocker::NocpIsoClocker() : cig_id_(-1), cis_handle_(-1) {} - -void NocpIsoClocker::OnHciEvent(const HciPacket& packet) { - const int HCI_CMD_SET_CIG_PARAMETERS = 0x2062; - const int HCI_EVT_COMMAND_COMPLETE = 0x0e; - const int HCI_EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13; - - // HCI Event [Core 4.E.5.4.4] - // | [0] Event Code - // | [1] Parameter Total Length - // | [2+] Parameters - - if (packet.size() < 2) return; - - const uint8_t* payload = packet.data() + 2; - size_t payload_length = std::min(size_t(packet[1]), packet.size() - 2); - - switch (packet[0]) { - // HCI Command Complete Event [Core 4.E.7.7.14] - // | [0] Num_HCI_Command_Packets, Ignored - // | [1..2] Command_Opcode, catch `HCI_LE_Set_CIG_Parameters` - // | [3+] Return Parameters - - case HCI_EVT_COMMAND_COMPLETE: { - if (payload_length < 3) return; - - int cmd_opcode = payload[1] | (payload[2] << 8); - if (cmd_opcode != HCI_CMD_SET_CIG_PARAMETERS) return; - - const uint8_t* parameters = payload + 3; - size_t parameters_length = payload_length - 3; - - // HCI LE Set CIG Parameters return parameters [4.E.7.8.97] - // | [0] Status, 0 when OK - // | [1] CIG_ID - // | [2] CIS_Count - // | [3..4] Connection_Handle[0] - - if (parameters_length < 3) return; - - int status = parameters[0]; - int cig_id = parameters[1]; - int cis_count = parameters[2]; - - if (status != 0) return; - - if (cig_id_ >= 0 && cis_handle_ >= 0 && cig_id_ != cig_id) { - LOG_WARN("Multiple groups not supported"); - return; - } - - cig_id_ = -1; - cis_handle_ = -1; - - if (cis_count > 0 && parameters_length >= 5) { - cig_id_ = cig_id; - cis_handle_ = (parameters[3] | (parameters[4] << 8)) & 0xfff; - } - - break; - } - - // HCI Number Of Completed Packets event [Core 4.E.7.7.19] - // | [0] Num_Handles - // | FOR each `Num_Handles` connection handles - // | | [0..1] Connection_Handle, catch the CIS Handle - // | | [2..3] Num_Completed_Packets - - case HCI_EVT_NUMBER_OF_COMPLETED_PACKETS: { - if (payload_length < 1) return; - - int i, num_handles = payload[0]; - const uint8_t* item = payload + 1; - if (payload_length < size_t(1 + 4 * num_handles)) return; - - for (i = 0; i < num_handles && ((item[0] | (item[1] << 8)) & 0xfff) != cis_handle_; - i++, item += 4) - ; - if (i >= num_handles) return; - - auto timestamp = std::chrono::system_clock::now().time_since_epoch(); - unsigned timestamp_us = - std::chrono::duration_cast<std::chrono::microseconds>(timestamp).count(); - int num_of_completed_packets = item[2] | (item[3] << 8); - (*g_handler).OnEvent(timestamp_us, 0, num_of_completed_packets); - - break; - } - } -} - -void NocpIsoClocker::Register(NocpIsoHandler* handler) { - g_handler = handler; -} -void NocpIsoClocker::Unregister() { - g_handler = &g_empty_handler; -} - -const ModuleFactory NocpIsoClocker::Factory = ModuleFactory([]() { return new NocpIsoClocker(); }); - -} // namespace bluetooth::hal