blob: 4906b83007e9990bd6e15bac77d121a361cb0a78 [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 "vendor_hci.h"
#include "logging.h"
#include <fbl/algorithm.h>
#include <lib/zx/time.h>
#include <zircon/status.h>
namespace btintel {
using ::btlib::hci::CommandPacket;
namespace {
constexpr size_t kMaxSecureSendArgLen = 252;
} // namespace
VendorHci::VendorHci(zx::channel* channel)
: channel_(channel), manufacturer_(false){};
ReadVersionReturnParams VendorHci::SendReadVersion() const {
auto packet = CommandPacket::New(kReadVersion);
SendCommand(packet->view());
auto evt_packet = ReadEventPacket();
if (evt_packet) {
auto params = evt_packet->return_params<ReadVersionReturnParams>();
if (params)
return *params;
}
errorf("VendorHci: ReadVersion: Error reading response!\n");
return ReadVersionReturnParams{.status =
btlib::hci::StatusCode::kUnspecifiedError};
}
ReadBootParamsReturnParams VendorHci::SendReadBootParams() const {
auto packet = CommandPacket::New(kReadBootParams);
SendCommand(packet->view());
auto evt_packet = ReadEventPacket();
if (evt_packet) {
auto params = evt_packet->return_params<ReadBootParamsReturnParams>();
if (params)
return *params;
}
errorf("VendorHci: ReadBootParams: Error reading response!\n");
return ReadBootParamsReturnParams{
.status = btlib::hci::StatusCode::kUnspecifiedError};
}
void VendorHci::SendReset() const {
auto packet = CommandPacket::New(kReset, sizeof(ResetCommandParams));
auto params = packet->mutable_view()->mutable_payload<ResetCommandParams>();
params->data[0] = 0x00;
params->data[1] = 0x01;
params->data[2] = 0x00;
params->data[3] = 0x01;
params->data[4] = 0x00;
params->data[5] = 0x08;
params->data[6] = 0x04;
params->data[7] = 0x00;
SendCommand(packet->view());
// Don't expect a return here.
}
bool VendorHci::SendSecureSend(uint8_t type,
const btlib::common::BufferView& bytes) const {
size_t left = bytes.size();
while (left > 0) {
size_t frag_len = fbl::min(left, kMaxSecureSendArgLen);
auto cmd = CommandPacket::New(kSecureSend, frag_len + 1);
auto data = cmd->mutable_view()->mutable_payload_data();
data[0] = type;
data.Write(bytes.view(bytes.size() - left, frag_len), 1);
SendCommand(cmd->view());
auto event = ReadEventPacket();
if (!event) {
errorf("VendorHci: SecureSend: Error reading response!\n");
return false;
}
if (event->event_code() == btlib::hci::kCommandCompleteEventCode) {
const auto& event_params =
event->view()
.template payload<btlib::hci::CommandCompleteEventParams>();
if (le16toh(event_params.command_opcode) != kSecureSend) {
errorf("VendorHci: Received command complete for something else!\n");
} else if (event_params.return_parameters[0] != 0x00) {
errorf(
"VendorHci: Received 0x%x instead of zero in command complete!\n",
event_params.return_parameters[0]);
return false;
}
} else if (event->event_code() == btlib::hci::kVendorDebugEventCode) {
const auto& params =
event->view().template payload<SecureSendEventParams>();
infof("VendorHci: SecureSend result 0x%x, opcode: 0x%x, status: 0x%x\n",
params.result, params.opcode, params.status);
if (params.result) {
errorf("VendorHci: Result of %d indicates some error!\n",
params.result);
return false;
}
}
left -= frag_len;
}
return true;
}
bool VendorHci::SendAndExpect(
const btlib::common::PacketView<btlib::hci::CommandHeader>& command,
std::deque<btlib::common::BufferView> events) const {
SendCommand(command);
while (events.size() > 0) {
auto evt_packet = ReadEventPacket();
if (!evt_packet) {
return false;
}
auto expected = events.front();
if ((evt_packet->view().size() != expected.size()) ||
(memcmp(evt_packet->view().data().data(), expected.data(),
expected.size()) != 0)) {
errorf("VendorHci: SendAndExpect: unexpected event received\n");
return false;
}
events.pop_front();
}
return true;
}
void VendorHci::EnterManufacturerMode() {
if (manufacturer_)
return;
auto packet =
CommandPacket::New(kMfgModeChange, sizeof(MfgModeChangeCommandParams));
auto params =
packet->mutable_view()->mutable_payload<MfgModeChangeCommandParams>();
params->enable = btlib::hci::GenericEnableParam::kEnable;
params->disable_mode = MfgDisableMode::kNoPatches;
SendCommand(packet->view());
auto evt_packet = ReadEventPacket();
if (!evt_packet ||
evt_packet->event_code() != btlib::hci::kCommandCompleteEventCode) {
errorf("VendorHci: EnterManufacturerMode failed");
return;
}
manufacturer_ = true;
}
bool VendorHci::ExitManufacturerMode(MfgDisableMode mode) {
if (!manufacturer_)
return false;
manufacturer_ = false;
auto packet =
CommandPacket::New(kMfgModeChange, sizeof(MfgModeChangeCommandParams));
auto params =
packet->mutable_view()->mutable_payload<MfgModeChangeCommandParams>();
params->enable = btlib::hci::GenericEnableParam::kDisable;
params->disable_mode = mode;
SendCommand(packet->view());
auto evt_packet = ReadEventPacket();
if (!evt_packet ||
evt_packet->event_code() != btlib::hci::kCommandCompleteEventCode) {
errorf("VendorHci: ExitManufacturerMode failed");
return false;
}
return true;
}
void VendorHci::SendCommand(
const btlib::common::PacketView<btlib::hci::CommandHeader>& command) const {
zx_status_t status =
channel_->write(0, command.data().data(), command.size(), nullptr, 0);
if (status < 0) {
errorf("VendorHci: SendCommand failed: %s\n", zx_status_get_string(status));
}
}
std::unique_ptr<btlib::hci::EventPacket> VendorHci::ReadEventPacket() const {
zx_signals_t observed;
zx_status_t status = channel_->wait_one(
ZX_CHANNEL_READABLE, zx::deadline_after(zx::sec(1)), &observed);
if (status != ZX_OK) {
errorf("VendorHci: channel error: %s\n", zx_status_get_string(status));
return nullptr;
}
FXL_DCHECK(observed & ZX_CHANNEL_READABLE);
// Allocate a buffer for the event. We don't know the size
// beforehand we allocate the largest possible buffer.
auto packet =
btlib::hci::EventPacket::New(btlib::hci::kMaxCommandPacketPayloadSize);
if (!packet) {
errorf("VendorHci: Failed to allocate event packet!\n");
return nullptr;
}
uint32_t read_size;
auto packet_bytes = packet->mutable_view()->mutable_data();
zx_status_t read_status =
channel_->read(0u, packet_bytes.mutable_data(), packet_bytes.size(),
&read_size, nullptr, 0, nullptr);
if (read_status < 0) {
errorf("VendorHci: Failed to read event bytes: %s\n",
zx_status_get_string(read_status));
return nullptr;
}
if (read_size < sizeof(btlib::hci::EventHeader)) {
errorf("VendorHci: Malformed event packet expected >%zu bytes, got %d\n",
sizeof(btlib::hci::EventHeader), read_size);
return nullptr;
}
// Compare the received payload size to what is in the header.
const size_t rx_payload_size = read_size - sizeof(btlib::hci::EventHeader);
const size_t size_from_header = packet->view().header().parameter_total_size;
if (size_from_header != rx_payload_size) {
errorf(
"VendorHci: Malformed event packet - header payload size (%zu) != "
"received (%zu)\n",
size_from_header, rx_payload_size);
return nullptr;
}
packet->InitializeFromBuffer();
return packet;
}
} // namespace btintel