blob: 007537a43a15147fdd5026f842f6fd1e8f2e9bf4 [file] [log] [blame]
// Copyright 2022 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_HCI_TRANSPORT_UART_BT_TRANSPORT_UART_H_
#define SRC_CONNECTIVITY_BLUETOOTH_HCI_TRANSPORT_UART_BT_TRANSPORT_UART_H_
#include <fidl/fuchsia.driver.compat/cpp/wire.h>
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <fidl/fuchsia.hardware.bluetooth/cpp/wire.h>
#include <fidl/fuchsia.hardware.serialimpl/cpp/driver/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/wait.h>
#include <lib/driver/compat/cpp/device_server.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/fit/thread_checker.h>
#include <lib/zx/event.h>
#include <threads.h>
#include <zircon/device/bt-hci.h>
#include <mutex>
#include <sdk/lib/driver/logging/cpp/logger.h>
namespace bt_transport_uart {
class BtTransportUart
: public fdf::DriverBase,
public fidl::WireAsyncEventHandler<fuchsia_driver_framework::NodeController>,
public fidl::WireServer<fuchsia_hardware_bluetooth::Hci>,
public fdf::WireServer<fuchsia_hardware_serialimpl::Device> {
public:
// If |dispatcher| is non-null, it will be used instead of a new work thread.
// tests.
explicit BtTransportUart(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher);
zx::result<> Start() override;
void PrepareStop(fdf::PrepareStopCompleter completer) override;
void handle_unknown_event(
fidl::UnknownEventMetadata<fuchsia_driver_framework::NodeController> metadata) override {}
// Request handlers for Hci protocol.
void OpenCommandChannel(OpenCommandChannelRequestView request,
OpenCommandChannelCompleter::Sync& completer) override;
void OpenAclDataChannel(OpenAclDataChannelRequestView request,
OpenAclDataChannelCompleter::Sync& completer) override;
void OpenSnoopChannel(OpenSnoopChannelRequestView request,
OpenSnoopChannelCompleter::Sync& completer) override;
void OpenScoDataChannel(OpenScoDataChannelRequestView request,
OpenScoDataChannelCompleter::Sync& completer) override;
void OpenIsoDataChannel(OpenIsoDataChannelRequestView request,
OpenIsoDataChannelCompleter::Sync& completer) override;
void ConfigureSco(ConfigureScoRequestView request,
ConfigureScoCompleter::Sync& completer) override;
void ResetSco(ResetScoCompleter::Sync& completer) override;
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_bluetooth::Hci> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
// fuchsia_hardware_serialimpl::Device FIDL request handler implementation.
void GetInfo(fdf::Arena& arena, GetInfoCompleter::Sync& completer) override;
void Config(ConfigRequestView request, fdf::Arena& arena,
ConfigCompleter::Sync& completer) override;
void Enable(EnableRequestView request, fdf::Arena& arena,
EnableCompleter::Sync& completer) override;
void Read(fdf::Arena& arena, ReadCompleter::Sync& completer) override;
void Write(WriteRequestView request, fdf::Arena& arena, WriteCompleter::Sync& completer) override;
void CancelAll(fdf::Arena& arena, CancelAllCompleter::Sync& completer) override;
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_serialimpl::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
private:
// HCI UART packet indicators
enum BtHciPacketIndicator : uint8_t {
kHciNone = 0,
kHciCommand = 1,
kHciAclData = 2,
kHciSco = 3,
kHciEvent = 4,
};
struct HciWriteCtx {
BtTransportUart* ctx;
// Owned.
uint8_t* buffer;
};
// This wrapper around async_wait enables us to get a BtTransportUart* in the handler.
// We use this instead of async::WaitMethod because async::WaitBase isn't thread safe.
struct Wait : public async_wait {
explicit Wait(BtTransportUart* uart, zx::channel* channel);
static void Handler(async_dispatcher_t* dispatcher, async_wait_t* async_wait,
zx_status_t status, const zx_packet_signal_t* signal);
BtTransportUart* uart;
// Indicates whether a wait has begun and not ended.
bool pending = false;
// The channel that this wait waits on.
zx::channel* channel;
};
// Returns length of current event packet being received
// Must only be called in the read callback (HciHandleUartReadEvents).
size_t EventPacketLength();
// Returns length of current ACL data packet being received
// Must only be called in the read callback (HciHandleUartReadEvents).
size_t AclPacketLength();
// Returns length of current SCO data packet being received
// Must only be called in the read callback (HciHandleUartReadEvents).
size_t ScoPacketLength();
void ChannelCleanupLocked(zx::channel* channel) __TA_REQUIRES(mutex_);
void SnoopChannelWriteLocked(uint8_t flags, uint8_t* bytes, size_t length) __TA_REQUIRES(mutex_);
void HciBeginShutdown() __TA_EXCLUDES(mutex_);
void SerialWrite(uint8_t* buffer, size_t length) __TA_EXCLUDES(mutex_);
void HciHandleClientChannel(zx::channel* chan, zx_signals_t pending) __TA_EXCLUDES(mutex_);
// Queues a read callback for async serial on the dispatcher.
void QueueUartRead();
void HciHandleUartReadEvents(const uint8_t* buf, size_t length) __TA_EXCLUDES(mutex_);
// Reads the next packet chunk from |uart_src| into |buffer| and increments |buffer_offset| and
// |uart_src| by the number of bytes read. If a complete packet is read, it will be written to
// |channel|.
using PacketLengthFunction = size_t (BtTransportUart::*)();
void ProcessNextUartPacketFromReadBuffer(uint8_t* buffer, size_t buffer_size,
size_t* buffer_offset, const uint8_t** uart_src,
const uint8_t* uart_end,
PacketLengthFunction get_packet_length,
zx::channel* channel, bt_hci_snoop_type_t snoop_type);
void HciReadComplete(zx_status_t status, const uint8_t* buffer, size_t length)
__TA_EXCLUDES(mutex_);
void HciWriteComplete(zx_status_t status) __TA_EXCLUDES(mutex_);
static int HciThread(void* arg) __TA_EXCLUDES(mutex_);
void OnChannelSignal(Wait* wait, zx_status_t status, const zx_packet_signal_t* signal);
zx_status_t HciOpenChannel(zx::channel* in_channel, zx_handle_t in) __TA_EXCLUDES(mutex_);
zx_status_t ServeProtocols();
// Adds the device.
zx_status_t Bind() __TA_EXCLUDES(mutex_);
// 1 byte packet indicator + 3 byte header + payload
static constexpr uint32_t kCmdBufSize = 255 + 4;
// The number of currently supported HCI channel endpoints. We currently have
// one channel for command/event flow and one for ACL data flow. The sniff channel is managed
// separately.
static constexpr uint8_t kNumChannels = 2;
// add one for the wakeup event
static constexpr uint8_t kNumWaitItems = kNumChannels + 1;
// The maximum HCI ACL frame size used for data transactions
// (1024 + 4 bytes for the ACL header + 1 byte packet indicator)
static constexpr uint32_t kAclMaxFrameSize = 1029;
// The maximum HCI SCO frame size used for data transactions.
// (255 byte payload + 3 bytes for the SCO header + 1 byte packet indicator)
static constexpr uint32_t kScoMaxFrameSize = 259;
// 1 byte packet indicator + 2 byte header + payload
static constexpr uint32_t kEventBufSize = 255 + 3;
fdf::WireClient<fuchsia_hardware_serialimpl::Device> serial_client_;
zx::channel cmd_channel_ __TA_GUARDED(mutex_);
Wait cmd_channel_wait_ __TA_GUARDED(mutex_){this, &cmd_channel_};
zx::channel acl_channel_ __TA_GUARDED(mutex_);
Wait acl_channel_wait_ __TA_GUARDED(mutex_){this, &acl_channel_};
zx::channel sco_channel_ __TA_GUARDED(mutex_);
Wait sco_channel_wait_ __TA_GUARDED(mutex_){this, &sco_channel_};
zx::channel snoop_channel_ __TA_GUARDED(mutex_);
std::atomic_bool shutting_down_ = false;
// True if there is not a UART write pending. Set to false when a write is initiated, and set to
// true when the write completes.
bool can_write_ __TA_GUARDED(mutex_) = true;
// type of current packet being read from the UART
// Must only be used in the UART read callback (HciHandleUartReadEvents).
BtHciPacketIndicator cur_uart_packet_type_ = kHciNone;
// for accumulating HCI events
// Must only be used in the UART read callback (HciHandleUartReadEvents).
uint8_t event_buffer_[kEventBufSize];
// Must only be used in the UART read callback (HciHandleUartReadEvents).
size_t event_buffer_offset_ = 0;
// for accumulating ACL data packets
// Must only be used in the UART read callback (HciHandleUartReadEvents).
uint8_t acl_buffer_[kAclMaxFrameSize];
// Must only be used in the UART read callback (HciHandleUartReadEvents).
size_t acl_buffer_offset_ = 0;
// For accumulating SCO packets
// Must only be used in the UART read callback (HciHandleUartReadEvents).
uint8_t sco_buffer_[kScoMaxFrameSize];
// Must only be used in the UART read callback (HciHandleUartReadEvents).
size_t sco_buffer_offset_ = 0;
// for sending outbound packets to the UART
// kAclMaxFrameSize is the largest frame size sent.
uint8_t write_buffer_[kAclMaxFrameSize] __TA_GUARDED(mutex_);
// Save the serial device pid for vendor drivers to fetch.
uint32_t serial_pid_ = 0;
std::mutex mutex_;
std::optional<async::Loop> loop_;
// In production, this is loop_.dispatcher(). In tests, this is the test dispatcher.
async_dispatcher_t* dispatcher_ = nullptr;
fidl::WireClient<fuchsia_driver_framework::Node> node_;
fidl::WireClient<fuchsia_driver_framework::NodeController> node_controller_;
// The task which runs to queue a uart read.
async::TaskClosureMethod<BtTransportUart, &BtTransportUart::QueueUartRead> queue_read_task_{this};
compat::SyncInitializedDeviceServer compat_server_;
};
} // namespace bt_transport_uart
#endif // SRC_CONNECTIVITY_BLUETOOTH_HCI_TRANSPORT_UART_BT_TRANSPORT_UART_H_