blob: f2c5fa01ce3d5c338c1a39913b05e8f0aa12d5c0 [file] [log] [blame]
// Copyright 2018 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 <cstdio>
#include <future>
#include <thread>
#include <ddk/protocol/bt/hci.h>
#include <lib/async/cpp/task.h>
#include <zircon/device/bt-hci.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include "garnet/drivers/bluetooth/lib/testing/fake_device.h"
#include "fake-device.h"
using ::btlib::common::DeviceAddress;
using ::btlib::testing::FakeController;
using ::btlib::testing::FakeDevice;
namespace bthci_fake {
const DeviceAddress kAddress0(DeviceAddress::Type::kLEPublic,
"00:00:00:00:00:01");
const DeviceAddress kAddress1(DeviceAddress::Type::kBREDR, "00:00:00:00:00:02");
Device::Device(zx_device_t* device)
: loop_(&kAsyncLoopConfigNoAttachToThread), parent_(device) {}
#define DEV(c) static_cast<Device*>(c)
static zx_protocol_device_t bthci_fake_device_ops = {
.version = DEVICE_OPS_VERSION,
.get_protocol = [](void* ctx, uint32_t proto_id, void* out_proto)
-> zx_status_t { return DEV(ctx)->GetProtocol(proto_id, out_proto); },
.unbind = [](void* ctx) { DEV(ctx)->Unbind(); },
.release = [](void* ctx) { DEV(ctx)->Release(); },
.ioctl = [](void* ctx, uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len,
size_t* out_actual) -> zx_status_t {
return DEV(ctx)->Ioctl(op, in_buf, in_len, out_buf, out_len, out_actual);
},
};
static bt_hci_protocol_ops_t hci_protocol_ops = {
.open_command_channel = [](void* ctx, zx_handle_t* chan) -> zx_status_t {
return DEV(ctx)->OpenChan(Channel::COMMAND, chan);
},
.open_acl_data_channel = [](void* ctx, zx_handle_t* chan) -> zx_status_t {
return DEV(ctx)->OpenChan(Channel::ACL, chan);
},
.open_snoop_channel = [](void* ctx, zx_handle_t* chan) -> zx_status_t {
return DEV(ctx)->OpenChan(Channel::SNOOP, chan);
},
};
#undef DEV
zx_status_t Device::Bind() {
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "bt_hci_fake";
args.ctx = this;
args.ops = &bthci_fake_device_ops;
args.proto_id = ZX_PROTOCOL_BT_HCI;
zx_status_t status = device_add(parent_, &args, &zxdev_);
if (status != ZX_OK) {
printf("bt_hci_fake: could not add device: %d\n", status);
return status;
}
FakeController::Settings settings;
settings.ApplyDualModeDefaults();
std::lock_guard<std::mutex> lock(device_lock_);
fake_device_ = fbl::AdoptRef(new FakeController());
fake_device_->set_settings(settings);
// A Sample LE remote device for le-scan to pick up.
// TODO(bwb): add tooling for adding/removing fake devices
const auto kAdvData0 = btlib::common::CreateStaticByteBuffer(
// Flags
0x02, 0x01, 0x02,
// Complete 16-bit service UUIDs
0x05, 0x03, 0x0d, 0x18, 0x0f, 0x18,
// Complete local name
0x05, 0x09, 'F', 'a', 'k', 'e');
auto device = std::make_unique<FakeDevice>(kAddress0, true, true);
device->SetAdvertisingData(kAdvData0);
fake_device_->AddDevice(std::move(device));
// A Sample BR/EDR remote device to interact with.
device = std::make_unique<FakeDevice>(kAddress1, false, false);
// A Toy Game
device->set_class_of_device(btlib::common::DeviceClass({0x14, 0x08, 0x00}));
fake_device_->AddDevice(std::move(device));
loop_.StartThread("bt_hci_fake");
return status;
}
void Device::Release() { delete this; }
void Device::Unbind() {
std::lock_guard<std::mutex> lock(device_lock_);
async::PostTask(loop_.dispatcher(), [loop = &loop_, fake_dev = fake_device_] {
fake_dev->Stop();
loop->Quit();
});
loop_.JoinThreads();
device_remove(zxdev_);
}
zx_status_t Device::Ioctl(uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len, size_t* out_actual) {
std::printf("%s\n", __func__);
zx_handle_t* reply = static_cast<zx_handle_t*>(out_buf);
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
if (op == IOCTL_BT_HCI_GET_COMMAND_CHANNEL) {
status = OpenChan(Channel::COMMAND, reply);
} else if (op == IOCTL_BT_HCI_GET_ACL_DATA_CHANNEL) {
status = OpenChan(Channel::ACL, reply);
} else if (op == IOCTL_BT_HCI_GET_SNOOP_CHANNEL) {
status = OpenChan(Channel::SNOOP, reply);
}
if (status == ZX_OK) {
*out_actual = sizeof(*reply);
}
return status;
}
zx_status_t Device::OpenChan(Channel chan_type, zx_handle_t* out_channel) {
zx::channel in, out;
auto status = zx::channel::create(0, &out, &in);
if (status != ZX_OK) {
printf("bt_hci_fake: could not create channel (type %i): %d\n", chan_type,
status);
return status;
}
*out_channel = out.release();
std::lock_guard<std::mutex> lock(device_lock_);
if (chan_type == Channel::COMMAND) {
async::PostTask(loop_.dispatcher(),
[device = fake_device_, in = std::move(in)]() mutable {
device->StartCmdChannel(std::move(in));
});
} else if (chan_type == Channel::ACL) {
async::PostTask(loop_.dispatcher(),
[device = fake_device_, in = std::move(in)]() mutable {
device->StartAclChannel(std::move(in));
});
} else if (chan_type == Channel::SNOOP) {
async::PostTask(loop_.dispatcher(),
[device = fake_device_, in = std::move(in)]() mutable {
device->StartSnoopChannel(std::move(in));
});
} else {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t Device::GetProtocol(uint32_t proto_id, void* out_proto) {
if (proto_id != ZX_PROTOCOL_BT_HCI) {
return ZX_ERR_NOT_SUPPORTED;
}
bt_hci_protocol_t* hci_proto = static_cast<bt_hci_protocol_t*>(out_proto);
hci_proto->ops = &hci_protocol_ops;
hci_proto->ctx = this;
return ZX_OK;
}
} // namespace bthci_fake