| // 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 "control_packets.h" |
| |
| #include <zircon/assert.h> |
| |
| #include "slab_allocators.h" |
| |
| namespace btlib { |
| namespace hci { |
| namespace slab_allocators { |
| |
| // Slab-allocator traits for command packets. |
| using LargeCommandTraits = PacketTraits<CommandHeader, |
| kLargeControlPacketSize, |
| kNumLargeControlPackets>; |
| using SmallCommandTraits = PacketTraits<CommandHeader, |
| kSmallControlPacketSize, |
| kNumSmallControlPackets>; |
| |
| // Slab-allocator traits for event packets. Since event packets are only |
| // received (and not sent) and because the packet size cannot be determined |
| // before the contents are read from the underlying channel, CommandChannel |
| // always allocates the largest possible buffer for events. Thus, a small buffer |
| // allocator is not needed. |
| using EventTraits = |
| PacketTraits<EventHeader, kLargeControlPacketSize, kNumLargeControlPackets>; |
| |
| using LargeCommandAllocator = fbl::SlabAllocator<LargeCommandTraits>; |
| using SmallCommandAllocator = fbl::SlabAllocator<SmallCommandTraits>; |
| using EventAllocator = fbl::SlabAllocator<EventTraits>; |
| |
| } // namespace slab_allocators |
| |
| namespace { |
| |
| std::unique_ptr<CommandPacket> NewCommandPacket(size_t payload_size) { |
| ZX_DEBUG_ASSERT(payload_size <= slab_allocators::kLargeControlPayloadSize); |
| |
| if (payload_size <= slab_allocators::kSmallControlPayloadSize) { |
| auto buffer = slab_allocators::SmallCommandAllocator::New(payload_size); |
| if (buffer) |
| return buffer; |
| |
| // We failed to allocate a small buffer; fall back to the large allocator. |
| } |
| |
| return slab_allocators::LargeCommandAllocator::New(payload_size); |
| } |
| |
| // Returns true and populates the |out_code| field with the status parameter. |
| // Returns false if |event|'s payload is too small to hold a T. T must have a |
| // |status| member of type hci::StatusCode for this to compile. |
| template <typename T> |
| bool StatusCodeFromEvent(const EventPacket& event, hci::StatusCode* out_code) { |
| ZX_DEBUG_ASSERT(out_code); |
| |
| if (event.view().payload_size() < sizeof(T)) |
| return false; |
| |
| *out_code = event.view().payload<T>().status; |
| return true; |
| } |
| |
| // Specialization for the CommandComplete event. |
| template <> |
| bool StatusCodeFromEvent<CommandCompleteEventParams>( |
| const EventPacket& event, |
| hci::StatusCode* out_code) { |
| ZX_DEBUG_ASSERT(out_code); |
| |
| const auto* params = event.return_params<SimpleReturnParams>(); |
| if (!params) |
| return false; |
| |
| *out_code = params->status; |
| return true; |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<CommandPacket> CommandPacket::New(OpCode opcode, |
| size_t payload_size) { |
| auto packet = NewCommandPacket(payload_size); |
| if (!packet) |
| return nullptr; |
| |
| packet->WriteHeader(opcode); |
| return packet; |
| } |
| |
| void CommandPacket::WriteHeader(OpCode opcode) { |
| mutable_view()->mutable_header()->opcode = htole16(opcode); |
| mutable_view()->mutable_header()->parameter_total_size = |
| view().payload_size(); |
| } |
| |
| // static |
| std::unique_ptr<EventPacket> EventPacket::New(size_t payload_size) { |
| return slab_allocators::EventAllocator::New(payload_size); |
| } |
| |
| bool EventPacket::ToStatusCode(StatusCode* out_code) const { |
| #define CASE_EVENT_STATUS(event_name) \ |
| case k##event_name##EventCode: \ |
| return StatusCodeFromEvent<event_name##EventParams>(*this, out_code) |
| |
| switch (event_code()) { |
| CASE_EVENT_STATUS(CommandComplete); |
| CASE_EVENT_STATUS(CommandStatus); |
| CASE_EVENT_STATUS(ConnectionComplete); |
| CASE_EVENT_STATUS(DisconnectionComplete); |
| CASE_EVENT_STATUS(InquiryComplete); |
| CASE_EVENT_STATUS(EncryptionChange); |
| CASE_EVENT_STATUS(RemoteNameRequestComplete); |
| CASE_EVENT_STATUS(ReadRemoteVersionInfoComplete); |
| CASE_EVENT_STATUS(ReadRemoteSupportedFeaturesComplete); |
| CASE_EVENT_STATUS(ReadRemoteExtendedFeaturesComplete); |
| |
| // TODO(armansito): Complete this list. |
| |
| default: |
| ZX_PANIC("event (%#.2x) not implemented!", event_code()); |
| break; |
| } |
| return false; |
| |
| #undef CASE_EVENT_STATUS |
| } |
| |
| Status EventPacket::ToStatus() const { |
| StatusCode code; |
| if (!ToStatusCode(&code)) { |
| return Status(common::HostError::kPacketMalformed); |
| } |
| return Status(code); |
| } |
| |
| void EventPacket::InitializeFromBuffer() { |
| mutable_view()->Resize(view().header().parameter_total_size); |
| } |
| |
| } // namespace hci |
| } // namespace btlib |
| |
| DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE( |
| ::btlib::hci::slab_allocators::LargeCommandTraits, |
| ::btlib::hci::slab_allocators::kMaxNumSlabs, |
| true); |
| DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE( |
| ::btlib::hci::slab_allocators::SmallCommandTraits, |
| ::btlib::hci::slab_allocators::kMaxNumSlabs, |
| true); |
| DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE( |
| ::btlib::hci::slab_allocators::EventTraits, |
| ::btlib::hci::slab_allocators::kMaxNumSlabs, |
| true); |