| // 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. |
| |
| #pragma once |
| |
| #include "wlan.h" |
| |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/slab_allocator.h> |
| #include <fbl/unique_ptr.h> |
| #include <zircon/types.h> |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| |
| typedef struct ethmac_netbuf ethmac_netbuf_t; |
| |
| namespace wlan { |
| |
| // A Buffer is a type that points at bytes and knows how big it is. For now, it can also carry |
| // out-of-band control data. |
| class Buffer { |
| public: |
| virtual ~Buffer() {} |
| virtual uint8_t* data() = 0; |
| virtual uint8_t* ctrl() = 0; |
| virtual size_t capacity() const = 0; |
| virtual void clear(size_t len) = 0; |
| }; |
| |
| constexpr size_t kCtrlSize = 32; |
| |
| namespace internal { |
| template <size_t BufferSize> class FixedBuffer : public Buffer { |
| public: |
| uint8_t* data() override { return data_; } |
| uint8_t* ctrl() override { return ctrl_; } |
| size_t capacity() const override { return BufferSize; } |
| void clear(size_t len) override { |
| std::memset(data_, 0, std::min(BufferSize, len)); |
| std::memset(ctrl_, 0, kCtrlSize); |
| } |
| |
| private: |
| uint8_t data_[BufferSize]; |
| // Embedding the control data directly into the buffer is not ideal. |
| // TODO(tkilbourn): replace this with a general solution. |
| uint8_t ctrl_[kCtrlSize]; |
| }; |
| } // namespace internal |
| |
| constexpr size_t kSlabOverhead = 16; // overhead for the slab allocator as a whole |
| |
| template <size_t NumBuffers, size_t BufferSize> class SlabBuffer; |
| template <size_t NumBuffers, size_t BufferSize> |
| using SlabBufferTraits = |
| fbl::StaticSlabAllocatorTraits<fbl::unique_ptr<SlabBuffer<NumBuffers, BufferSize>>, |
| sizeof(internal::FixedBuffer<BufferSize>) * NumBuffers + |
| kSlabOverhead>; |
| |
| // A SlabBuffer is an implementation of a Buffer that comes from a fbl::SlabAllocator. The size of |
| // the internal::FixedBuffer and the number of buffers is part of the typename of the SlabAllocator, |
| // so the SlabBuffer itself is also templated on these parameters. |
| template <size_t NumBuffers, size_t BufferSize> |
| class SlabBuffer final : public internal::FixedBuffer<BufferSize>, |
| public fbl::SlabAllocated<SlabBufferTraits<NumBuffers, BufferSize>> {}; |
| |
| // Huge buffers are used for sending lots of data between drivers and the wlanstack. They are not |
| // (currently) intended for transmitting over-the-air. |
| constexpr size_t kHugeBuffers = 8; |
| constexpr size_t kHugeBufferSize = 16384; |
| // Large buffers can hold the largest 802.11 MSDU or standard Ethernet MTU. |
| constexpr size_t kLargeBuffers = 32; |
| constexpr size_t kLargeBufferSize = 2560; |
| // Small buffers are for smaller control packets within the driver stack itself (though they could |
| // be used for transfering small 802.11 frames as well). |
| constexpr size_t kSmallBuffers = 1024; |
| constexpr size_t kSmallBufferSize = 64; |
| |
| using HugeBufferTraits = SlabBufferTraits<kHugeBuffers, kHugeBufferSize>; |
| using LargeBufferTraits = SlabBufferTraits<kLargeBuffers, kLargeBufferSize>; |
| using SmallBufferTraits = SlabBufferTraits<kSmallBuffers, kSmallBufferSize>; |
| using HugeBufferAllocator = fbl::SlabAllocator<HugeBufferTraits>; |
| using LargeBufferAllocator = fbl::SlabAllocator<LargeBufferTraits>; |
| using SmallBufferAllocator = fbl::SlabAllocator<SmallBufferTraits>; |
| |
| // Gets a (slab allocated) Buffer with at least |len| bytes capacity. |
| fbl::unique_ptr<Buffer> GetBuffer(size_t len); |
| |
| // A Packet wraps a buffer with information about the recipient/sender and length of the data |
| // within the buffer. |
| class Packet : public fbl::DoublyLinkedListable<fbl::unique_ptr<Packet>> { |
| public: |
| enum class Peer { |
| kUnknown, |
| kDevice, |
| kWlan, |
| kEthernet, |
| kService, |
| }; |
| |
| Packet(fbl::unique_ptr<Buffer> buffer, size_t len); |
| size_t Capacity() const { return buffer_->capacity(); } |
| void clear() { |
| ZX_DEBUG_ASSERT(!has_ext_data()); |
| buffer_->clear(len_); |
| ctrl_len_ = 0; |
| } |
| |
| void set_peer(Peer s) { peer_ = s; } |
| Peer peer() const { return peer_; } |
| |
| const uint8_t* data() const { return buffer_->data(); } |
| uint8_t* mut_data() { return buffer_->data(); } |
| |
| // Length can only be made shorter at this time. |
| zx_status_t set_len(size_t len) { |
| if (len > len_) return ZX_ERR_INVALID_ARGS; |
| len_ = len; |
| return ZX_OK; |
| } |
| size_t len() const { return len_; } |
| |
| template <typename T> const T* field(size_t offset) const { |
| return FromBytes<T>(buffer_->data() + offset, len_ - offset); |
| } |
| |
| template <typename T> T* mut_field(size_t offset) const { |
| return FromBytes<T>(buffer_->data() + offset, len_ - offset); |
| } |
| |
| template <typename T> bool has_ctrl_data() const { return ctrl_len_ == sizeof(T); } |
| |
| template <typename T> const T* ctrl_data() const { |
| static_assert(fbl::is_standard_layout<T>::value, "Control data must have standard layout"); |
| static_assert(kCtrlSize >= sizeof(T), |
| "Control data type too large for Buffer ctrl_data field"); |
| return FromBytes<T>(buffer_->ctrl(), ctrl_len_); |
| } |
| |
| template <typename T> void CopyCtrlFrom(const T& t) { |
| static_assert(fbl::is_standard_layout<T>::value, "Control data must have standard layout"); |
| static_assert(kCtrlSize >= sizeof(T), |
| "Control data type too large for Buffer ctrl_data field"); |
| std::memcpy(buffer_->ctrl(), &t, sizeof(T)); |
| ctrl_len_ = sizeof(T); |
| } |
| |
| zx_status_t CopyFrom(const void* src, size_t len, size_t offset); |
| |
| bool has_ext_data() const { return ext_data_ != nullptr; } |
| void set_ext_data(ethmac_netbuf_t* netbuf, uint16_t offset) { |
| ZX_DEBUG_ASSERT(!has_ext_data()); |
| ext_data_ = netbuf; |
| ext_offset_ = offset; |
| } |
| ethmac_netbuf_t* ext_data() const { return ext_data_; } |
| uint16_t ext_offset() const { return ext_offset_; } |
| |
| private: |
| fbl::unique_ptr<Buffer> buffer_; |
| size_t len_ = 0; |
| size_t ctrl_len_ = 0; |
| Peer peer_ = Peer::kUnknown; |
| ethmac_netbuf_t* ext_data_ = nullptr; |
| uint16_t ext_offset_ = 0; |
| }; |
| |
| class PacketQueue { |
| public: |
| using PacketPtr = fbl::unique_ptr<Packet>; |
| |
| bool is_empty() const { return queue_.is_empty(); } |
| size_t size() const { return size_; } |
| void clear() { |
| queue_.clear(); |
| size_ = 0; |
| } |
| |
| void Enqueue(PacketPtr packet) { |
| ZX_DEBUG_ASSERT(packet.get() != nullptr); |
| queue_.push_front(std::move(packet)); |
| size_++; |
| } |
| void UndoEnqueue() { |
| ZX_DEBUG_ASSERT(!is_empty()); |
| queue_.pop_front(); |
| size_--; |
| } |
| |
| PacketPtr Dequeue() { |
| auto packet = queue_.pop_back(); |
| if (packet.get()) size_--; |
| return packet; |
| } |
| |
| private: |
| fbl::DoublyLinkedList<PacketPtr> queue_; |
| size_t size_ = 0; |
| }; |
| |
| } // namespace wlan |
| |
| // Declaration of static slab allocators. |
| FWD_DECL_STATIC_SLAB_ALLOCATOR(::wlan::LargeBufferTraits); |
| FWD_DECL_STATIC_SLAB_ALLOCATOR(::wlan::SmallBufferTraits); |