genode/repos/os/src/drivers/nic/spec/gem/rx_buffer_descriptor.h
Johannes Schlatow dc0bfd7008 zynq: zero-copy implementation of nic_drv
- Packet stream buffers are directly passed to DMA.
- Also enables pause frames and checksum offloading.

Issue #3053
2018-11-29 11:46:01 +01:00

136 lines
3.4 KiB
C++

/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \author Johannes Schlatow
* \date 2015-03-10
*/
/*
* Copyright (C) 2015-2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_
#define _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_
#include "buffer_descriptor.h"
using namespace Genode;
class Rx_buffer_descriptor : public Buffer_descriptor
{
private:
struct Addr : Register<0x00, 32> {
struct Addr31to2 : Bitfield<2, 30> {};
struct Wrap : Bitfield<1, 1> {};
struct Used : Bitfield<0, 1> {};
};
struct Status : Register<0x04, 32> {
struct Length : Bitfield<0, 13> {};
struct Start_of_frame : Bitfield<14, 1> {};
struct End_of_frame : Bitfield<15, 1> {};
};
enum { MAX_BUFFER_COUNT = 1024 };
addr_t const _phys_base;
void _reset_descriptor(unsigned const i, addr_t phys_addr) {
if (i > _max_index())
return;
/* clear status */
_descriptors[i].status = 0;
/* set physical buffer address and set not used by SW
* last descriptor must be marked by Wrap bit
*/
_descriptors[i].addr =
(phys_addr & Addr::Addr31to2::reg_mask())
| Addr::Wrap::bits(i == _max_index());
}
inline bool _head_available()
{
return Addr::Used::get(_head().addr)
&& Status::Length::get(_head().status);
}
public:
Rx_buffer_descriptor(Genode::Env &env,
Nic::Session::Tx::Source &source)
: Buffer_descriptor(env, MAX_BUFFER_COUNT),
_phys_base(Dataspace_client(source.dataspace()).phys_addr())
{
for (size_t i=0; i <= _max_index(); i++) {
try {
Nic::Packet_descriptor p = source.alloc_packet(BUFFER_SIZE);
_reset_descriptor(i, _phys_base + p.offset());
} catch (Nic::Session::Rx::Source::Packet_alloc_failed) {
/* set new _buffer_count */
_max_index(i-1);
/* set wrap bit */
_descriptors[_max_index()].addr |= Addr::Wrap::bits(1);
break;
}
}
Genode::log("Initialised ", _max_index()+1, " RX buffer descriptors");
}
bool reset_descriptor(Packet_descriptor pd)
{
addr_t const phys = _phys_base + pd.offset();
for (size_t i=0; i <= _max_index(); i++) {
_advance_tail();
if (Addr::Addr31to2::masked(_tail().addr) == phys) {
_reset_descriptor(_tail_index(), phys);
return true;
}
}
return false;
}
bool next_packet()
{
/* Find next available descriptor (head) holding a packet. */
for (unsigned int i=0; i < _max_index(); i++) {
if (_head_available())
return true;
_advance_head();
}
return false;
}
Nic::Packet_descriptor get_packet_descriptor()
{
if (!_head_available())
return Nic::Packet_descriptor(0, 0);
const Status::access_t status = _head().status;
if (!Status::Start_of_frame::get(status) || !Status::End_of_frame::get(status)) {
warning("Packet split over more than one descriptor. Packet ignored!");
_reset_descriptor(_head_index(), _head().addr);
return Nic::Packet_descriptor(0, 0);
}
const size_t length = Status::Length::get(status);
/* reset status */
_head().status = 0;
return Nic::Packet_descriptor((addr_t)Addr::Addr31to2::masked(_head().addr) - _phys_base, length);
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_ */