| #pragma once |
| #include <array> |
| #include <concepts> |
| #include <cstdint> |
| |
| /** |
| * Helper concept for the type returned by `receive_frame`. This must provide a |
| * buffer and a length. It may also be an RAII type that handles any cleanup |
| * when it is destroyed. |
| * |
| * This is not required to be copyable. |
| */ |
| template<typename T> |
| concept ReceivedEthernetFrame = requires(T frame) |
| { |
| { |
| frame->length |
| } -> std::convertible_to<uint16_t>; |
| { |
| frame->buffer |
| } -> std::convertible_to<const uint8_t *>; |
| { |
| frame->buffer |
| } -> std::convertible_to<bool>; |
| }; |
| |
| /** |
| * Concept for an Ethernet adaptor. |
| */ |
| template<typename T> |
| concept EthernetAdaptor = requires(T adaptor, |
| const uint8_t *buffer, |
| uint16_t length, |
| std::array<uint8_t, 6> macAddress) |
| { |
| /** |
| * The default MAC address for this adaptor. Must return a 6-byte array. |
| */ |
| { |
| T::mac_address_default() |
| } -> std::same_as<std::array<uint8_t, 6>>; |
| /** |
| * Is the default MAC address unique? If the device (e.g. soft MAC) |
| * doesn't have its own hardware MAC address then callers may prefer to |
| * generate a locally administered MAC address randomly. |
| */ |
| { |
| T::has_unique_mac_address() |
| } -> std::convertible_to<bool>; |
| /** |
| * Set the MAC address of this adaptor. |
| */ |
| {adaptor.mac_address_set(macAddress)}; |
| /** |
| * Set the MAC address of this adaptor to the default value. |
| */ |
| {adaptor.mac_address_set()}; |
| |
| /** |
| * Check if PHY link is up. |
| */ |
| { |
| adaptor.phy_link_status() |
| } -> std::convertible_to<bool>; |
| |
| /** |
| * Receive a frame. Returns an optional value (convertible to bool) that |
| * has a length and a buffer. The return value owns the buffer for its |
| * lifetime. |
| */ |
| { |
| adaptor.receive_frame() |
| } -> ReceivedEthernetFrame; |
| |
| /** |
| * Send a frame identified by a base and length. Returns true if the frame |
| * was sent successfully, false otherwise. |
| * |
| * The third argument is a hook that allows the caller to validate the |
| * packet after it's been buffered but before it's sent. This avoids |
| * time-of-check-to-time-of-use issues in egress filtering. |
| */ |
| { |
| adaptor.send_frame( |
| buffer, length, [](const uint8_t *buffer, uint16_t length) { |
| return true; |
| }) |
| } -> std::same_as<bool>; |
| |
| /** |
| * Read the current interrupt counter for receive interrupts. If this |
| * device doesn't support interrupts then this can return some other value. |
| * This is used only with `receive_interrupt_complete`. |
| */ |
| { |
| adaptor.receive_interrupt_value() |
| } -> std::same_as<uint32_t>; |
| |
| /** |
| * Called after `receive_frame` fails to return new frames to block until a |
| * new frame is ready to receive. |
| * |
| * TODO: This currently blocks on a single value, this may later be |
| * augmented with an interface that sets up a multiwaiter. |
| */ |
| { |
| adaptor.receive_interrupt_complete(nullptr, 0) |
| } -> std::same_as<int>; |
| }; |