blob: 44e2a543424be1a9569958f147a885a0229f2f98 [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 "fake_controller.h"
#include <endian.h>
#include <lib/async/cpp/task.h>
#include "garnet/drivers/bluetooth/lib/common/log.h"
#include "garnet/drivers/bluetooth/lib/common/packet_view.h"
#include "garnet/drivers/bluetooth/lib/hci/defaults.h"
#include "garnet/drivers/bluetooth/lib/hci/hci.h"
#include "garnet/drivers/bluetooth/lib/hci/util.h"
#include "garnet/drivers/bluetooth/lib/testing/fake_device.h"
#include "lib/fxl/strings/string_printf.h"
namespace btlib {
using common::BufferView;
using common::ByteBuffer;
using common::StaticByteBuffer;
namespace testing {
namespace {
template <typename NUM_TYPE, typename ENUM_TYPE>
void SetBit(NUM_TYPE* num_type, ENUM_TYPE bit) {
*num_type |= static_cast<NUM_TYPE>(bit);
}
template <typename NUM_TYPE, typename ENUM_TYPE>
void UnsetBit(NUM_TYPE* num_type, ENUM_TYPE bit) {
*num_type &= ~static_cast<NUM_TYPE>(bit);
}
template <typename NUM_TYPE, typename ENUM_TYPE>
bool CheckBit(NUM_TYPE num_type, ENUM_TYPE bit) {
return (num_type & static_cast<NUM_TYPE>(bit));
}
hci::LEPeerAddressType ToPeerAddrType(common::DeviceAddress::Type type) {
hci::LEPeerAddressType result = hci::LEPeerAddressType::kAnonymous;
switch (type) {
case common::DeviceAddress::Type::kLEPublic:
result = hci::LEPeerAddressType::kPublic;
break;
case common::DeviceAddress::Type::kLERandom:
result = hci::LEPeerAddressType::kRandom;
break;
default:
break;
}
return result;
}
} // namespace
FakeController::Settings::Settings() {
std::memset(this, 0, sizeof(*this));
hci_version = hci::HCIVersion::k5_0;
num_hci_command_packets = 250;
}
void FakeController::Settings::ApplyDualModeDefaults() {
std::memset(this, 0, sizeof(*this));
hci_version = hci::HCIVersion::k5_0;
num_hci_command_packets = 250;
acl_data_packet_length = 512;
total_num_acl_data_packets = 1;
le_acl_data_packet_length = 512;
le_total_num_acl_data_packets = 1;
SetBit(&lmp_features_page0, hci::LMPFeature::kLESupported);
SetBit(&lmp_features_page0, hci::LMPFeature::kSimultaneousLEAndBREDR);
SetBit(&lmp_features_page0, hci::LMPFeature::kExtendedFeatures);
SetBit(&lmp_features_page0, hci::LMPFeature::kRSSIwithInquiryResults);
SetBit(&lmp_features_page0, hci::LMPFeature::kExtendedInquiryResponse);
AddBREDRSupportedCommands();
AddLESupportedCommands();
}
void FakeController::Settings::ApplyLEOnlyDefaults() {
ApplyDualModeDefaults();
UnsetBit(&lmp_features_page0, hci::LMPFeature::kSimultaneousLEAndBREDR);
SetBit(&lmp_features_page0, hci::LMPFeature::kBREDRNotSupported);
std::memset(supported_commands, 0, sizeof(supported_commands));
AddLESupportedCommands();
}
void FakeController::Settings::AddBREDRSupportedCommands() {
SetBit(supported_commands + 7, hci::SupportedCommand::kWriteLocalName);
SetBit(supported_commands + 7, hci::SupportedCommand::kReadLocalName);
SetBit(supported_commands + 7, hci::SupportedCommand::kReadScanEnable);
SetBit(supported_commands + 7, hci::SupportedCommand::kWriteScanEnable);
SetBit(supported_commands + 8, hci::SupportedCommand::kReadPageScanActivity);
SetBit(supported_commands + 8, hci::SupportedCommand::kWritePageScanActivity);
SetBit(supported_commands + 12, hci::SupportedCommand::kReadInquiryMode);
SetBit(supported_commands + 12, hci::SupportedCommand::kWriteInquiryMode);
SetBit(supported_commands + 13, hci::SupportedCommand::kReadPageScanType);
SetBit(supported_commands + 13, hci::SupportedCommand::kWritePageScanType);
SetBit(supported_commands + 14, hci::SupportedCommand::kReadBufferSize);
SetBit(supported_commands + 17,
hci::SupportedCommand::kReadSimplePairingMode);
SetBit(supported_commands + 17,
hci::SupportedCommand::kWriteSimplePairingMode);
}
void FakeController::Settings::AddLESupportedCommands() {
SetBit(supported_commands, hci::SupportedCommand::kDisconnect);
SetBit(supported_commands + 5, hci::SupportedCommand::kSetEventMask);
SetBit(supported_commands + 5, hci::SupportedCommand::kReset);
SetBit(supported_commands + 14,
hci::SupportedCommand::kReadLocalVersionInformation);
SetBit(supported_commands + 14,
hci::SupportedCommand::kReadLocalSupportedFeatures);
SetBit(supported_commands + 14,
hci::SupportedCommand::kReadLocalExtendedFeatures);
SetBit(supported_commands + 24, hci::SupportedCommand::kWriteLEHostSupport);
SetBit(supported_commands + 25, hci::SupportedCommand::kLESetEventMask);
SetBit(supported_commands + 25, hci::SupportedCommand::kLEReadBufferSize);
SetBit(supported_commands + 25,
hci::SupportedCommand::kLEReadLocalSupportedFeatures);
SetBit(supported_commands + 25, hci::SupportedCommand::kLESetRandomAddress);
SetBit(supported_commands + 25,
hci::SupportedCommand::kLESetAdvertisingParameters);
SetBit(supported_commands + 25, hci::SupportedCommand::kLESetAdvertisingData);
SetBit(supported_commands + 26,
hci::SupportedCommand::kLESetScanResponseData);
SetBit(supported_commands + 26,
hci::SupportedCommand::kLESetAdvertisingEnable);
SetBit(supported_commands + 26, hci::SupportedCommand::kLECreateConnection);
SetBit(supported_commands + 26,
hci::SupportedCommand::kLECreateConnectionCancel);
SetBit(supported_commands + 27, hci::SupportedCommand::kLEConnectionUpdate);
}
void FakeController::Settings::ApplyLegacyLEConfig() {
ApplyLEOnlyDefaults();
hci_version = hci::HCIVersion::k4_2;
SetBit(supported_commands + 26, hci::SupportedCommand::kLESetScanParameters);
SetBit(supported_commands + 26, hci::SupportedCommand::kLESetScanEnable);
}
void FakeController::Settings::ApplyLEConfig() {
ApplyLEOnlyDefaults();
SetBit(&le_features, hci::LESupportedFeature::kLEExtendedAdvertising);
}
FakeController::LEScanState::LEScanState()
: enabled(false),
scan_type(hci::LEScanType::kPassive),
scan_interval(0),
scan_window(0),
filter_duplicates(false),
filter_policy(hci::LEScanFilterPolicy::kNoWhiteList) {}
FakeController::LEAdvertisingState::LEAdvertisingState()
: enabled(false), interval(0), data_length(0), scan_rsp_length(0) {
std::memset(data, 0, sizeof(data));
std::memset(scan_rsp_data, 0, sizeof(scan_rsp_data));
}
FakeController::FakeController()
: page_scan_type_(hci::PageScanType::kStandardScan),
page_scan_interval_(0x0800),
page_scan_window_(0x0012),
next_conn_handle_(0u),
le_connect_pending_(false),
next_le_sig_id_(1u),
scan_state_cb_dispatcher_(nullptr),
advertising_state_cb_dispatcher_(nullptr),
conn_state_cb_dispatcher_(nullptr),
le_conn_params_cb_dispatcher_(nullptr) {}
FakeController::~FakeController() { Stop(); }
void FakeController::SetDefaultResponseStatus(hci::OpCode opcode,
hci::StatusCode status) {
ZX_DEBUG_ASSERT(status != hci::StatusCode::kSuccess);
default_status_map_[opcode] = status;
}
void FakeController::ClearDefaultResponseStatus(hci::OpCode opcode) {
default_status_map_.erase(opcode);
}
void FakeController::AddDevice(std::unique_ptr<FakeDevice> device) {
device->set_ctrl(this);
devices_.push_back(std::move(device));
}
void FakeController::SetScanStateCallback(ScanStateCallback callback,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(callback);
ZX_DEBUG_ASSERT(dispatcher);
scan_state_cb_ = std::move(callback);
scan_state_cb_dispatcher_ = dispatcher;
}
void FakeController::SetAdvertisingStateCallback(
fit::closure callback, async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(callback);
ZX_DEBUG_ASSERT(dispatcher);
advertising_state_cb_ = std::move(callback);
advertising_state_cb_dispatcher_ = dispatcher;
}
void FakeController::SetConnectionStateCallback(
ConnectionStateCallback callback, async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(callback);
ZX_DEBUG_ASSERT(dispatcher);
conn_state_cb_ = std::move(callback);
conn_state_cb_dispatcher_ = dispatcher;
}
void FakeController::SetLEConnectionParametersCallback(
LEConnectionParametersCallback callback, async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(callback);
ZX_DEBUG_ASSERT(dispatcher);
le_conn_params_cb_ = std::move(callback);
le_conn_params_cb_dispatcher_ = dispatcher;
}
FakeDevice* FakeController::FindDeviceByAddress(
const common::DeviceAddress& addr) {
for (auto& dev : devices_) {
if (dev->address() == addr)
return dev.get();
}
return nullptr;
}
FakeDevice* FakeController::FindDeviceByConnHandle(
hci::ConnectionHandle handle) {
for (auto& dev : devices_) {
if (dev->HasLink(handle))
return dev.get();
}
return nullptr;
}
uint8_t FakeController::NextL2CAPCommandId() {
// TODO(armansito): Guard against overflow?
return next_le_sig_id_++;
}
void FakeController::RespondWithCommandComplete(hci::OpCode opcode,
const ByteBuffer& params) {
common::DynamicByteBuffer buffer(sizeof(hci::CommandCompleteEventParams) +
params.size());
common::MutablePacketView<hci::CommandCompleteEventParams> event(
&buffer, params.size());
event.mutable_header()->num_hci_command_packets =
settings_.num_hci_command_packets;
event.mutable_header()->command_opcode = htole16(opcode);
event.mutable_payload_data().Write(params);
SendEvent(hci::kCommandCompleteEventCode, buffer);
}
void FakeController::RespondWithSuccess(hci::OpCode opcode) {
hci::SimpleReturnParams out_params;
out_params.status = hci::StatusCode::kSuccess;
RespondWithCommandComplete(opcode,
BufferView(&out_params, sizeof(out_params)));
}
void FakeController::RespondWithCommandStatus(hci::OpCode opcode,
hci::StatusCode status) {
common::StaticByteBuffer<sizeof(hci::CommandStatusEventParams)> buffer;
common::MutablePacketView<hci::CommandStatusEventParams> event(&buffer);
event.mutable_header()->status = status;
event.mutable_header()->num_hci_command_packets =
settings_.num_hci_command_packets;
event.mutable_header()->command_opcode = htole16(opcode);
SendEvent(hci::kCommandStatusEventCode, buffer);
}
void FakeController::SendEvent(hci::EventCode event_code,
const ByteBuffer& payload) {
common::DynamicByteBuffer buffer(sizeof(hci::EventHeader) + payload.size());
common::MutablePacketView<hci::EventHeader> event(&buffer, payload.size());
event.mutable_header()->event_code = event_code;
event.mutable_header()->parameter_total_size = payload.size();
event.mutable_payload_data().Write(payload);
SendCommandChannelPacket(buffer);
}
void FakeController::SendLEMetaEvent(hci::EventCode subevent_code,
const ByteBuffer& payload) {
common::DynamicByteBuffer buffer(sizeof(hci::LEMetaEventParams) +
payload.size());
buffer[0] = subevent_code;
buffer.Write(payload, 1);
SendEvent(hci::kLEMetaEventCode, buffer);
}
void FakeController::SendACLPacket(hci::ConnectionHandle handle,
const ByteBuffer& payload) {
ZX_DEBUG_ASSERT(payload.size() <= hci::kMaxACLPayloadSize);
common::DynamicByteBuffer buffer(sizeof(hci::ACLDataHeader) + payload.size());
common::MutablePacketView<hci::ACLDataHeader> acl(&buffer, payload.size());
acl.mutable_header()->handle_and_flags = htole16(handle);
acl.mutable_header()->data_total_length =
htole16(static_cast<uint16_t>(payload.size()));
acl.mutable_payload_data().Write(payload);
SendACLDataChannelPacket(buffer);
}
void FakeController::SendL2CAPBFrame(hci::ConnectionHandle handle,
l2cap::ChannelId channel_id,
const ByteBuffer& payload) {
ZX_DEBUG_ASSERT(payload.size() <=
hci::kMaxACLPayloadSize - sizeof(l2cap::BasicHeader));
common::DynamicByteBuffer buffer(sizeof(l2cap::BasicHeader) + payload.size());
common::MutablePacketView<l2cap::BasicHeader> bframe(&buffer, payload.size());
bframe.mutable_header()->length = htole16(payload.size());
bframe.mutable_header()->channel_id = htole16(channel_id);
bframe.mutable_payload_data().Write(payload);
SendACLPacket(handle, buffer);
}
void FakeController::SendL2CAPCFrame(hci::ConnectionHandle handle, bool is_le,
l2cap::CommandCode code, uint8_t id,
const ByteBuffer& payload) {
common::DynamicByteBuffer buffer(sizeof(l2cap::CommandHeader) +
payload.size());
common::MutablePacketView<l2cap::CommandHeader> cframe(&buffer,
payload.size());
cframe.mutable_header()->code = code;
cframe.mutable_header()->id = id;
cframe.mutable_header()->length = payload.size();
cframe.mutable_payload_data().Write(payload);
SendL2CAPBFrame(
handle, is_le ? l2cap::kLESignalingChannelId : l2cap::kSignalingChannelId,
buffer);
}
void FakeController::SendNumberOfCompletedPacketsEvent(
hci::ConnectionHandle handle, uint16_t num) {
StaticByteBuffer<sizeof(hci::NumberOfCompletedPacketsEventParams) +
sizeof(hci::NumberOfCompletedPacketsEventData)>
buffer;
auto* params = reinterpret_cast<hci::NumberOfCompletedPacketsEventParams*>(
buffer.mutable_data());
params->number_of_handles = 1;
params->data->connection_handle = htole16(handle);
params->data->hc_num_of_completed_packets = htole16(num);
SendEvent(hci::kNumberOfCompletedPacketsEventCode, buffer);
}
void FakeController::ConnectLowEnergy(const common::DeviceAddress& addr,
hci::ConnectionRole role) {
async::PostTask(dispatcher(), [addr, role, this] {
FakeDevice* dev = FindDeviceByAddress(addr);
if (!dev) {
bt_log(WARN, "fake-hci", "no device found with address: %s",
addr.ToString().c_str());
return;
}
// TODO(armansito): Don't worry about managing multiple links per device
// until this supports Bluetooth classic.
if (dev->connected()) {
bt_log(WARN, "fake-hci", "device already connected");
return;
}
dev->set_connected(true);
hci::ConnectionHandle handle = ++next_conn_handle_;
dev->AddLink(handle);
NotifyConnectionState(addr, true);
auto interval_min = hci::defaults::kLEConnectionIntervalMin;
auto interval_max = hci::defaults::kLEConnectionIntervalMax;
hci::LEConnectionParameters conn_params(
interval_min + ((interval_max - interval_min) / 2), 0,
hci::defaults::kLESupervisionTimeout);
dev->set_le_params(conn_params);
hci::LEConnectionCompleteSubeventParams params;
std::memset(&params, 0, sizeof(params));
params.status = hci::StatusCode::kSuccess;
params.peer_address = addr.value();
params.peer_address_type = ToPeerAddrType(addr.type());
params.conn_latency = htole16(conn_params.latency());
params.conn_interval = htole16(conn_params.interval());
params.supervision_timeout = htole16(conn_params.supervision_timeout());
params.role = role;
params.connection_handle = htole16(handle);
SendLEMetaEvent(hci::kLEConnectionCompleteSubeventCode,
BufferView(&params, sizeof(params)));
});
}
void FakeController::L2CAPConnectionParameterUpdate(
const common::DeviceAddress& addr,
const hci::LEPreferredConnectionParameters& params) {
async::PostTask(dispatcher(), [addr, params, this] {
FakeDevice* dev = FindDeviceByAddress(addr);
if (!dev) {
bt_log(WARN, "fake-hci", "no device found with address: %s",
addr.ToString().c_str());
return;
}
if (!dev->connected()) {
bt_log(WARN, "fake-hci", "device not connected");
return;
}
ZX_DEBUG_ASSERT(!dev->logical_links().empty());
l2cap::ConnectionParameterUpdateRequestPayload payload;
payload.interval_min = htole16(params.min_interval());
payload.interval_max = htole16(params.max_interval());
payload.slave_latency = htole16(params.max_latency());
payload.timeout_multiplier = htole16(params.supervision_timeout());
// TODO(armansito): Instead of picking the first handle we should pick
// the handle that matches the current LE-U link.
SendL2CAPCFrame(*dev->logical_links().begin(), true,
l2cap::kConnectionParameterUpdateRequest,
NextL2CAPCommandId(),
BufferView(&payload, sizeof(payload)));
});
}
void FakeController::Disconnect(const common::DeviceAddress& addr) {
async::PostTask(dispatcher(), [addr, this] {
FakeDevice* dev = FindDeviceByAddress(addr);
if (!dev || !dev->connected()) {
bt_log(WARN, "fake-hci", "no connected device found with address: %s",
addr.ToString().c_str());
return;
}
auto links = dev->Disconnect();
ZX_DEBUG_ASSERT(!dev->connected());
ZX_DEBUG_ASSERT(!links.empty());
NotifyConnectionState(addr, false);
for (auto link : links) {
hci::DisconnectionCompleteEventParams params;
params.status = hci::StatusCode::kSuccess;
params.connection_handle = htole16(link);
params.reason = hci::StatusCode::kRemoteUserTerminatedConnection;
SendEvent(hci::kDisconnectionCompleteEventCode,
BufferView(&params, sizeof(params)));
}
});
}
bool FakeController::MaybeRespondWithDefaultStatus(hci::OpCode opcode) {
auto iter = default_status_map_.find(opcode);
if (iter == default_status_map_.end())
return false;
bt_log(INFO, "fake-hci",
"responding with error (command: %#.4x, status: %#.2x)", opcode,
iter->second);
hci::SimpleReturnParams params;
params.status = iter->second;
RespondWithCommandComplete(opcode, BufferView(&params, sizeof(params)));
return true;
}
void FakeController::SendInquiryResponses() {
// TODO(jamuraa): combine some of these into a single response event
for (const auto& device : devices_) {
if (!device->has_inquiry_response()) {
continue;
}
SendCommandChannelPacket(device->CreateInquiryResponseEvent(inquiry_mode_));
inquiry_num_responses_left_--;
if (inquiry_num_responses_left_ == 0) {
break;
}
}
}
void FakeController::SendAdvertisingReports() {
if (!le_scan_state_.enabled || devices_.empty())
return;
for (const auto& device : devices_) {
if (!device->has_advertising_reports()) {
continue;
}
// We want to send scan response packets only during an active scan and if
// the device is scannable.
bool need_scan_rsp =
(le_scan_state().scan_type == hci::LEScanType::kActive) &&
device->scannable();
SendCommandChannelPacket(device->CreateAdvertisingReportEvent(
need_scan_rsp && device->should_batch_reports()));
// If the original report did not include a scan response then we send it as
// a separate event.
if (need_scan_rsp && !device->should_batch_reports()) {
SendCommandChannelPacket(device->CreateScanResponseReportEvent());
}
}
// We'll send new reports for the same devices if duplicate filtering is
// disabled.
if (!le_scan_state_.filter_duplicates) {
async::PostTask(dispatcher(), [this] { SendAdvertisingReports(); });
}
}
void FakeController::NotifyAdvertisingState() {
if (!advertising_state_cb_) {
return;
}
ZX_DEBUG_ASSERT(advertising_state_cb_dispatcher_);
async::PostTask(advertising_state_cb_dispatcher_,
advertising_state_cb_.share());
}
void FakeController::NotifyConnectionState(const common::DeviceAddress& addr,
bool connected, bool canceled) {
if (!conn_state_cb_)
return;
ZX_DEBUG_ASSERT(conn_state_cb_dispatcher_);
async::PostTask(conn_state_cb_dispatcher_,
[addr, connected, canceled, cb = conn_state_cb_.share()] {
cb(addr, connected, canceled);
});
}
void FakeController::NotifyLEConnectionParameters(
const common::DeviceAddress& addr,
const hci::LEConnectionParameters& params) {
if (!le_conn_params_cb_)
return;
ZX_DEBUG_ASSERT(le_conn_params_cb_dispatcher_);
async::PostTask(
le_conn_params_cb_dispatcher_,
[addr, params, cb = le_conn_params_cb_.share()] { cb(addr, params); });
}
void FakeController::OnLECreateConnectionCommandReceived(
const hci::LECreateConnectionCommandParams& params) {
// Cannot issue this command while a request is already pending.
if (le_connect_pending_) {
RespondWithCommandStatus(hci::kLECreateConnection,
hci::StatusCode::kCommandDisallowed);
return;
}
common::DeviceAddress::Type addr_type =
hci::AddressTypeFromHCI(params.peer_address_type);
ZX_DEBUG_ASSERT(addr_type != common::DeviceAddress::Type::kBREDR);
const common::DeviceAddress peer_address(addr_type, params.peer_address);
hci::StatusCode status = hci::StatusCode::kSuccess;
// Find the device that matches the requested address.
FakeDevice* device = FindDeviceByAddress(peer_address);
if (device) {
if (device->connected())
status = hci::StatusCode::kConnectionAlreadyExists;
else
status = device->connect_status();
}
// First send the Command Status response.
RespondWithCommandStatus(hci::kLECreateConnection, status);
// If we just sent back an error status then the operation is complete.
if (status != hci::StatusCode::kSuccess)
return;
le_connect_pending_ = true;
pending_le_connect_addr_ = peer_address;
// The procedure was initiated successfully but the device cannot be connected
// because it either doesn't exist or isn't connectable.
if (!device || !device->connectable()) {
bt_log(INFO, "fake-hci",
"requested fake device cannot be connected; request will time out");
return;
}
if (next_conn_handle_ == 0x0FFF) {
// Ran out of handles
status = hci::StatusCode::kConnectionLimitExceeded;
} else {
status = device->connect_response();
}
hci::LEConnectionCompleteSubeventParams response;
std::memset(&response, 0, sizeof(response));
response.status = status;
response.peer_address = params.peer_address;
response.peer_address_type = ToPeerAddrType(addr_type);
if (status == hci::StatusCode::kSuccess) {
uint16_t interval_min = le16toh(params.conn_interval_min);
uint16_t interval_max = le16toh(params.conn_interval_max);
uint16_t interval = interval_min + ((interval_max - interval_min) / 2);
hci::LEConnectionParameters conn_params(
interval, le16toh(params.conn_latency),
le16toh(params.supervision_timeout));
device->set_le_params(conn_params);
response.conn_latency = params.conn_latency;
response.conn_interval = le16toh(interval);
response.supervision_timeout = params.supervision_timeout;
response.role = hci::ConnectionRole::kMaster;
hci::ConnectionHandle handle = ++next_conn_handle_;
response.connection_handle = htole16(handle);
}
// Don't send a connection event if we were asked to force the request to
// remain pending. This is used by test cases that operate during the pending
// state.
if (device->force_pending_connect())
return;
pending_le_connect_rsp_.Reset([response, device, this] {
le_connect_pending_ = false;
if (response.status == hci::StatusCode::kSuccess) {
bool notify = !device->connected();
device->AddLink(le16toh(response.connection_handle));
if (notify && device->connected())
NotifyConnectionState(device->address(), true);
}
SendLEMetaEvent(hci::kLEConnectionCompleteSubeventCode,
BufferView(&response, sizeof(response)));
});
async::PostTask(dispatcher(),
[cb = pending_le_connect_rsp_.callback()] { cb(); });
}
void FakeController::OnLEConnectionUpdateCommandReceived(
const hci::LEConnectionUpdateCommandParams& params) {
hci::ConnectionHandle handle = le16toh(params.connection_handle);
FakeDevice* device = FindDeviceByConnHandle(handle);
if (!device) {
RespondWithCommandStatus(hci::kLEConnectionUpdate,
hci::StatusCode::kUnknownConnectionId);
return;
}
ZX_DEBUG_ASSERT(device->connected());
uint16_t min_interval = le16toh(params.conn_interval_min);
uint16_t max_interval = le16toh(params.conn_interval_max);
uint16_t max_latency = le16toh(params.conn_latency);
uint16_t supv_timeout = le16toh(params.supervision_timeout);
if (min_interval > max_interval) {
RespondWithCommandStatus(hci::kLEConnectionUpdate,
hci::StatusCode::kInvalidHCICommandParameters);
return;
}
RespondWithCommandStatus(hci::kLEConnectionUpdate, hci::StatusCode::kSuccess);
hci::LEConnectionParameters conn_params(
min_interval + ((max_interval - min_interval) / 2), max_latency,
supv_timeout);
device->set_le_params(conn_params);
hci::LEConnectionUpdateCompleteSubeventParams reply;
reply.status = hci::StatusCode::kSuccess;
reply.connection_handle = params.connection_handle;
reply.conn_interval = htole16(conn_params.interval());
reply.conn_latency = params.conn_latency;
reply.supervision_timeout = params.supervision_timeout;
SendLEMetaEvent(hci::kLEConnectionUpdateCompleteSubeventCode,
BufferView(&reply, sizeof(reply)));
NotifyLEConnectionParameters(device->address(), conn_params);
}
void FakeController::OnDisconnectCommandReceived(
const hci::DisconnectCommandParams& params) {
hci::ConnectionHandle handle = le16toh(params.connection_handle);
// Find the device that matches the disconnected handle.
FakeDevice* device = FindDeviceByConnHandle(handle);
if (!device) {
RespondWithCommandStatus(hci::kDisconnect,
hci::StatusCode::kUnknownConnectionId);
return;
}
ZX_DEBUG_ASSERT(device->connected());
RespondWithCommandStatus(hci::kDisconnect, hci::StatusCode::kSuccess);
bool notify = device->connected();
device->RemoveLink(handle);
if (notify && !device->connected())
NotifyConnectionState(device->address(), false);
hci::DisconnectionCompleteEventParams reply;
reply.status = hci::StatusCode::kSuccess;
reply.connection_handle = params.connection_handle;
reply.reason = hci::StatusCode::kConnectionTerminatedByLocalHost;
SendEvent(hci::kDisconnectionCompleteEventCode,
BufferView(&reply, sizeof(reply)));
}
void FakeController::OnCommandPacketReceived(
const common::PacketView<hci::CommandHeader>& command_packet) {
hci::OpCode opcode = le16toh(command_packet.header().opcode);
if (MaybeRespondWithDefaultStatus(opcode))
return;
// TODO(NET-825): Validate size of payload to be the correct length below.
switch (opcode) {
case hci::kReadLocalVersionInfo: {
hci::ReadLocalVersionInfoReturnParams params;
std::memset(&params, 0, sizeof(params));
params.hci_version = settings_.hci_version;
RespondWithCommandComplete(hci::kReadLocalVersionInfo,
BufferView(&params, sizeof(params)));
break;
}
case hci::kReadLocalSupportedCommands: {
hci::ReadLocalSupportedCommandsReturnParams params;
params.status = hci::StatusCode::kSuccess;
std::memcpy(params.supported_commands, settings_.supported_commands,
sizeof(params.supported_commands));
RespondWithCommandComplete(hci::kReadLocalSupportedCommands,
BufferView(&params, sizeof(params)));
break;
}
case hci::kReadLocalSupportedFeatures: {
hci::ReadLocalSupportedFeaturesReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.lmp_features = htole64(settings_.lmp_features_page0);
RespondWithCommandComplete(hci::kReadLocalSupportedFeatures,
BufferView(&params, sizeof(params)));
break;
}
case hci::kLESetRandomAddress: {
const auto& in_params =
command_packet.payload<hci::LESetRandomAddressCommandParams>();
le_random_address_ = common::DeviceAddress(
common::DeviceAddress::Type::kLERandom, in_params.random_address);
RespondWithSuccess(opcode);
break;
}
case hci::kLESetAdvertisingParameters: {
const auto& in_params =
command_packet
.payload<hci::LESetAdvertisingParametersCommandParams>();
// TODO(jamuraa): when we parse advertising params, return Invalid HCI
// Command Parameters when apporopriate (Vol 2, Part E, 7.8.9 p1259)
if (le_adv_state_.enabled) {
hci::SimpleReturnParams out_params;
out_params.status = hci::StatusCode::kCommandDisallowed;
RespondWithCommandComplete(opcode,
BufferView(&out_params, sizeof(out_params)));
return;
}
uint32_t interval_min = le16toh(in_params.adv_interval_min);
uint32_t interval_max = le16toh(in_params.adv_interval_max);
// Just assign the average for the interval.
le_adv_state_.interval = (interval_min + interval_max) / 2;
le_adv_state_.adv_type = in_params.adv_type;
RespondWithSuccess(opcode);
NotifyAdvertisingState();
break;
}
case hci::kLESetAdvertisingData: {
const auto& in_params =
command_packet.payload<hci::LESetAdvertisingDataCommandParams>();
le_adv_state_.data_length = in_params.adv_data_length;
std::memcpy(le_adv_state_.data, in_params.adv_data,
le_adv_state_.data_length);
RespondWithSuccess(opcode);
NotifyAdvertisingState();
break;
}
case hci::kLESetScanResponseData: {
const auto& in_params =
command_packet.payload<hci::LESetScanResponseDataCommandParams>();
le_adv_state_.scan_rsp_length = in_params.scan_rsp_data_length;
std::memcpy(le_adv_state_.scan_rsp_data, in_params.scan_rsp_data,
le_adv_state_.scan_rsp_length);
RespondWithSuccess(opcode);
NotifyAdvertisingState();
break;
}
case hci::kLESetAdvertisingEnable: {
const auto& in_params =
command_packet.payload<hci::LESetAdvertisingEnableCommandParams>();
le_adv_state_.enabled =
(in_params.advertising_enable == hci::GenericEnableParam::kEnable);
RespondWithSuccess(opcode);
NotifyAdvertisingState();
break;
}
case hci::kReadBDADDR: {
hci::ReadBDADDRReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.bd_addr = settings_.bd_addr.value();
RespondWithCommandComplete(hci::kReadBDADDR,
BufferView(&params, sizeof(params)));
break;
}
case hci::kReadBufferSize: {
hci::ReadBufferSizeReturnParams params;
std::memset(&params, 0, sizeof(params));
params.hc_acl_data_packet_length =
htole16(settings_.acl_data_packet_length);
params.hc_total_num_acl_data_packets =
settings_.total_num_acl_data_packets;
RespondWithCommandComplete(hci::kReadBufferSize,
BufferView(&params, sizeof(params)));
break;
}
case hci::kDisconnect: {
OnDisconnectCommandReceived(
command_packet.payload<hci::DisconnectCommandParams>());
break;
}
case hci::kWriteLocalName: {
const auto& in_params =
command_packet.payload<hci::WriteLocalNameCommandParams>();
local_name_ = std::string(in_params.local_name,
in_params.local_name + hci::kMaxNameLength);
RespondWithSuccess(opcode);
break;
}
case hci::kReadLocalName: {
hci::ReadLocalNameReturnParams params;
params.status = hci::StatusCode::kSuccess;
auto mut_view =
common::MutableBufferView(params.local_name, hci::kMaxNameLength);
mut_view.Write((uint8_t*)(local_name_.c_str()), hci::kMaxNameLength);
RespondWithCommandComplete(hci::kReadLocalName,
BufferView(&params, sizeof(params)));
break;
}
case hci::kReadScanEnable: {
hci::ReadScanEnableReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.scan_enable = bredr_scan_state_;
RespondWithCommandComplete(hci::kReadScanEnable,
BufferView(&params, sizeof(params)));
break;
}
case hci::kWriteScanEnable: {
const auto& in_params =
command_packet.payload<hci::WriteScanEnableCommandParams>();
bredr_scan_state_ = in_params.scan_enable;
RespondWithSuccess(opcode);
break;
}
case hci::kReadPageScanActivity: {
hci::ReadPageScanActivityReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.page_scan_interval = htole16(page_scan_interval_);
params.page_scan_window = htole16(page_scan_window_);
RespondWithCommandComplete(hci::kReadPageScanActivity,
BufferView(&params, sizeof(params)));
break;
}
case hci::kWritePageScanActivity: {
const auto& in_params =
command_packet.payload<hci::WritePageScanActivityCommandParams>();
page_scan_interval_ = letoh16(in_params.page_scan_interval);
page_scan_window_ = letoh16(in_params.page_scan_window);
RespondWithSuccess(opcode);
break;
}
case hci::kReadInquiryMode: {
hci::ReadInquiryModeReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.inquiry_mode = inquiry_mode_;
RespondWithCommandComplete(hci::kReadInquiryMode,
common::BufferView(&params, sizeof(params)));
break;
}
case hci::kWriteInquiryMode: {
const auto& in_params =
command_packet.payload<hci::WriteInquiryModeCommandParams>();
inquiry_mode_ = in_params.inquiry_mode;
RespondWithSuccess(opcode);
break;
};
case hci::kReadPageScanType: {
hci::ReadPageScanTypeReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.page_scan_type = page_scan_type_;
RespondWithCommandComplete(hci::kReadPageScanType,
BufferView(&params, sizeof(params)));
break;
}
case hci::kWritePageScanType: {
const auto& in_params =
command_packet.payload<hci::WritePageScanTypeCommandParams>();
page_scan_type_ = in_params.page_scan_type;
RespondWithSuccess(opcode);
break;
}
case hci::kReadSimplePairingMode: {
hci::ReadSimplePairingModeReturnParams params;
params.status = hci::StatusCode::kSuccess;
if (CheckBit(settings_.lmp_features_page1,
hci::LMPFeature::kSecureSimplePairingHostSupport)) {
params.simple_pairing_mode = hci::GenericEnableParam::kEnable;
} else {
params.simple_pairing_mode = hci::GenericEnableParam::kDisable;
}
RespondWithCommandComplete(hci::kReadSimplePairingMode,
common::BufferView(&params, sizeof(params)));
break;
}
case hci::kWriteSimplePairingMode: {
const auto& in_params =
command_packet.payload<hci::WriteSimplePairingModeCommandParams>();
// "A host shall not set the Simple Pairing Mode to 'disabled'"
// Spec 5.0 Vol 2 Part E Sec 7.3.59
if (in_params.simple_pairing_mode != hci::GenericEnableParam::kEnable) {
hci::SimpleReturnParams params;
params.status = hci::StatusCode::kInvalidHCICommandParameters;
RespondWithCommandComplete(hci::kWriteSimplePairingMode,
common::BufferView(&params, sizeof(params)));
break;
}
SetBit(&settings_.lmp_features_page1,
hci::LMPFeature::kSecureSimplePairingHostSupport);
RespondWithSuccess(opcode);
break;
}
case hci::kLEConnectionUpdate: {
OnLEConnectionUpdateCommandReceived(
command_packet.payload<hci::LEConnectionUpdateCommandParams>());
break;
}
case hci::kLECreateConnection: {
OnLECreateConnectionCommandReceived(
command_packet.payload<hci::LECreateConnectionCommandParams>());
break;
}
case hci::kLECreateConnectionCancel: {
hci::SimpleReturnParams params;
params.status = hci::StatusCode::kSuccess;
if (!le_connect_pending_) {
// No request is currently pending.
params.status = hci::StatusCode::kCommandDisallowed;
RespondWithCommandComplete(hci::kLECreateConnectionCancel,
BufferView(&params, sizeof(params)));
return;
}
le_connect_pending_ = false;
pending_le_connect_rsp_.Cancel();
NotifyConnectionState(pending_le_connect_addr_, false, true);
hci::LEConnectionCompleteSubeventParams response;
std::memset(&response, 0, sizeof(response));
response.status = hci::StatusCode::kUnknownConnectionId;
response.peer_address = pending_le_connect_addr_.value();
response.peer_address_type =
ToPeerAddrType(pending_le_connect_addr_.type());
RespondWithCommandComplete(hci::kLECreateConnectionCancel,
BufferView(&params, sizeof(params)));
SendLEMetaEvent(hci::kLEConnectionCompleteSubeventCode,
BufferView(&response, sizeof(response)));
break;
}
case hci::kLEReadLocalSupportedFeatures: {
hci::LEReadLocalSupportedFeaturesReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.le_features = htole64(settings_.le_features);
RespondWithCommandComplete(hci::kLEReadLocalSupportedFeatures,
BufferView(&params, sizeof(params)));
break;
}
case hci::kLEReadSupportedStates: {
hci::LEReadSupportedStatesReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.le_states = htole64(settings_.le_supported_states);
RespondWithCommandComplete(hci::kLEReadSupportedStates,
BufferView(&params, sizeof(params)));
break;
}
case hci::kLEReadBufferSize: {
hci::LEReadBufferSizeReturnParams params;
params.status = hci::StatusCode::kSuccess;
params.hc_le_acl_data_packet_length =
htole16(settings_.le_acl_data_packet_length);
params.hc_total_num_le_acl_data_packets =
settings_.le_total_num_acl_data_packets;
RespondWithCommandComplete(hci::kLEReadBufferSize,
BufferView(&params, sizeof(params)));
break;
}
case hci::kSetEventMask: {
const auto& in_params =
command_packet.payload<hci::SetEventMaskCommandParams>();
settings_.event_mask = le64toh(in_params.event_mask);
RespondWithSuccess(opcode);
break;
}
case hci::kLESetEventMask: {
const auto& in_params =
command_packet.payload<hci::LESetEventMaskCommandParams>();
settings_.le_event_mask = le64toh(in_params.le_event_mask);
RespondWithSuccess(opcode);
break;
}
case hci::kReadLocalExtendedFeatures: {
const auto& in_params =
command_packet.payload<hci::ReadLocalExtendedFeaturesCommandParams>();
hci::ReadLocalExtendedFeaturesReturnParams out_params;
out_params.page_number = in_params.page_number;
out_params.maximum_page_number = 2;
if (in_params.page_number > 2) {
out_params.status = hci::StatusCode::kInvalidHCICommandParameters;
} else {
out_params.status = hci::StatusCode::kSuccess;
switch (in_params.page_number) {
case 0:
out_params.extended_lmp_features =
htole64(settings_.lmp_features_page0);
break;
case 1:
out_params.extended_lmp_features =
htole64(settings_.lmp_features_page1);
break;
case 2:
out_params.extended_lmp_features =
htole64(settings_.lmp_features_page2);
break;
}
}
RespondWithCommandComplete(hci::kReadLocalExtendedFeatures,
BufferView(&out_params, sizeof(out_params)));
break;
}
case hci::kLESetScanParameters: {
const auto& in_params =
command_packet.payload<hci::LESetScanParametersCommandParams>();
hci::SimpleReturnParams out_params;
if (le_scan_state_.enabled) {
out_params.status = hci::StatusCode::kCommandDisallowed;
} else {
out_params.status = hci::StatusCode::kSuccess;
le_scan_state_.scan_type = in_params.scan_type;
le_scan_state_.scan_interval = le16toh(in_params.scan_interval);
le_scan_state_.scan_window = le16toh(in_params.scan_window);
le_scan_state_.own_address_type = in_params.own_address_type;
le_scan_state_.filter_policy = in_params.filter_policy;
}
RespondWithCommandComplete(opcode,
BufferView(&out_params, sizeof(out_params)));
break;
}
case hci::kLESetScanEnable: {
const auto& in_params =
command_packet.payload<hci::LESetScanEnableCommandParams>();
le_scan_state_.enabled =
(in_params.scanning_enabled == hci::GenericEnableParam::kEnable);
le_scan_state_.filter_duplicates =
(in_params.filter_duplicates == hci::GenericEnableParam::kEnable);
// Post the scan state update before scheduling the HCI Command Complete
// event. This guarantees that single-threaded unit tests receive the scan
// state update BEFORE the HCI command sequence terminates.
if (scan_state_cb_) {
ZX_DEBUG_ASSERT(scan_state_cb_dispatcher_);
async::PostTask(scan_state_cb_dispatcher_,
[cb = scan_state_cb_.share(),
enabled = le_scan_state_.enabled] { cb(enabled); });
}
RespondWithSuccess(opcode);
if (le_scan_state_.enabled)
SendAdvertisingReports();
break;
}
case hci::kInquiry: {
const auto& in_params =
command_packet.payload<hci::InquiryCommandParams>();
if (in_params.lap != hci::kGIAC && in_params.lap != hci::kLIAC) {
RespondWithCommandStatus(opcode, hci::kInvalidHCICommandParameters);
break;
}
if (in_params.inquiry_length == 0x00 ||
in_params.inquiry_length > hci::kInquiryLengthMax) {
RespondWithCommandStatus(opcode, hci::kInvalidHCICommandParameters);
break;
}
inquiry_num_responses_left_ = in_params.num_responses;
if (in_params.num_responses == 0) {
inquiry_num_responses_left_ = -1;
}
RespondWithCommandStatus(opcode, hci::kSuccess);
bt_log(INFO, "fake-hci", "sending inquiry responses..");
SendInquiryResponses();
async::PostDelayedTask(
dispatcher(),
[this] {
hci::InquiryCompleteEventParams params;
params.status = hci::kSuccess;
SendEvent(hci::kInquiryCompleteEventCode,
common::BufferView(&params, sizeof(params)));
},
zx::msec(in_params.inquiry_length * 1280));
break;
}
case hci::kReset: {
// TODO(jamuraa): actually do some resetting of stuff here
RespondWithSuccess(opcode);
break;
}
case hci::kWriteLEHostSupport: {
const auto& in_params =
command_packet.payload<hci::WriteLEHostSupportCommandParams>();
if (in_params.le_supported_host == hci::GenericEnableParam::kEnable) {
SetBit(&settings_.lmp_features_page1,
hci::LMPFeature::kLESupportedHost);
} else {
UnsetBit(&settings_.lmp_features_page1,
hci::LMPFeature::kLESupportedHost);
}
RespondWithSuccess(opcode);
break;
}
default: {
hci::SimpleReturnParams params;
params.status = hci::StatusCode::kUnknownCommand;
RespondWithCommandComplete(opcode, BufferView(&params, sizeof(params)));
break;
}
}
}
void FakeController::OnACLDataPacketReceived(
const ByteBuffer& acl_data_packet) {
if (acl_data_packet.size() < sizeof(hci::ACLDataHeader)) {
bt_log(WARN, "fake-hci", "malformed ACL packet!");
return;
}
const auto& header = acl_data_packet.As<hci::ACLDataHeader>();
hci::ConnectionHandle handle = le16toh(header.handle_and_flags) & 0x0FFFF;
FakeDevice* dev = FindDeviceByConnHandle(handle);
if (!dev) {
bt_log(WARN, "fake-hci", "ACL data received for unknown handle!");
return;
}
SendNumberOfCompletedPacketsEvent(handle, 1);
dev->OnRxL2CAP(handle, acl_data_packet.view(sizeof(hci::ACLDataHeader)));
}
} // namespace testing
} // namespace btlib