diff --git a/CMakeLists.txt b/CMakeLists.txt index aaf615f..993cd4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ pico_sdk_init() add_executable(picomap main.cpp + dhcp.cpp net.cpp tusb_config.cpp w6300/w6300.cpp diff --git a/dhcp.cpp b/dhcp.cpp new file mode 100644 index 0000000..45e9c94 --- /dev/null +++ b/dhcp.cpp @@ -0,0 +1,54 @@ +#include "dhcp.h" +#include +#include "w6300.h" + +namespace dhcp_opt { + constexpr uint8_t message_type = 53; + constexpr uint8_t param_request = 55; + constexpr uint8_t end = 255; + constexpr uint8_t discover = 1; + constexpr uint8_t subnet_mask = 1; +} + +struct __attribute__((packed)) dhcp_discover { + uint8_t op = 1; + uint8_t htype = 1; + uint8_t hlen = 6; + uint8_t hops = 0; + uint32_t xid = __builtin_bswap32(0x12345678); + uint16_t secs = 0; + uint16_t flags = __builtin_bswap16(0x8000); + std::array ciaddr = {}; + std::array yiaddr = {}; + std::array siaddr = {}; + std::array giaddr = {}; + std::array chaddr = {}; + std::array sname = {}; + std::array file = {}; + std::array magic = {99, 130, 83, 99}; + uint8_t opt_msg_type[3] = {dhcp_opt::message_type, 1, dhcp_opt::discover}; + uint8_t opt_params[3] = {dhcp_opt::param_request, 1, dhcp_opt::subnet_mask}; + uint8_t opt_end = dhcp_opt::end; +}; + +static_assert(sizeof(dhcp_discover) == 247); + +static void send_discover(timer_queue& timers, const std::array& mac) { + auto sn = w6300::socket_id{0}; + w6300::open_socket(sn, w6300::protocol::udp, w6300::port_num{68}, w6300::sock_flag::none); + + dhcp_discover pkt; + std::copy(mac.begin(), mac.end(), pkt.chaddr.begin()); + + w6300::ip_address broadcast = {}; + broadcast.ip = {255, 255, 255, 255}; + broadcast.len = 4; + + w6300::sendto(sn, std::span{reinterpret_cast(&pkt), sizeof(pkt)}, broadcast, w6300::port_num{67}); + + timers.schedule_ms(5000, [&timers, mac]() { send_discover(timers, mac); }); +} + +void dhcp_start(timer_queue& timers, const std::array& mac) { + send_discover(timers, mac); +} diff --git a/include/dhcp.h b/include/dhcp.h new file mode 100644 index 0000000..fd6177b --- /dev/null +++ b/include/dhcp.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include "timer_queue.h" + +void dhcp_start(timer_queue& timers, const std::array& mac); diff --git a/include/sorted_list.h b/include/sorted_list.h new file mode 100644 index 0000000..96e5ca8 --- /dev/null +++ b/include/sorted_list.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include + +template +struct sorted_list { + struct node { + alignas(T) uint8_t storage[sizeof(T)]; + node* next = nullptr; + + T& value() { return *reinterpret_cast(storage); } + const T& value() const { return *reinterpret_cast(storage); } + }; + + node nodes[N]; + node* head = nullptr; + node* free_head = &nodes[0]; + + sorted_list() { + for (int i = 0; i < N - 1; i++) nodes[i].next = &nodes[i + 1]; + nodes[N - 1].next = nullptr; + } + + bool empty() const { return head == nullptr; } + bool full() const { return free_head == nullptr; } + + T& front() { return head->value(); } + const T& front() const { return head->value(); } + + void insert(T value) { + if (full()) return; + node* n = free_head; + free_head = n->next; + new (n->storage) T(std::move(value)); + + if (!head || n->value() < head->value()) { + n->next = head; + head = n; + return; + } + + node* cur = head; + while (cur->next && !(n->value() < cur->next->value())) + cur = cur->next; + n->next = cur->next; + cur->next = n; + } + + void pop_front() { + if (empty()) return; + node* n = head; + head = n->next; + n->value().~T(); + n->next = free_head; + free_head = n; + } +}; diff --git a/include/timer_queue.h b/include/timer_queue.h new file mode 100644 index 0000000..4b170fd --- /dev/null +++ b/include/timer_queue.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include "pico/time.h" +#include "sorted_list.h" + +struct timer_entry { + absolute_time_t when; + std::function fn; +}; + +inline bool operator<(const timer_entry& a, const timer_entry& b) { + return absolute_time_diff_us(b.when, a.when) < 0; +} + +struct timer_queue { + sorted_list queue; + alarm_id_t alarm = -1; + + void schedule(absolute_time_t when, std::function fn) { + queue.insert({when, std::move(fn)}); + arm(); + } + + void schedule_ms(uint32_t ms, std::function fn) { + schedule(make_timeout_time_ms(ms), std::move(fn)); + } + + void run() { + while (!queue.empty()) { + auto& front = queue.front(); + if (absolute_time_diff_us(get_absolute_time(), front.when) > 0) break; + auto fn = std::move(front.fn); + queue.pop_front(); + fn(); + } + arm(); + } + + bool empty() const { return queue.empty(); } + +private: + static int64_t alarm_cb(alarm_id_t, void*) { return 0; } + + void arm() { + if (alarm >= 0) cancel_alarm(alarm); + alarm = -1; + if (!queue.empty()) + alarm = add_alarm_at(queue.front().when, alarm_cb, nullptr, false); + } +}; diff --git a/main.cpp b/main.cpp index 1f58e2c..d862177 100644 --- a/main.cpp +++ b/main.cpp @@ -4,22 +4,29 @@ #include "tusb.h" #include "wire.h" #include "usb_cdc.h" +#include "timer_queue.h" +#include "dhcp.h" #include "net.h" #include "w6300.h" static usb_cdc usb; +static timer_queue timers; int main() { tusb_init(); net_init(); + auto ninfo = w6300::get_net_info(); + dhcp_start(timers, ninfo.mac); + static static_vector rx_buf; while (true) { tud_task(); usb.drain(); + timers.run(); while (tud_cdc_available()) { uint8_t byte;