2026-05-01 11:03:16 -07:00
|
|
|
#include "eth.h"
|
2026-04-19 17:28:44 -07:00
|
|
|
#include <array>
|
|
|
|
|
#include "pico/unique_id.h"
|
|
|
|
|
#include "parse_buffer.h"
|
|
|
|
|
#include "prepend_buffer.h"
|
|
|
|
|
#include "w6300.h"
|
|
|
|
|
#include "debug_log.h"
|
|
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
namespace eth {
|
2026-05-01 10:15:54 -07:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
mac_addr g_mac;
|
2026-05-01 10:15:54 -07:00
|
|
|
w6300::socket_id raw_socket{0};
|
|
|
|
|
frame_cb_list frame_callbacks;
|
2026-04-19 17:28:44 -07:00
|
|
|
|
|
|
|
|
struct ethertype_entry {
|
|
|
|
|
uint16_t ethertype_be;
|
|
|
|
|
ethertype_handler fn;
|
|
|
|
|
};
|
2026-05-01 10:15:54 -07:00
|
|
|
std::array<ethertype_entry, 8> eth_handlers;
|
|
|
|
|
size_t eth_handler_count = 0;
|
2026-04-19 17:28:44 -07:00
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
std::array<mac_filter, 4> mac_filters;
|
|
|
|
|
size_t mac_filter_count = 0;
|
2026-04-19 17:28:44 -07:00
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
bool default_mac_filter(const mac_addr& dst) {
|
|
|
|
|
return dst == g_mac || dst == MAC_BROADCAST;
|
2026-04-19 17:28:44 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
bool mac_match(const mac_addr& dst) {
|
2026-05-01 10:15:54 -07:00
|
|
|
for (size_t i = 0; i < mac_filter_count; i++) {
|
|
|
|
|
if (mac_filters[i](dst)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2026-04-19 17:28:44 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
void process_frame(std::span<const uint8_t> frame, span_writer& tx) {
|
2026-05-01 11:03:16 -07:00
|
|
|
if (frame.size() < sizeof(header)) {
|
2026-05-01 10:15:54 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2026-05-01 11:03:16 -07:00
|
|
|
auto& eth_hdr = *reinterpret_cast<const header*>(frame.data());
|
2026-04-19 17:28:44 -07:00
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
if (!mac_match(eth_hdr.dst)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
|
|
|
|
|
frame_callbacks.for_each([&](frame_cb_list::node* n) {
|
2026-05-01 10:15:54 -07:00
|
|
|
if (n->value(frame)) {
|
2026-04-19 17:28:44 -07:00
|
|
|
frame_callbacks.remove(n);
|
2026-05-01 10:15:54 -07:00
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < eth_handler_count; i++) {
|
|
|
|
|
if (eth_handlers[i].ethertype_be == eth_hdr.ethertype) {
|
|
|
|
|
eth_handlers[i].fn(frame, tx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void register_ethertype(uint16_t ethertype_be, ethertype_handler fn) {
|
|
|
|
|
if (eth_handler_count >= eth_handlers.size()) {
|
2026-05-01 11:03:16 -07:00
|
|
|
dlogf("eth::register_ethertype overflow: ethertype=0x%04x dropped", __builtin_bswap16(ethertype_be));
|
2026-05-01 10:15:54 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
eth_handlers[eth_handler_count++] = {ethertype_be, fn};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void register_mac_filter(mac_filter fn) {
|
|
|
|
|
if (mac_filter_count >= mac_filters.size()) {
|
2026-05-01 11:03:16 -07:00
|
|
|
dlog("eth::register_mac_filter overflow: filter dropped");
|
2026-05-01 10:15:54 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mac_filters[mac_filter_count++] = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_raw(std::span<const uint8_t> data) {
|
2026-05-01 11:03:16 -07:00
|
|
|
dlog_if_slow("eth::send_raw", 1000, [&]{
|
2026-05-01 10:15:54 -07:00
|
|
|
auto result = w6300::send(raw_socket, data);
|
|
|
|
|
if (!result) {
|
|
|
|
|
dlogf("w6300 send failed: %zu bytes, err %d",
|
|
|
|
|
data.size(), static_cast<int>(result.error()));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool init() {
|
2026-04-19 17:28:44 -07:00
|
|
|
w6300::init_spi();
|
|
|
|
|
w6300::reset();
|
|
|
|
|
w6300::init();
|
2026-05-01 10:15:54 -07:00
|
|
|
if (!w6300::check()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
|
|
|
|
|
pico_unique_board_id_t uid;
|
|
|
|
|
pico_get_unique_board_id(&uid);
|
2026-05-01 11:03:16 -07:00
|
|
|
g_mac[0] = (uid.id[0] & 0xFC) | 0x02;
|
|
|
|
|
g_mac[1] = uid.id[1];
|
|
|
|
|
g_mac[2] = uid.id[2];
|
|
|
|
|
g_mac[3] = uid.id[3];
|
|
|
|
|
g_mac[4] = uid.id[4];
|
|
|
|
|
g_mac[5] = uid.id[5];
|
2026-04-19 17:28:44 -07:00
|
|
|
|
|
|
|
|
w6300::open_socket(raw_socket, w6300::protocol::macraw, w6300::sock_flag::none);
|
|
|
|
|
w6300::set_interrupt_mask(w6300::ik_sock_0);
|
|
|
|
|
|
2026-05-01 13:44:53 -07:00
|
|
|
register_mac_filter(default_mac_filter);
|
|
|
|
|
|
2026-04-19 17:28:44 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
const mac_addr& get_mac() {
|
|
|
|
|
return g_mac;
|
2026-04-19 17:28:44 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
frame_cb_handle add_frame_callback(frame_callback cb) {
|
2026-04-19 17:28:44 -07:00
|
|
|
auto h = frame_callbacks.insert(cb);
|
2026-05-01 10:15:54 -07:00
|
|
|
if (!h) {
|
2026-05-01 11:03:16 -07:00
|
|
|
dlog("eth::add_frame_callback overflow: callback dropped");
|
2026-05-01 10:15:54 -07:00
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
void remove_frame_callback(frame_cb_handle h) {
|
2026-04-19 17:28:44 -07:00
|
|
|
frame_callbacks.remove(h);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-01 10:15:54 -07:00
|
|
|
void poll(std::span<uint8_t> tx) {
|
|
|
|
|
if (!w6300::irq_pending) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
w6300::irq_pending = false;
|
|
|
|
|
w6300::clear_interrupt(w6300::ik_int_all);
|
|
|
|
|
static uint8_t rx_buf[1518];
|
|
|
|
|
for (int i = 0; i < 16 && w6300::get_socket_recv_buf(raw_socket) > 0; i++) {
|
|
|
|
|
auto result = w6300::recv(raw_socket, std::span{rx_buf});
|
2026-05-01 10:15:54 -07:00
|
|
|
if (!result) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-04-19 17:28:44 -07:00
|
|
|
span_writer tx_writer(tx);
|
|
|
|
|
process_frame({rx_buf, *result}, tx_writer);
|
|
|
|
|
}
|
|
|
|
|
w6300::rearm_gpio_irq();
|
|
|
|
|
}
|
2026-05-01 10:15:54 -07:00
|
|
|
|
2026-05-01 11:03:16 -07:00
|
|
|
} // namespace eth
|