#include "eth.h" #include #include "pico/unique_id.h" #include "parse_buffer.h" #include "prepend_buffer.h" #include "w6300.h" #include "debug_log.h" namespace eth { namespace { mac_addr g_mac; w6300::socket_id raw_socket{0}; frame_cb_list frame_callbacks; struct ethertype_entry { uint16_t ethertype_be; ethertype_handler fn; }; std::array eth_handlers; size_t eth_handler_count = 0; std::array mac_filters; size_t mac_filter_count = 0; bool default_mac_filter(const mac_addr& dst) { return dst == g_mac || dst == MAC_BROADCAST; } bool mac_match(const mac_addr& dst) { for (size_t i = 0; i < mac_filter_count; i++) { if (mac_filters[i](dst)) { return true; } } return false; } void process_frame(std::span frame, span_writer& tx) { if (frame.size() < sizeof(header)) { return; } auto& eth_hdr = *reinterpret_cast(frame.data()); if (!mac_match(eth_hdr.dst)) { return; } frame_callbacks.for_each([&](frame_cb_list::node* n) { if (n->value(frame)) { frame_callbacks.remove(n); } }); 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; } } } __attribute__((constructor)) void register_default_mac_filter() { register_mac_filter(default_mac_filter); } } // namespace void register_ethertype(uint16_t ethertype_be, ethertype_handler fn) { if (eth_handler_count >= eth_handlers.size()) { dlogf("eth::register_ethertype overflow: ethertype=0x%04x dropped", __builtin_bswap16(ethertype_be)); return; } eth_handlers[eth_handler_count++] = {ethertype_be, fn}; } void register_mac_filter(mac_filter fn) { if (mac_filter_count >= mac_filters.size()) { dlog("eth::register_mac_filter overflow: filter dropped"); return; } mac_filters[mac_filter_count++] = fn; } void send_raw(std::span data) { dlog_if_slow("eth::send_raw", 1000, [&]{ auto result = w6300::send(raw_socket, data); if (!result) { dlogf("w6300 send failed: %zu bytes, err %d", data.size(), static_cast(result.error())); } }); } bool init() { w6300::init_spi(); w6300::reset(); w6300::init(); if (!w6300::check()) { return false; } pico_unique_board_id_t uid; pico_get_unique_board_id(&uid); 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]; w6300::open_socket(raw_socket, w6300::protocol::macraw, w6300::sock_flag::none); w6300::set_interrupt_mask(w6300::ik_sock_0); return true; } const mac_addr& get_mac() { return g_mac; } frame_cb_handle add_frame_callback(frame_callback cb) { auto h = frame_callbacks.insert(cb); if (!h) { dlog("eth::add_frame_callback overflow: callback dropped"); } return h; } void remove_frame_callback(frame_cb_handle h) { frame_callbacks.remove(h); } void poll(std::span tx) { if (!w6300::irq_pending) { return; } 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}); if (!result) { break; } span_writer tx_writer(tx); process_frame({rx_buf, *result}, tx_writer); } w6300::rearm_gpio_irq(); } } // namespace eth