blob: 0c96ea716fe5868b4fade9910d8f5ae9d1753d7d [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_TESTING_FAKE_CONTROLLER_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_TESTING_FAKE_CONTROLLER_H_
#include <memory>
#include <unordered_map>
#include <vector>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <lib/async/default.h>
#include <lib/fit/function.h>
#include <lib/zx/channel.h>
#include "garnet/drivers/bluetooth/lib/common/device_address.h"
#include "garnet/drivers/bluetooth/lib/hci/connection_parameters.h"
#include "garnet/drivers/bluetooth/lib/hci/hci.h"
#include "garnet/drivers/bluetooth/lib/hci/hci_constants.h"
#include "garnet/drivers/bluetooth/lib/l2cap/l2cap.h"
#include "garnet/drivers/bluetooth/lib/testing/fake_controller_base.h"
#include "lib/fxl/functional/cancelable_callback.h"
#include "lib/fxl/macros.h"
namespace btlib {
namespace testing {
class FakeDevice;
// FakeController emulates a real Bluetooth controller. It can be configured to
// respond to HCI commands in a predictable manner.
class FakeController : public FakeControllerBase,
public fbl::RefCounted<FakeController> {
public:
// Global settings for the FakeController. These can be used to initialize a
// FakeController and/or to re-configure an existing one.
struct Settings final {
// The default constructor initializes all fields to 0, unless another
// default is specified below.
Settings();
~Settings() = default;
void ApplyDualModeDefaults();
void ApplyLEOnlyDefaults();
void ApplyLegacyLEConfig();
void ApplyLEConfig();
void AddBREDRSupportedCommands();
void AddLESupportedCommands();
// HCI settings.
hci::HCIVersion hci_version; // Default: HCIVersion::k5_0.
uint8_t num_hci_command_packets; // Default: 1
uint64_t event_mask;
uint64_t le_event_mask;
// BD_ADDR (BR/EDR) or Public Device Address (LE)
common::DeviceAddress bd_addr;
// Local supported features and commands.
uint64_t lmp_features_page0;
uint64_t lmp_features_page1;
uint64_t lmp_features_page2;
uint64_t le_features;
uint64_t le_supported_states;
uint8_t supported_commands[64];
// Buffer Size.
uint16_t acl_data_packet_length;
uint8_t total_num_acl_data_packets;
uint16_t le_acl_data_packet_length;
uint8_t le_total_num_acl_data_packets;
};
// Current device low energy scan state.
struct LEScanState final {
LEScanState();
bool enabled;
hci::LEScanType scan_type;
uint16_t scan_interval;
uint16_t scan_window;
bool filter_duplicates;
hci::LEOwnAddressType own_address_type;
hci::LEScanFilterPolicy filter_policy;
};
// Current device basic advertising state
struct LEAdvertisingState final {
LEAdvertisingState();
common::BufferView advertised_view() const {
return common::BufferView(data, data_length);
}
common::BufferView scan_rsp_view() const {
return common::BufferView(scan_rsp_data, scan_rsp_length);
}
bool enabled;
hci::LEAdvertisingType adv_type;
uint16_t interval;
uint8_t data_length;
uint8_t data[hci::kMaxLEAdvertisingDataLength];
uint8_t scan_rsp_length;
uint8_t scan_rsp_data[hci::kMaxLEAdvertisingDataLength];
};
// Constructor initializes the controller with the minimal default settings
// (equivalent to calling Settings::ApplyDefaults()).
FakeController();
~FakeController() override;
// Resets the controller settings.
void set_settings(const Settings& settings) { settings_ = settings; }
// Tells the FakeController to always respond to the given command opcode with
// the given HCI status code.
void SetDefaultResponseStatus(hci::OpCode opcode, hci::StatusCode status);
void ClearDefaultResponseStatus(hci::OpCode opcode);
// Returns the current LE scan state.
const LEScanState& le_scan_state() const { return le_scan_state_; }
// Returns the current LE advertising state.
const LEAdvertisingState& le_advertising_state() const {
return le_adv_state_;
}
const common::DeviceAddress& le_random_address() const {
return le_random_address_;
}
// Returns the current Local Name.set in the controller
const std::string& local_name() const { return local_name_; }
// Adds a fake remote device.
void AddDevice(std::unique_ptr<FakeDevice> device);
// Sets a callback to be invoked when the scan state changes.
using ScanStateCallback = fit::function<void(bool enabled)>;
void SetScanStateCallback(ScanStateCallback callback,
async_dispatcher_t* dispatcher);
// Sets a callback to be invoked when the LE Advertising state changes.
void SetAdvertisingStateCallback(fit::closure callback,
async_dispatcher_t* dispatcher);
// Sets a callback to be invoked on connection events.
using ConnectionStateCallback = fit::function<
void(const common::DeviceAddress&, bool connected, bool canceled)>;
void SetConnectionStateCallback(ConnectionStateCallback callback,
async_dispatcher_t* dispatcher);
// Sets a callback to be invoked when LE connection parameters are updated for
// a fake device.
using LEConnectionParametersCallback =
fit::function<void(const common::DeviceAddress&,
const hci::LEConnectionParameters&)>;
void SetLEConnectionParametersCallback(
LEConnectionParametersCallback callback,
async_dispatcher_t* dispatcher);
// Sends a HCI event with the given parameters.
void SendEvent(hci::EventCode event_code, const common::ByteBuffer& payload);
// Sends a LE Meta event with the given parameters.
void SendLEMetaEvent(hci::EventCode subevent_code,
const common::ByteBuffer& payload);
// Sends an ACL data packet with the given parameters.
void SendACLPacket(hci::ConnectionHandle handle,
const common::ByteBuffer& payload);
// Sends a L2CAP basic frame.
void SendL2CAPBFrame(hci::ConnectionHandle handle,
l2cap::ChannelId channel_id,
const common::ByteBuffer& payload);
// Sends a L2CAP control frame over a signaling channel. If |is_le| is true,
// then the LE signaling channel will be used.
void SendL2CAPCFrame(hci::ConnectionHandle handle,
bool is_le,
l2cap::CommandCode code,
uint8_t id,
const common::ByteBuffer& payload);
void SendNumberOfCompletedPacketsEvent(hci::ConnectionHandle conn,
uint16_t num);
// Sets up a LE link to the device with the given |addr|. FakeController will
// report a connection event in which it is in the given |role|.
void ConnectLowEnergy(const common::DeviceAddress& addr,
hci::ConnectionRole role = hci::ConnectionRole::kSlave);
// Tells a fake device to initiate the L2CAP Connection Parameter Update
// procedure using the given |params|. Has no effect if a connected fake
// device with the given |addr| is not found.
void L2CAPConnectionParameterUpdate(
const common::DeviceAddress& addr,
const hci::LEPreferredConnectionParameters& params);
// Marks the FakeDevice with address |address| as disconnected and sends a HCI
// Disconnection Complete event for all of its links.
void Disconnect(const common::DeviceAddress& addr);
private:
// Returns the current thread's task dispatcher.
async_dispatcher_t* dispatcher() const {
return async_get_default_dispatcher();
}
// Finds and returns the FakeDevice with the given parameters or nullptr if no
// such device exists.
FakeDevice* FindDeviceByAddress(const common::DeviceAddress& addr);
FakeDevice* FindDeviceByConnHandle(hci::ConnectionHandle handle);
// Returns the next available L2CAP signaling channel command ID.
uint8_t NextL2CAPCommandId();
// Sends a HCI_Command_Complete event in response to the command with |opcode|
// and using the given data as the parameter payload.
void RespondWithCommandComplete(hci::OpCode opcode,
const common::ByteBuffer& params);
// Sends a HCI_Command_Complete event with "Success" status in response to the
// command with |opcode|.
void RespondWithSuccess(hci::OpCode opcode);
// Sends a HCI_Command_Status event in response to the command with |opcode|
// and using the given data as the parameter payload.
void RespondWithCommandStatus(hci::OpCode opcode, hci::StatusCode status);
// If a default status has been configured for the given opcode, sends back an
// error response and returns true. Returns false if no response was set.
bool MaybeRespondWithDefaultStatus(hci::OpCode opcode);
// Sends Inquiry Response reports for known BR/EDR devices.
void SendInquiryResponses();
// Sends LE advertising reports for known devices with advertising data, if a
// scan is currently enabled.
void SendAdvertisingReports();
// Notifies |advertising_state_cb_|
void NotifyAdvertisingState();
// Notifies |conn_state_cb_| with the given parameters.
void NotifyConnectionState(const common::DeviceAddress& addr,
bool connected,
bool canceled = false);
// Notifies |le_conn_params_cb_|
void NotifyLEConnectionParameters(const common::DeviceAddress& addr,
const hci::LEConnectionParameters& params);
// Called when a HCI_LE_Create_Connection command is received.
void OnLECreateConnectionCommandReceived(
const hci::LECreateConnectionCommandParams& params);
// Called when a HCI_LE_Connection_Update command is received.
void OnLEConnectionUpdateCommandReceived(
const hci::LEConnectionUpdateCommandParams& params);
// Called when a HCI_Disconnect command is received.
void OnDisconnectCommandReceived(const hci::DisconnectCommandParams& params);
// FakeControllerBase overrides:
void OnCommandPacketReceived(
const common::PacketView<hci::CommandHeader>& command_packet) override;
void OnACLDataPacketReceived(
const common::ByteBuffer& acl_data_packet) override;
Settings settings_;
LEScanState le_scan_state_;
LEAdvertisingState le_adv_state_;
// Used for Advertising, Create Connection, and Active Scanning
// Set by HCI_LE_Set_Random_Address
common::DeviceAddress le_random_address_;
// Used for BR/EDR Scans
uint8_t bredr_scan_state_;
hci::PageScanType page_scan_type_;
uint16_t page_scan_interval_;
uint16_t page_scan_window_;
// The GAP local name, as written/read by HCI_(Read/Write)_Local_Name
std::string local_name_;
// Variables used for
// HCI_LE_Create_Connection/HCI_LE_Create_Connection_Cancel.
uint16_t next_conn_handle_;
fxl::CancelableClosure pending_le_connect_rsp_;
common::DeviceAddress pending_le_connect_addr_;
bool le_connect_pending_;
// ID used for L2CAP LE signaling channel commands.
uint8_t next_le_sig_id_;
// The Inquiry Mode that the controller is in. Determines what types of
// events are faked when a kInquiry is started.
hci::InquiryMode inquiry_mode_;
// The number of results left in Inquiry Mode operation.
// If negative, no limit has been set.
int16_t inquiry_num_responses_left_;
// Used to setup default status responses (for simulating errors)
std::unordered_map<hci::OpCode, hci::StatusCode> default_status_map_;
// The set of fake devices that are visible.
std::vector<std::unique_ptr<FakeDevice>> devices_;
ScanStateCallback scan_state_cb_;
async_dispatcher_t* scan_state_cb_dispatcher_;
fit::closure advertising_state_cb_;
async_dispatcher_t* advertising_state_cb_dispatcher_;
ConnectionStateCallback conn_state_cb_;
async_dispatcher_t* conn_state_cb_dispatcher_;
LEConnectionParametersCallback le_conn_params_cb_;
async_dispatcher_t* le_conn_params_cb_dispatcher_;
FXL_DISALLOW_COPY_AND_ASSIGN(FakeController);
};
} // namespace testing
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_TESTING_FAKE_CONTROLLER_H_