blob: 6602ea6acca3ab707037a111db6f68e0685d3824 [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 "acl_data_packet.h"
#include <endian.h>
#include <zircon/assert.h>
#include "slab_allocators.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
namespace bt::hci {
namespace slab_allocators {
// Slab-allocator traits for ACL data packets.
using LargeACLTraits =
PacketTraits<ACLDataHeader, kLargeACLDataPacketSize, kNumLargeACLDataPackets>;
using MediumACLTraits =
PacketTraits<ACLDataHeader, kMediumACLDataPacketSize, kNumMediumACLDataPackets>;
using SmallACLTraits =
PacketTraits<ACLDataHeader, kSmallACLDataPacketSize, kNumSmallACLDataPackets>;
using LargeACLAllocator = fbl::SlabAllocator<LargeACLTraits>;
using MediumACLAllocator = fbl::SlabAllocator<MediumACLTraits>;
using SmallACLAllocator = fbl::SlabAllocator<SmallACLTraits>;
} // namespace slab_allocators
namespace {
// Type containing both a fixed packet storage buffer and a ACLDataPacket interface to the buffer.
// Does not deallocate from a slab buffer when destroyed (unlike SlabPacket).
using LargeACLDataPacket =
slab_allocators::internal::FixedSizePacket<ACLDataHeader,
slab_allocators::kLargeACLDataPacketSize>;
ACLDataPacketPtr NewACLDataPacket(size_t payload_size) {
ZX_ASSERT_MSG(payload_size <= slab_allocators::kLargeACLDataPayloadSize,
"payload size %zu too large (allowed = %zu)", payload_size,
slab_allocators::kLargeACLDataPayloadSize);
if (payload_size <= slab_allocators::kSmallACLDataPayloadSize) {
auto buffer = slab_allocators::SmallACLAllocator::New(payload_size);
if (buffer) {
return buffer;
}
// Fall back to the next allocator.
}
if (payload_size <= slab_allocators::kMediumACLDataPayloadSize) {
auto buffer = slab_allocators::MediumACLAllocator::New(payload_size);
if (buffer) {
return buffer;
}
// Fall back to the next allocator.
}
auto buffer = slab_allocators::LargeACLAllocator::New(payload_size);
if (buffer) {
return buffer;
}
bt_log(TRACE, "hci", "ACLDataPacket slab allocators capacity exhausted");
// Fall back to system allocator.
return std::make_unique<LargeACLDataPacket>(payload_size);
}
} // namespace
// static
ACLDataPacketPtr ACLDataPacket::New(uint16_t payload_size) {
return NewACLDataPacket(payload_size);
}
// static
ACLDataPacketPtr ACLDataPacket::New(ConnectionHandle connection_handle,
ACLPacketBoundaryFlag packet_boundary_flag,
ACLBroadcastFlag broadcast_flag, uint16_t payload_size) {
auto packet = NewACLDataPacket(payload_size);
if (!packet)
return nullptr;
packet->WriteHeader(connection_handle, packet_boundary_flag, broadcast_flag);
return packet;
}
ConnectionHandle ACLDataPacket::connection_handle() const {
// Return the lower 12-bits of the first two octets.
return le16toh(ACLDataPacket::view().header().handle_and_flags) & 0x0FFF;
}
ACLPacketBoundaryFlag ACLDataPacket::packet_boundary_flag() const {
// Return bits 4-5 in the higher octet of |handle_and_flags| or
// "0b00xx000000000000".
return static_cast<ACLPacketBoundaryFlag>(
(le16toh(ACLDataPacket::view().header().handle_and_flags) >> 12) & 0x0003);
}
ACLBroadcastFlag ACLDataPacket::broadcast_flag() const {
// Return bits 6-7 in the higher octet of |handle_and_flags| or
// "0bxx00000000000000".
return static_cast<ACLBroadcastFlag>(le16toh(view().header().handle_and_flags) >> 14);
}
void ACLDataPacket::InitializeFromBuffer() {
mutable_view()->Resize(le16toh(view().header().data_total_length));
}
void ACLDataPacket::WriteHeader(ConnectionHandle connection_handle,
ACLPacketBoundaryFlag packet_boundary_flag,
ACLBroadcastFlag broadcast_flag) {
// Must fit inside 12-bits.
ZX_DEBUG_ASSERT(connection_handle <= 0x0FFF);
// Must fit inside 2-bits.
ZX_DEBUG_ASSERT(static_cast<uint8_t>(packet_boundary_flag) <= 0x03);
ZX_DEBUG_ASSERT(static_cast<uint8_t>(broadcast_flag) <= 0x03);
uint16_t handle_and_flags = connection_handle |
(static_cast<uint16_t>(packet_boundary_flag) << 12) |
(static_cast<uint16_t>(broadcast_flag) << 14);
mutable_view()->mutable_header()->handle_and_flags = htole16(handle_and_flags);
mutable_view()->mutable_header()->data_total_length = htole16(view().payload_size());
}
} // namespace bt::hci
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(bt::hci::slab_allocators::LargeACLTraits,
bt::hci::slab_allocators::kMaxNumSlabs, true);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(bt::hci::slab_allocators::MediumACLTraits,
bt::hci::slab_allocators::kMaxNumSlabs, true);
DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(bt::hci::slab_allocators::SmallACLTraits,
bt::hci::slab_allocators::kMaxNumSlabs, true);