blob: 5fe4a746081fd79d4ffff4185f972d52002c5b93 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_FIDL_FAKE_HCI_SERVER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_FIDL_FAKE_HCI_SERVER_H_
#include <fuchsia/hardware/bluetooth/cpp/fidl_test_base.h>
#include <lib/async/cpp/wait.h>
#include <lib/async/dispatcher.h>
#include <lib/zx/channel.h>
#include <string>
#include "fuchsia/hardware/bluetooth/cpp/fidl.h"
#include "gmock/gmock.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/iso/iso_common.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/slab_allocators.h"
namespace bt::fidl::testing {
namespace fhbt = fuchsia::hardware::bluetooth;
class FakeHciServer final : public fhbt::testing::Hci_TestBase {
public:
FakeHciServer(::fidl::InterfaceRequest<fhbt::Hci> request, async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher) {
binding_.Bind(std::move(request));
}
void Unbind() { binding_.Unbind(); }
zx_status_t SendEvent(const BufferView& event) {
return command_channel_.write(/*flags=*/0, event.data(), static_cast<uint32_t>(event.size()),
/*handles=*/nullptr, /*num_handles=*/0);
}
zx_status_t SendAcl(const BufferView& buffer) {
return acl_channel_.write(/*flags=*/0, buffer.data(), static_cast<uint32_t>(buffer.size()),
/*handles=*/nullptr, /*num_handles=*/0);
}
zx_status_t SendSco(const BufferView& buffer) {
return sco_channel_.write(/*flags=*/0, buffer.data(), static_cast<uint32_t>(buffer.size()),
/*handles=*/nullptr, /*num_handles=*/0);
}
zx_status_t SendIso(const BufferView& buffer) {
return iso_channel_.write(/*flags=*/0, buffer.data(), static_cast<uint32_t>(buffer.size()),
/*handles=*/nullptr, /*num_handles=*/0);
}
const std::vector<bt::DynamicByteBuffer>& commands_received() const { return commands_received_; }
const std::vector<bt::DynamicByteBuffer>& acl_packets_received() const {
return acl_packets_received_;
}
const std::vector<bt::DynamicByteBuffer>& sco_packets_received() const {
return sco_packets_received_;
}
const std::vector<bt::DynamicByteBuffer>& iso_packets_received() const {
return iso_packets_received_;
}
bool CloseCommandChannel() {
bool was_valid = command_channel_valid();
command_channel_.reset();
return was_valid;
}
bool CloseAclChannel() {
bool was_valid = acl_channel_.is_valid();
acl_channel_.reset();
return was_valid;
}
// Use custom |ConfigureScoTestCallback| to manually verify configuration fields from tests
using ConfigureScoTestCallback =
fit::function<void(fhbt::ScoCodingFormat, fhbt::ScoEncoding, fhbt::ScoSampleRate)>;
void set_check_configure_sco(ConfigureScoTestCallback callback) {
check_configure_sco_ = std::move(callback);
}
// Uee custom |ResetScoTestCallback| to manually perform reset actions from tests
using ResetScoTestCallback = fit::function<void()>;
void set_reset_sco_callback(ResetScoTestCallback callback) {
reset_sco_cb_ = std::move(callback);
}
bool acl_channel_valid() const { return acl_channel_.is_valid(); }
bool command_channel_valid() const { return command_channel_.is_valid(); }
bool sco_channel_valid() const { return sco_channel_.is_valid(); }
bool iso_channel_valid() const { return iso_channel_.is_valid(); }
private:
void OpenCommandChannel(zx::channel channel, OpenCommandChannelCallback callback) override {
command_channel_ = std::move(channel);
InitializeWait(command_wait_, command_channel_);
callback(fpromise::ok());
}
void OpenAclDataChannel(zx::channel channel, OpenAclDataChannelCallback callback) override {
acl_channel_ = std::move(channel);
InitializeWait(acl_wait_, acl_channel_);
callback(fpromise::ok());
}
void OpenScoDataChannel(zx::channel channel, OpenScoDataChannelCallback callback) override {
sco_channel_ = std::move(channel);
InitializeWait(sco_wait_, sco_channel_);
callback(fpromise::ok());
}
void OpenIsoDataChannel(zx::channel channel, OpenIsoDataChannelCallback callback) override {
iso_channel_ = std::move(channel);
InitializeWait(iso_wait_, iso_channel_);
callback(fpromise::ok());
}
void ConfigureSco(fhbt::ScoCodingFormat coding_format, fhbt::ScoEncoding encoding,
fhbt::ScoSampleRate sample_rate, ConfigureScoCallback callback) override {
if (check_configure_sco_) {
check_configure_sco_(coding_format, encoding, sample_rate);
}
callback(fpromise::ok());
}
void ResetSco(ResetScoCallback callback) override {
if (reset_sco_cb_) {
reset_sco_cb_();
}
callback(fpromise::ok());
}
void NotImplemented_(const std::string& name) override { FAIL() << name << " not implemented"; }
void InitializeWait(async::WaitBase& wait, zx::channel& channel) {
BT_ASSERT(channel.is_valid());
wait.Cancel();
wait.set_object(channel.get());
wait.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
BT_ASSERT(wait.Begin(dispatcher_) == ZX_OK);
}
void OnAclSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
ASSERT_TRUE(status == ZX_OK);
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
acl_channel_.reset();
return;
}
ASSERT_TRUE(signal->observed & ZX_CHANNEL_READABLE);
bt::StaticByteBuffer<hci::allocators::kLargeACLDataPacketSize> buffer;
uint32_t read_size = 0;
zx_status_t read_status = acl_channel_.read(0u, buffer.mutable_data(), /*handles=*/nullptr,
static_cast<uint32_t>(buffer.size()), 0, &read_size,
/*actual_handles=*/nullptr);
ASSERT_TRUE(read_status == ZX_OK);
acl_packets_received_.emplace_back(bt::BufferView(buffer, read_size));
acl_wait_.Begin(dispatcher_);
}
void OnCommandSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
ASSERT_TRUE(status == ZX_OK);
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
command_channel_.reset();
return;
}
ASSERT_TRUE(signal->observed & ZX_CHANNEL_READABLE);
bt::StaticByteBuffer<hci::allocators::kLargeControlPacketSize> buffer;
uint32_t read_size = 0;
zx_status_t read_status =
command_channel_.read(0u, buffer.mutable_data(), /*handles=*/nullptr,
static_cast<uint32_t>(buffer.size()), 0, &read_size,
/*actual_handles=*/nullptr);
ASSERT_TRUE(read_status == ZX_OK);
commands_received_.emplace_back(bt::BufferView(buffer, read_size));
command_wait_.Begin(dispatcher_);
}
void OnScoSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
ASSERT_TRUE(status == ZX_OK);
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
sco_channel_.reset();
return;
}
ASSERT_TRUE(signal->observed & ZX_CHANNEL_READABLE);
bt::StaticByteBuffer<hci::allocators::kMaxScoDataPacketSize> buffer;
uint32_t read_size = 0;
zx_status_t read_status = sco_channel_.read(0u, buffer.mutable_data(), /*handles=*/nullptr,
static_cast<uint32_t>(buffer.size()), 0, &read_size,
/*actual_handles=*/nullptr);
ASSERT_TRUE(read_status == ZX_OK);
sco_packets_received_.emplace_back(bt::BufferView(buffer, read_size));
sco_wait_.Begin(dispatcher_);
}
void OnIsoSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
ASSERT_TRUE(status == ZX_OK);
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
iso_channel_.reset();
return;
}
ASSERT_TRUE(signal->observed & ZX_CHANNEL_READABLE);
bt::StaticByteBuffer<iso::kMaxIsochronousDataPacketSize> buffer;
uint32_t read_size = 0;
zx_status_t read_status = iso_channel_.read(0u, buffer.mutable_data(), /*handles=*/nullptr,
static_cast<uint32_t>(buffer.size()), 0, &read_size,
/*actual_handles=*/nullptr);
ASSERT_TRUE(read_status == ZX_OK);
iso_packets_received_.emplace_back(bt::BufferView(buffer, read_size));
iso_wait_.Begin(dispatcher_);
}
::fidl::Binding<fhbt::Hci> binding_{this};
zx::channel command_channel_;
std::vector<bt::DynamicByteBuffer> commands_received_;
zx::channel acl_channel_;
std::vector<bt::DynamicByteBuffer> acl_packets_received_;
zx::channel sco_channel_;
std::vector<bt::DynamicByteBuffer> sco_packets_received_;
ConfigureScoTestCallback check_configure_sco_;
ResetScoTestCallback reset_sco_cb_;
zx::channel iso_channel_;
std::vector<bt::DynamicByteBuffer> iso_packets_received_;
async::WaitMethod<FakeHciServer, &FakeHciServer::OnAclSignal> acl_wait_{this};
async::WaitMethod<FakeHciServer, &FakeHciServer::OnCommandSignal> command_wait_{this};
async::WaitMethod<FakeHciServer, &FakeHciServer::OnScoSignal> sco_wait_{this};
async::WaitMethod<FakeHciServer, &FakeHciServer::OnIsoSignal> iso_wait_{this};
async_dispatcher_t* dispatcher_;
};
} // namespace bt::fidl::testing
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_FIDL_FAKE_HCI_SERVER_H_