blob: 9ba72690cebb3841c25e4f1c1c8d85d175b257cb [file] [log] [blame]
// Copyright 2017 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.
#include "fake_controller_base.h"
#include <zircon/status.h>
#include "garnet/drivers/bluetooth/lib/common/run_task_sync.h"
#include "garnet/drivers/bluetooth/lib/hci/acl_data_packet.h"
#include "garnet/drivers/bluetooth/lib/hci/hci_constants.h"
#include "lib/fsl/threading/create_thread.h"
#include "lib/fxl/functional/make_copyable.h"
namespace bluetooth {
namespace testing {
FakeControllerBase::FakeControllerBase(zx::channel cmd_channel,
zx::channel acl_data_channel)
: cmd_channel_(std::move(cmd_channel)),
acl_channel_(std::move(acl_data_channel)) {}
FakeControllerBase::~FakeControllerBase() {
// When this destructor gets called any subclass state will be undefined. If
// Stop() has not been called before reaching this point this can cause
// runtime errors when our MessageLoop handlers attempt to invoke the pure
// virtual methods of this class. So we require that the FakeController be
// stopped by now.
FXL_DCHECK(!IsStarted());
}
void FakeControllerBase::Start() {
FXL_DCHECK(!IsStarted());
FXL_DCHECK(cmd_channel_.is_valid());
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
thread_ = fsl::CreateThread(&task_runner_, "bluetooth-hci-test-controller");
auto setup_task = [this] {
cmd_channel_wait_.set_object(cmd_channel_.get());
cmd_channel_wait_.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
cmd_channel_wait_.set_handler(
fbl::BindMember(this, &FakeControllerBase::HandleCommandPacket));
zx_status_t status =
cmd_channel_wait_.Begin(fsl::MessageLoop::GetCurrent()->async());
FXL_DCHECK(status == ZX_OK);
if (acl_channel_.is_valid()) {
acl_channel_wait_.set_object(acl_channel_.get());
acl_channel_wait_.set_trigger(ZX_CHANNEL_READABLE |
ZX_CHANNEL_PEER_CLOSED);
acl_channel_wait_.set_handler(
fbl::BindMember(this, &FakeControllerBase::HandleACLPacket));
zx_status_t status =
acl_channel_wait_.Begin(fsl::MessageLoop::GetCurrent()->async());
FXL_DCHECK(status == ZX_OK);
}
};
common::RunTaskSync(setup_task, task_runner_);
}
void FakeControllerBase::Stop() {
FXL_DCHECK(IsStarted());
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
task_runner_->PostTask([this] {
CloseCommandChannelInternal();
CloseACLDataChannelInternal();
fsl::MessageLoop::GetCurrent()->QuitNow();
});
if (thread_.joinable())
thread_.join();
task_runner_ = nullptr;
}
void FakeControllerBase::SendCommandChannelPacket(
const common::ByteBuffer& packet) {
FXL_DCHECK(IsStarted());
zx_status_t status =
cmd_channel_.write(0, packet.data(), packet.size(), nullptr, 0);
if (status != ZX_OK) {
FXL_LOG(WARNING) << "FakeController: Failed to write to control channel: "
<< zx_status_get_string(status);
}
}
void FakeControllerBase::SendACLDataChannelPacket(
const common::ByteBuffer& packet) {
FXL_DCHECK(IsStarted());
zx_status_t status =
acl_channel_.write(0, packet.data(), packet.size(), nullptr, 0);
if (status != ZX_OK) {
FXL_LOG(WARNING) << "FakeController: Failed to write to ACL data channel: "
<< zx_status_get_string(status);
}
}
void FakeControllerBase::CloseCommandChannel() {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
common::RunTaskSync([this] { CloseCommandChannelInternal(); }, task_runner_);
}
void FakeControllerBase::CloseACLDataChannel() {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
common::RunTaskSync([this] { CloseACLDataChannelInternal(); }, task_runner_);
}
async_wait_result_t FakeControllerBase::HandleCommandPacket(
async_t* async,
zx_status_t wait_status,
const zx_packet_signal_t* signal) {
common::StaticByteBuffer<hci::kMaxCommandPacketPayloadSize> buffer;
uint32_t read_size;
zx_status_t status = cmd_channel_.read(0u, buffer.mutable_data(),
hci::kMaxCommandPacketPayloadSize,
&read_size, nullptr, 0, nullptr);
FXL_DCHECK(status == ZX_OK || status == ZX_ERR_PEER_CLOSED);
if (status < 0) {
if (status == ZX_ERR_PEER_CLOSED)
FXL_LOG(INFO) << "Command channel was closed";
else
FXL_LOG(ERROR) << "Failed to read on cmd channel: "
<< zx_status_get_string(status);
CloseCommandChannelInternal();
return ASYNC_WAIT_FINISHED;
}
if (read_size < sizeof(hci::CommandHeader)) {
FXL_LOG(ERROR) << "Malformed command packet received";
return ASYNC_WAIT_AGAIN;
}
common::MutableBufferView view(buffer.mutable_data(), read_size);
common::PacketView<hci::CommandHeader> packet(
&view, read_size - sizeof(hci::CommandHeader));
OnCommandPacketReceived(packet);
return ASYNC_WAIT_AGAIN;
}
async_wait_result_t FakeControllerBase::HandleACLPacket(
async_t* async,
zx_status_t wait_status,
const zx_packet_signal_t* signal) {
common::StaticByteBuffer<hci::kMaxACLPayloadSize + sizeof(hci::ACLDataHeader)>
buffer;
uint32_t read_size;
zx_status_t status =
acl_channel_.read(0u, buffer.mutable_data(), buffer.size(), &read_size,
nullptr, 0, nullptr);
FXL_DCHECK(status == ZX_OK || status == ZX_ERR_PEER_CLOSED);
if (status < 0) {
if (status == ZX_ERR_PEER_CLOSED)
FXL_LOG(INFO) << "ACL channel was closed";
else
FXL_LOG(ERROR) << "Failed to read on ACL channel: "
<< zx_status_get_string(status);
CloseACLDataChannelInternal();
return ASYNC_WAIT_FINISHED;
}
common::BufferView view(buffer.data(), read_size);
OnACLDataPacketReceived(view);
return ASYNC_WAIT_AGAIN;
}
void FakeControllerBase::CloseCommandChannelInternal() {
FXL_DCHECK(task_runner_->RunsTasksOnCurrentThread());
cmd_channel_wait_.Cancel(fsl::MessageLoop::GetCurrent()->async());
cmd_channel_wait_.set_object(ZX_HANDLE_INVALID);
cmd_channel_.reset();
}
void FakeControllerBase::CloseACLDataChannelInternal() {
FXL_DCHECK(task_runner_->RunsTasksOnCurrentThread());
acl_channel_wait_.Cancel(fsl::MessageLoop::GetCurrent()->async());
acl_channel_wait_.set_object(ZX_HANDLE_INVALID);
acl_channel_.reset();
}
} // namespace testing
} // namespace bluetooth