blob: 2d7a3f0126cc5566df2d5e7b0d2a473f6991e41a [file] [log] [blame]
// Copyright 2024 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.
#ifndef ZIRCON_THIRD_PARTY_DEV_ETHERNET_E1000_RINGS_H_
#define ZIRCON_THIRD_PARTY_DEV_ETHERNET_E1000_RINGS_H_
#include <lib/stdcompat/bit.h>
#include <array>
#include <fbl/macros.h>
#include "e1000_api.h"
namespace e1000 {
template <uint32_t Depth>
class TxRing {
static_assert(cpp20::has_single_bit(Depth), "Depth must be a power of two");
public:
TxRing() = default;
DISALLOW_COPY_ASSIGN_AND_MOVE(TxRing);
void AssignDescriptorMmio(void* descriptor_mmio) {
descriptors_ = static_cast<e1000_tx_desc*>(descriptor_mmio);
}
// Push TX buffer to the ring. Returns the NEW tail after pushing to match the behavior of the
// hardware TX ring. Note that this is different from the RX ring.
uint32_t Push(uint32_t id, zx_paddr_t physical_addr, uint64_t length) {
ids_[tail_] = id;
descriptors_[tail_].buffer_addr = physical_addr;
descriptors_[tail_].lower.data =
static_cast<uint32_t>(E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | E1000_TXD_CMD_RS | length);
tail_ = (tail_ + 1) & (Depth - 1);
++size_;
return tail_;
}
void Pop() {
descriptors_[head_].upper.data = 0;
head_ = (head_ + 1) & (Depth - 1);
--size_;
}
void Clear() {
size_ = head_ = tail_ = 0;
memset(descriptors_, 0, sizeof(e1000_tx_desc) * Depth);
ids_.fill({});
}
uint32_t HeadIndex() const { return head_; }
uint32_t HeadId() const { return ids_[head_]; }
e1000_tx_desc& HeadDesc() { return descriptors_[head_]; }
bool IsEmpty() const { return size_ == 0; }
uint32_t Size() const { return size_; }
bool Available() const { return descriptors_[head_].upper.fields.status & E1000_TXD_STAT_DD; }
uint32_t Id(size_t index) const { return ids_[index]; }
e1000_tx_desc& Desc(size_t index) { return descriptors_[index]; }
private:
e1000_tx_desc* descriptors_ = nullptr;
std::array<uint32_t, Depth> ids_;
uint32_t head_ = 0;
uint32_t tail_ = 0;
uint32_t size_ = 0;
};
template <uint32_t Depth, typename Descriptor>
class RxRing {
static_assert(cpp20::has_single_bit(Depth), "Depth must be a power of two");
static_assert(std::is_same_v<Descriptor, e1000_adv_rx_desc> ||
std::is_same_v<Descriptor, e1000_rx_desc_extended> ||
std::is_same_v<Descriptor, e1000_rx_desc>,
"Descriptor type must match one of the supported types");
public:
RxRing() = default;
DISALLOW_COPY_ASSIGN_AND_MOVE(RxRing);
void AssignDescriptorMmio(void* descriptor_mmio) {
descriptors_ = static_cast<Descriptor*>(descriptor_mmio);
}
// Push RX space to the ring. Returns the PREVIOUS tail before pushing to match the expectation
// of the hardware RX ring. Note that this is different from the TX ring.
uint32_t Push(uint32_t id, zx_paddr_t physical_addr) {
ids_[tail_] = id;
if constexpr (std::is_same_v<Descriptor, e1000_adv_rx_desc>) {
descriptors_[tail_].read.pkt_addr = physical_addr;
} else if constexpr (std::is_same_v<Descriptor, e1000_rx_desc_extended>) {
descriptors_[tail_].read.buffer_addr = physical_addr;
descriptors_[tail_].wb.upper.status_error = 0;
} else if constexpr (std::is_same_v<Descriptor, e1000_rx_desc>) {
descriptors_[tail_].buffer_addr = physical_addr;
descriptors_[tail_].status = 0;
}
const uint32_t prev_tail = tail_;
tail_ = (tail_ + 1) & (Depth - 1);
++size_;
return prev_tail;
}
void Pop() {
if constexpr (std::is_same_v<Descriptor, e1000_adv_rx_desc>) {
descriptors_[head_].wb.upper.status_error = 0;
} else if constexpr (std::is_same_v<Descriptor, e1000_rx_desc_extended>) {
descriptors_[head_].wb.upper.status_error &= 0xFF;
} else if constexpr (std::is_same_v<Descriptor, e1000_rx_desc>) {
descriptors_[head_].status = 0;
}
head_ = (head_ + 1) & (Depth - 1);
--size_;
}
void Clear() {
size_ = head_ = tail_ = 0;
memset(descriptors_, 0, sizeof(e1000_tx_desc) * Depth);
ids_.fill({});
}
uint32_t HeadId() const { return ids_[head_]; }
bool IsEmpty() const { return size_ == 0; }
uint32_t Size() const { return size_; }
bool Available(uint16_t* out_len) const {
if (IsEmpty()) {
return false;
}
uint32_t status = 0;
if constexpr (std::is_same_v<Descriptor, e1000_adv_rx_desc> ||
std::is_same_v<Descriptor, e1000_rx_desc_extended>) {
status = descriptors_[head_].wb.upper.status_error;
if (unlikely(status & E1000_RXDEXT_ERR_FRAME_ERR_MASK)) {
*out_len = 0;
} else {
*out_len = descriptors_[head_].wb.upper.length;
}
} else if constexpr (std::is_same_v<Descriptor, e1000_rx_desc>) {
status = descriptors_[head_].status;
if (unlikely(status & E1000_RXD_ERR_FRAME_ERR_MASK)) {
*out_len = 0;
} else {
*out_len = descriptors_[head_].length;
}
}
return status & E1000_TXD_STAT_DD;
}
uint32_t Id(size_t index) const { return ids_[index]; }
Descriptor& Desc(size_t index) { return descriptors_[index]; }
private:
Descriptor* descriptors_ = nullptr;
std::array<uint32_t, Depth> ids_;
uint32_t head_ = 0;
uint32_t tail_ = 0;
uint32_t size_ = 0;
};
} // namespace e1000
#endif // ZIRCON_THIRD_PARTY_DEV_ETHERNET_E1000_RINGS_H_