blob: 5113627eb478edfcffed5585d646d96787235a35 [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 <assert.h>
#include <fidl/fuchsia.hardware.bluetooth/cpp/wire.h>
#include <fuchsia/hardware/bt/hci/c/banjo.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/status.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
namespace {
class PassthroughDevice;
using PassthroughDeviceType = ddk::Device<PassthroughDevice, ddk::GetProtocolable,
ddk::Messageable<fuchsia_hardware_bluetooth::Hci>::Mixin>;
class PassthroughDevice : public PassthroughDeviceType {
public:
explicit PassthroughDevice(zx_device_t* parent) : PassthroughDeviceType(parent) {}
// Ddk Apis.
static zx_status_t Bind(void* ctx, zx_device_t* device);
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
void DdkRelease();
private:
// fuchsia.hardware.bluetooth.Hci APIs.
void OpenCommandChannel(OpenCommandChannelRequestView request,
OpenCommandChannelCompleter::Sync& completer) override;
void OpenAclDataChannel(OpenAclDataChannelRequestView request,
OpenAclDataChannelCompleter::Sync& completer) override;
void OpenScoDataChannel(OpenScoDataChannelRequestView request,
OpenScoDataChannelCompleter::Sync& completer) override;
void ConfigureSco(ConfigureScoRequestView request,
ConfigureScoCompleter::Sync& completer) override;
void ResetSco(ResetScoCompleter::Sync& completer) override;
void OpenIsoDataChannel(OpenIsoDataChannelRequestView request,
OpenIsoDataChannelCompleter::Sync& completer) override;
void OpenSnoopChannel(OpenSnoopChannelRequestView request,
OpenSnoopChannelCompleter::Sync& completer) override;
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Hci> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
bt_hci_protocol_t hci;
};
zx_status_t PassthroughDevice::DdkGetProtocol(uint32_t proto_id, void* out_proto) {
if (proto_id != ZX_PROTOCOL_BT_HCI) {
return ZX_ERR_NOT_SUPPORTED;
}
// Forward the underlying bt-transport ops.
bt_hci_protocol_t* hci_proto = static_cast<bt_hci_protocol_t*>(out_proto);
hci_proto->ops = this->hci.ops;
hci_proto->ctx = this->hci.ctx;
return ZX_OK;
}
void PassthroughDevice::DdkRelease() { delete this; }
void PassthroughDevice::OpenCommandChannel(OpenCommandChannelRequestView request,
OpenCommandChannelCompleter::Sync& completer) {
if (zx_status_t status = bt_hci_open_command_channel(&this->hci, request->channel.release());
status != ZX_OK) {
completer.ReplyError(status);
}
completer.ReplySuccess();
}
void PassthroughDevice::OpenAclDataChannel(OpenAclDataChannelRequestView request,
OpenAclDataChannelCompleter::Sync& completer) {
if (zx_status_t status = bt_hci_open_acl_data_channel(&this->hci, request->channel.release());
status != ZX_OK) {
completer.ReplyError(status);
}
completer.ReplySuccess();
}
void PassthroughDevice::OpenScoDataChannel(OpenScoDataChannelRequestView request,
OpenScoDataChannelCompleter::Sync& completer) {
// This interface is not implemented.
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void PassthroughDevice::ConfigureSco(ConfigureScoRequestView request,
ConfigureScoCompleter::Sync& completer) {
// This interface is not implemented.
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void PassthroughDevice::ResetSco(ResetScoCompleter::Sync& completer) {
// This interface is not implemented.
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void PassthroughDevice::OpenIsoDataChannel(OpenIsoDataChannelRequestView request,
OpenIsoDataChannelCompleter::Sync& completer) {
if (zx_status_t status = bt_hci_open_iso_data_channel(&this->hci, request->channel.release());
status != ZX_OK) {
completer.ReplyError(status);
return;
}
completer.ReplySuccess();
}
void PassthroughDevice::OpenSnoopChannel(OpenSnoopChannelRequestView request,
OpenSnoopChannelCompleter::Sync& completer) {
if (zx_status_t status = bt_hci_open_snoop_channel(&this->hci, request->channel.release());
status != ZX_OK) {
completer.ReplyError(status);
}
completer.ReplySuccess();
}
void PassthroughDevice::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Hci> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
zxlogf(ERROR, "Unknown method in Hci request, closing with ZX_ERR_NOT_SUPPORTED");
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
zx_status_t PassthroughDevice::Bind(void* ctx, zx_device_t* device) {
zxlogf(INFO, "bt_hci_passthrough_bind: starting");
std::unique_ptr passthrough = std::make_unique<PassthroughDevice>(device);
zx_status_t status = device_get_protocol(device, ZX_PROTOCOL_BT_HCI, &passthrough->hci);
if (status != ZX_OK) {
zxlogf(ERROR, "bt_hci_passthrough_bind: failed protocol: %s", zx_status_get_string(status));
return status;
}
if (zx_status_t status = passthrough->DdkAdd(
ddk::DeviceAddArgs("bt_hci_passthrough").set_proto_id(ZX_PROTOCOL_BT_HCI));
status != ZX_OK) {
zxlogf(ERROR, "bt_hci_passthrough_bind failed: %s", zx_status_get_string(status));
return status;
}
// The driver framework now owns the memory for passthrough.
std::ignore = passthrough.release();
return ZX_OK;
}
zx_driver_ops_t bt_hci_passthrough_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = PassthroughDevice::Bind,
};
} // namespace
// This should be the last driver queried, so we match any transport.
ZIRCON_DRIVER(bt_hci_passthrough, bt_hci_passthrough_driver_ops, "fuchsia", "0.1");