| // 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/object.h> |
| #include <lib/zx/time.h> |
| |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| |
| namespace btintel { |
| |
| using ::btlib::hci::CommandPacket; |
| |
| namespace { |
| |
| constexpr size_t kMaxSecureSendArgLen = 252; |
| constexpr auto kInitTimeoutMs = zx::sec(10); |
| |
| } // namespace |
| |
| VendorHci::VendorHci(zx::channel* ctrl) |
| : ctrl_(ctrl), acl_(nullptr), manufacturer_(false){}; |
| |
| ReadVersionReturnParams VendorHci::SendReadVersion() const { |
| auto packet = CommandPacket::New(kReadVersion); |
| SendCommand(packet->view()); |
| auto evt_packet = WaitForEventPacket(); |
| 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 = WaitForEventPacket(); |
| 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}; |
| } |
| |
| btlib::hci::StatusCode VendorHci::SendHciReset() const { |
| auto packet = CommandPacket::New(btlib::hci::kReset); |
| SendCommand(packet->view()); |
| |
| // TODO(armansito): Consider collecting a metric for initialization time |
| // (successful and failing) to provide us with a better sense of how long |
| // these timeouts should be. |
| auto evt_packet = |
| WaitForEventPacket(kInitTimeoutMs, btlib::hci::kCommandCompleteEventCode); |
| if (!evt_packet) { |
| errorf("VendorHci: failed while waiting for HCI_Reset response\n"); |
| return btlib::hci::StatusCode::kUnspecifiedError; |
| } |
| |
| const auto* params = |
| evt_packet->return_params<btlib::hci::SimpleReturnParams>(); |
| if (!params) { |
| errorf("VendorHci: HCI_Reset: received malformed response\n"); |
| return btlib::hci::StatusCode::kUnspecifiedError; |
| } |
| |
| return params->status; |
| } |
| |
| void VendorHci::SendVendorReset() 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()); |
| |
| // Sleep for 2 seconds to let the controller process the reset. |
| zx_nanosleep(zx_deadline_after(ZX_SEC(2))); |
| } |
| |
| 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 = WaitForEventPacket(); |
| 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 = WaitForEventPacket(); |
| 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 = WaitForEventPacket(); |
| 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 = WaitForEventPacket(); |
| 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 = |
| ctrl_->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::WaitForEventPacket( |
| zx::duration timeout, btlib::hci::EventCode expected_event) const { |
| zx_wait_item_t wait_items[2]; |
| uint32_t wait_item_count = 1; |
| |
| ZX_DEBUG_ASSERT(ctrl_); |
| wait_items[0].handle = ctrl_->get(); |
| wait_items[0].waitfor = ZX_CHANNEL_READABLE; |
| wait_items[0].pending = 0; |
| |
| if (acl_) { |
| wait_items[1].handle = acl_->get(); |
| wait_items[1].waitfor = ZX_CHANNEL_READABLE; |
| wait_items[1].pending = 0; |
| wait_item_count++; |
| } |
| |
| auto begin = zx::clock::get_monotonic(); |
| for (zx::duration elapsed; elapsed < timeout; |
| elapsed = zx::clock::get_monotonic() - begin) { |
| zx_status_t status = |
| zx_object_wait_many(wait_items, wait_item_count, |
| zx::deadline_after(timeout - elapsed).get()); |
| if (status != ZX_OK) { |
| errorf("VendorHci: channel error: %s\n", zx_status_get_string(status)); |
| return nullptr; |
| } |
| |
| // Determine which channel caused the event. |
| zx_handle_t evt_handle = 0; |
| for (unsigned i = 0; i < wait_item_count; ++i) { |
| if (wait_items[i].pending & ZX_CHANNEL_READABLE) { |
| evt_handle = wait_items[0].handle; |
| break; |
| } |
| } |
| |
| ZX_DEBUG_ASSERT(evt_handle); |
| |
| // 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 = |
| zx_channel_read(evt_handle, 0u, packet_bytes.mutable_data(), nullptr, |
| packet_bytes.size(), 0, &read_size, 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; |
| } |
| |
| if (expected_event && expected_event != packet->event_code()) { |
| tracef("VendorHci: keep waiting (expected: 0x%02x, got: 0x%02x)\n", |
| expected_event, packet->event_code()); |
| continue; |
| } |
| |
| packet->InitializeFromBuffer(); |
| return packet; |
| } |
| |
| errorf("VendorHci: timed out waiting for event\n"); |
| return nullptr; |
| } |
| |
| } // namespace btintel |