blob: 688bc73f78b88af98b270b9954e227cb430af882 [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.
#ifndef GARNET_DRIVERS_BLUETOOTH_LIB_HCI_TRANSPORT_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_HCI_TRANSPORT_H_
#include <atomic>
#include <memory>
#include <thread>
#include <lib/async/cpp/wait.h>
#include <lib/async/dispatcher.h>
#include <lib/async-loop/cpp/loop.h>
#include "garnet/drivers/bluetooth/lib/hci/acl_data_channel.h"
#include "garnet/drivers/bluetooth/lib/hci/command_channel.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_counted.h"
#include "lib/fxl/memory/ref_ptr.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "lib/fxl/synchronization/thread_checker.h"
namespace btlib {
namespace hci {
class DeviceWrapper;
// Represents the HCI transport layer. This object owns the HCI command, ACL,
// and SCO channels and provides the necessary control-flow mechanisms to send
// and receive HCI packets from the underlying Bluetooth controller.
//
// Transport expects to be initialized and shut down (via Initialize() and
// ShutDown()) on the same thread. ShutDown() MUST be called to guarantee clean
// up.
//
// TODO(armansito): This object has become too heavy-weight. I think it will be
// cleaner to have CommandChannel and ACLDataChannel each be owned directly by
// the main and L2CAP domains. Transport should go away as part of the HCI layer
// clean up (and also NET-388).
class Transport final : public fxl::RefCountedThreadSafe<Transport> {
public:
static fxl::RefPtr<Transport> Create(
std::unique_ptr<DeviceWrapper> hci_device);
// Initializes the HCI command channel and starts the I/O event loop.
// I/O events are run on the dispatcher given, or a new I/O thread
// is started if one is not given.
//
// The ACLDataChannel will be left uninitialized. The ACLDataChannel must be
// initialized after available data buffer information has been obtained from
// the controller (via HCI_Read_Buffer_Size and HCI_LE_Read_Buffer_Size).
//
// This method is NOT thread-safe! Care must be taken such that the public
// methods of this class and those of the individual channel classes are not
// called in a manner that would race with the execution of Initialize().
bool Initialize(async_dispatcher_t* dispatcher = nullptr);
// Initializes the ACL data channel with the given parameters. Returns false
// if an error occurs during initialization. Initialize() must have been
// called successfully prior to calling this method.
bool InitializeACLDataChannel(const DataBufferInfo& bredr_buffer_info,
const DataBufferInfo& le_buffer_info);
// Cleans up all transport channels, stops the I/O event loop, and joins the
// I/O thread. Once a Transport has been shut down, it cannot be
// re-initialized.
//
// NOTE: Care must be taken such that this method is not called from a thread
// that would race with a call to Initialize(). ShutDown() is not thread-safe;
// Initialize(), InitializeACLDataChannel(), and ShutDown() MUST be called on
// the same thread.
void ShutDown();
// Returns true if this Transport has been fully initialized and running.
bool IsInitialized() const;
// Returns a pointer to the HCI command and event flow control handler.
CommandChannel* command_channel() const { return command_channel_.get(); }
// Returns a pointer to the HCI ACL data flow control handler.
ACLDataChannel* acl_data_channel() const { return acl_data_channel_.get(); }
// Returns the I/O thread dispatcher. If this is called when this Transport
// instance is not initialized, the return value will be nullptr.
async_dispatcher_t* io_dispatcher() const {
return io_dispatcher_;
}
// Set a callback that should be invoked when any one of the underlying
// channels gets closed for any reason (e.g. the HCI device has disappeared)
// and the dispatcher on which the callback should be posted.
//
// When this callback is called the channels will be in an invalid state and
// packet processing is no longer guaranteed to work. It is the responsibility
// of the callback implementation to clean up this Transport instance by
// calling ShutDown() and/or deleting it.
void SetTransportClosedCallback(fit::closure callback,
async_dispatcher_t* dispatcher);
private:
FRIEND_REF_COUNTED_THREAD_SAFE(Transport);
explicit Transport(std::unique_ptr<DeviceWrapper> hci_device);
~Transport();
// Channel closed callback.
void OnChannelClosed(async_dispatcher_t* dispatcher,
async::WaitBase* wait,
zx_status_t status,
const zx_packet_signal_t* signal);
using Waiter = async::WaitMethod<Transport, &Transport::OnChannelClosed>;
// Sets up a wait to watch for |channel| to close and calls OnChannelClosed
void WatchChannelClosed(const zx::channel& channel, Waiter& wait);
// Notifies the closed callback.
void NotifyClosedCallback();
// Used to assert that certain public functions are only called on the
// creation thread.
fxl::ThreadChecker thread_checker_;
// The Bluetooth HCI device file descriptor.
std::unique_ptr<DeviceWrapper> hci_device_;
// The state of the initialization sequence.
std::atomic_bool is_initialized_;
// The loop that performs all HCI I/O operations. This is initialized with its
// own separate thread.
std::unique_ptr<async::Loop> io_loop_;
// async::Waits for the command and ACL channels
Waiter cmd_channel_wait_{this};
Waiter acl_channel_wait_{this};
// The dispatcher used for posting tasks on the HCI transport I/O thread.
async_dispatcher_t* io_dispatcher_;
// The ACL data flow control handler.
std::unique_ptr<ACLDataChannel> acl_data_channel_;
// The HCI command and event flow control handler.
std::unique_ptr<CommandChannel> command_channel_;
// Callback invoked when the transport is closed (due to a channel error) and
// its dispatcher.
fit::closure closed_cb_;
async_dispatcher_t* closed_cb_dispatcher_;
FXL_DISALLOW_COPY_AND_ASSIGN(Transport);
};
} // namespace hci
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_HCI_TRANSPORT_H_