diff --git a/drivers/build.zig b/drivers/build.zig index cc819cb82..658b93e5b 100644 --- a/drivers/build.zig +++ b/drivers/build.zig @@ -8,6 +8,10 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("framework.zig"), .target = target, .optimize = optimize, + .imports = &.{.{ + .name = "link", + .module = b.dependency("link", .{}).module("link"), + }}, }); const test_suite = b.addTest(.{ diff --git a/drivers/build.zig.zon b/drivers/build.zig.zon index 33813fc0f..3e8974260 100644 --- a/drivers/build.zig.zon +++ b/drivers/build.zig.zon @@ -2,6 +2,9 @@ .name = .mz_drivers, .fingerprint = 0x576453eedc5af46e, .minimum_zig_version = "0.15.1", + .dependencies = .{ + .link = .{ .path = "../modules/network/link" }, + }, .version = "0.0.1", .paths = .{ "build.zig", diff --git a/drivers/wireless/cyw43439.zig b/drivers/wireless/cyw43439.zig index 5db0b5cec..f539fd6b6 100644 --- a/drivers/wireless/cyw43439.zig +++ b/drivers/wireless/cyw43439.zig @@ -2,6 +2,7 @@ const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; +const Link = @import("link"); const Bus = @import("cyw43439/bus.zig"); const WiFi = @import("cyw43439/wifi.zig"); pub const JoinOptions = WiFi.JoinOptions; @@ -58,14 +59,13 @@ pub fn recv_zc(ptr: *anyopaque, bytes: []u8) anyerror!?struct { usize, usize } { return self.wifi.recv_zc(bytes); } -pub fn send_zc(ptr: *anyopaque, bytes: []u8) anyerror!void { +pub fn send_zc(ptr: *anyopaque, bytes: []u8) Link.Error!void { const self: *Self = @ptrCast(@alignCast(ptr)); - try self.wifi.send_zc(bytes); -} - -pub fn ready(ptr: *anyopaque) bool { - const self: *Self = @ptrCast(@alignCast(ptr)); - return self.wifi.has_credit(); + self.wifi.send_zc(bytes) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.LinkDown => return error.LinkDown, + else => return error.InternalError, + }; } pub fn gpio(self: *Self, pin: u2) Pin { @@ -86,6 +86,16 @@ pub const Pin = struct { } }; +pub fn link(self: *Self) Link { + return .{ + .ptr = self, + .vtable = .{ + .recv = recv_zc, + .send = send_zc, + }, + }; +} + // References: // https://github.com/embassy-rs/embassy/blob/abb1d8286e2415686150e2e315ca1c380659c3c3/cyw43/src/consts.rs // https://github.com/jbentham/picowi/blob/main/lib/picowi_regs.h diff --git a/drivers/wireless/cyw43439/wifi.zig b/drivers/wireless/cyw43439/wifi.zig index 11808979b..6f86e3f11 100644 --- a/drivers/wireless/cyw43439/wifi.zig +++ b/drivers/wireless/cyw43439/wifi.zig @@ -528,8 +528,8 @@ pub fn recv_zc(self: *Self, buffer: []u8) !?struct { usize, usize } { /// /// Buffer has to be 4 bytes aligned and it will be extended in as_words to the /// word boundary! -pub fn send_zc(self: *Self, buffer: []u8) !void { - if (!self.has_credit()) return error.Cyw43NoCredit; +pub fn send_zc(self: *Self, buffer: []u8) anyerror!void { + if (!self.has_credit()) return error.OutOfMemory; const eth_frame_len = buffer.len - 22; // add bus header diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index a064b3949..822ef2bbe 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -145,26 +145,19 @@ pub fn build(b: *std.Build) void { .imports = example.imports, }); + // Import net module for some examples if (std.mem.indexOf(u8, example.name, "_net-") != null) { - const target = b.resolveTargetQuery(firmware.target.zig_target); - const foundation_dep = b.dependency("foundationlibc", .{ - .target = target, + const net_dep = b.dependency("net", .{ + .target = b.resolveTargetQuery(firmware.target.zig_target), .optimize = optimize, + .mtu = 1500, + // Cyw43 driver requires 22 bytes of header and 4 bytes of footer. + // header + ethernet + mtu + footer = 22 + 14 + 1500 + 4 = 1540 + .pbuf_length = 1540, + .pbuf_header_length = 22, }); - const lwip_dep = b.dependency("lwip", .{ - .target = target, - .optimize = optimize, - }); - const lwip_mod = lwip_dep.module("lwip"); - // link libc - lwip_mod.linkLibrary(foundation_dep.artifact("foundation")); - // add path to the configuration, lwipopts.h - lwip_mod.addIncludePath(b.path("src/net/lwip/include")); - // add c import paths - for (lwip_mod.include_dirs.items) |dir| { - firmware.app_mod.include_dirs.append(b.allocator, dir) catch @panic("out of memory"); - } - firmware.app_mod.addImport("lwip", lwip_mod); + const net_mod = net_dep.module("net"); + firmware.app_mod.addImport("net", net_mod); } // `install_firmware()` is the MicroZig pendant to `Build.installArtifact()` diff --git a/examples/raspberrypi/rp2xxx/build.zig.zon b/examples/raspberrypi/rp2xxx/build.zig.zon index 408c96a66..2e0f00ad7 100644 --- a/examples/raspberrypi/rp2xxx/build.zig.zon +++ b/examples/raspberrypi/rp2xxx/build.zig.zon @@ -9,8 +9,7 @@ .url = "git+https://github.com/Gnyblast/zig-ssd1306-gddram-fonts#84dd5fab70ddc1198eef1cd1305cc82f08f419dc", .hash = "ssd1306_font_8x8-0.0.0-oGb_cERcAAA6NJzWDOMepgnY6r4blNEHjpkldDaRAui5", }, - .foundationlibc = .{ .path = "../../../modules/foundation-libc/" }, - .lwip = .{ .path = "../../../modules/lwip/" }, + .net = .{ .path = "../../../modules/network/" }, }, .paths = .{ "README.md", diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip/exports.zig b/examples/raspberrypi/rp2xxx/src/net/lwip/exports.zig deleted file mode 100644 index 76146a286..000000000 --- a/examples/raspberrypi/rp2xxx/src/net/lwip/exports.zig +++ /dev/null @@ -1,45 +0,0 @@ -/// Platform dependent exports required by lwip -/// -const std = @import("std"); -const microzig = @import("microzig"); -const rp2xxx = microzig.hal; -const time = rp2xxx.time; -const trng = rp2xxx.rand.trng; - -const log = std.log.scoped(.lwip); -const assert = std.debug.assert; - -export fn lwip_lock_interrupts(were_enabled: *bool) void { - _ = were_enabled; -} - -export fn lwip_unlock_interrupts(enable: bool) void { - _ = enable; -} - -export fn lwip_rand() u32 { - return if (rp2xxx.compatibility.chip == .RP2350) - trng.random_blocking() - else - 4; -} - -export fn lwip_assert(msg: [*c]const u8, file: [*c]const u8, line: c_int) void { - log.err("assert: {s} in file: {s}, line: {}", .{ msg, file, line }); - @panic("lwip assert"); -} - -export fn lwip_diag(msg: [*c]const u8, file: [*c]const u8, line: c_int) void { - log.debug("{s} in file: {s}, line: {}", .{ msg, file, line }); -} - -// required by fundation-libc/src/modules/errno.zig -// line 12 threadlocal -export fn __aeabi_read_tp() u32 { - return 0; -} - -export fn sys_now() u32 { - const ts = time.get_time_since_boot(); - return @truncate(ts.to_us() / 1000); -} diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip/readme.md b/examples/raspberrypi/rp2xxx/src/net/lwip/readme.md deleted file mode 100644 index 4cedaa571..000000000 --- a/examples/raspberrypi/rp2xxx/src/net/lwip/readme.md +++ /dev/null @@ -1,4 +0,0 @@ - -net.zig - Chip independent network library. Connects lwip with underlying network link interface. -exports.zig - Platform dependent exports required by lwip. -include - lwip configuration files diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip_exports.zig b/examples/raspberrypi/rp2xxx/src/net/lwip_exports.zig new file mode 100644 index 000000000..01a7f72b5 --- /dev/null +++ b/examples/raspberrypi/rp2xxx/src/net/lwip_exports.zig @@ -0,0 +1,27 @@ +/// Exports for the lwip +/// Required from modules/network/src/include/arch.cc#L26 +const std = @import("std"); +const microzig = @import("microzig"); +const hal = microzig.hal; + +/// Time since boot in milliseconds. +export fn lwip_sys_now() u32 { + const ts = hal.time.get_time_since_boot(); + return @truncate(ts.to_us() / 1000); +} + +var rng: ?hal.rand.Ascon = null; + +export fn lwip_rand() u32 { + return switch (hal.compatibility.chip) { + .RP2350 => hal.rand.trng.random_blocking(), + .RP2040 => brk: { + if (rng == null) { + rng = .init(); + } + var val: u32 = 0; + rng.?.fill(std.mem.asBytes(&val)); + break :brk val; + }, + }; +} diff --git a/examples/raspberrypi/rp2xxx/src/net/pong.zig b/examples/raspberrypi/rp2xxx/src/net/pong.zig index 2099c1bbc..01ae742b9 100644 --- a/examples/raspberrypi/rp2xxx/src/net/pong.zig +++ b/examples/raspberrypi/rp2xxx/src/net/pong.zig @@ -14,10 +14,10 @@ pub const microzig_options = microzig.Options{ }; const log = std.log.scoped(.main); -const net = @import("lwip/net.zig"); comptime { - _ = @import("lwip/exports.zig"); + _ = @import("lwip_exports.zig"); } +const net = @import("net"); const secrets = @import("secrets.zig"); pub fn main() !void { @@ -39,14 +39,7 @@ pub fn main() !void { log.debug("wifi joined", .{}); // init lwip network interface - var nic: net.Interface = .{ - .link = .{ - .ptr = wifi, - .recv = drivers.WiFi.recv, - .send = drivers.WiFi.send, - .ready = drivers.WiFi.ready, - }, - }; + var nic: net.Interface = .{ .link = wifi.link() }; try nic.init(wifi.mac, try secrets.nic_options()); var ts = time.get_time_since_boot(); diff --git a/examples/raspberrypi/rp2xxx/src/net/secrets.zig b/examples/raspberrypi/rp2xxx/src/net/secrets.zig index 623a36f35..4d48753cc 100644 --- a/examples/raspberrypi/rp2xxx/src/net/secrets.zig +++ b/examples/raspberrypi/rp2xxx/src/net/secrets.zig @@ -1,4 +1,4 @@ -const net = @import("lwip/net.zig"); +const net = @import("net"); const microzig = @import("microzig"); const JoinOptions = microzig.hal.drivers.WiFi.Chip.JoinOptions; diff --git a/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig b/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig index dfb3c52a9..eaa47287d 100644 --- a/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig +++ b/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig @@ -14,10 +14,10 @@ pub const microzig_options = microzig.Options{ }; const log = std.log.scoped(.main); -const net = @import("lwip/net.zig"); comptime { - _ = @import("lwip/exports.zig"); + _ = @import("lwip_exports.zig"); } +const net = @import("net"); const secrets = @import("secrets.zig"); pub fn main() !void { @@ -36,14 +36,7 @@ pub fn main() !void { try wifi.join(secrets.ssid, secrets.pwd, secrets.join_opt); // init lwip network interface - var nic: net.Interface = .{ - .link = .{ - .ptr = wifi, - .recv = drivers.WiFi.recv, - .send = drivers.WiFi.send, - .ready = drivers.WiFi.ready, - }, - }; + var nic: net.Interface = .{ .link = wifi.link() }; try nic.init(wifi.mac, try secrets.nic_options()); // init handler diff --git a/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig b/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig index 79f51feb6..c176c86f8 100644 --- a/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig +++ b/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig @@ -14,10 +14,10 @@ pub const microzig_options = microzig.Options{ }; const log = std.log.scoped(.main); -const net = @import("lwip/net.zig"); comptime { - _ = @import("lwip/exports.zig"); + _ = @import("lwip_exports.zig"); } +const net = @import("net"); const secrets = @import("secrets.zig"); pub fn main() !void { @@ -36,14 +36,7 @@ pub fn main() !void { try wifi.join(secrets.ssid, secrets.pwd, secrets.join_opt); // init lwip network interface - var nic: net.Interface = .{ - .link = .{ - .ptr = wifi, - .recv = drivers.WiFi.recv, - .send = drivers.WiFi.send, - .ready = drivers.WiFi.ready, - }, - }; + var nic: net.Interface = .{ .link = wifi.link() }; try nic.init(wifi.mac, try secrets.nic_options()); // init server diff --git a/examples/raspberrypi/rp2xxx/src/net/udp.zig b/examples/raspberrypi/rp2xxx/src/net/udp.zig index a08fcbc49..0e6b34782 100644 --- a/examples/raspberrypi/rp2xxx/src/net/udp.zig +++ b/examples/raspberrypi/rp2xxx/src/net/udp.zig @@ -14,10 +14,10 @@ pub const microzig_options = microzig.Options{ }; const log = std.log.scoped(.main); -const net = @import("lwip/net.zig"); comptime { - _ = @import("lwip/exports.zig"); + _ = @import("lwip_exports.zig"); } +const net = @import("net"); const secrets = @import("secrets.zig"); pub fn main() !void { @@ -36,15 +36,9 @@ pub fn main() !void { try wifi.join(secrets.ssid, secrets.pwd, secrets.join_opt); // init lwip network interface - var nic: net.Interface = .{ - .link = .{ - .ptr = wifi, - .recv = drivers.WiFi.recv, - .send = drivers.WiFi.send, - .ready = drivers.WiFi.ready, - }, - }; + var nic: net.Interface = .{ .link = wifi.link() }; try nic.init(wifi.mac, try secrets.nic_options()); + // udp init var udp: net.Udp = try .init(&nic); // listen for udp packets on port 9999 and call on_recv for each received packet diff --git a/modules/network/build.zig b/modules/network/build.zig new file mode 100644 index 000000000..b777f7321 --- /dev/null +++ b/modules/network/build.zig @@ -0,0 +1,98 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const ethernet_header = 14; + + // Configurable options + const mem_size = b.option( + usize, + "mem_size", + "The size of the heap memory", + ) orelse 16 * 1024; + const pbuf_pool_size = b.option( + usize, + "pbuf_pool_size", + "The number of buffers in the packet buffer pool", + ) orelse 32; + const mtu = b.option( + u16, + "mtu", + "Layer 3 MTU, includes TCP/UDP and IP headers, don't include layer 2 Ethernet header", + ) orelse 1500; + const pbuf_header_length = b.option( + u16, + "pbuf_header_length", + "The number of bytes allocated in packet buffer before Ethernet header for use in the underlying link driver", + ) orelse 0; + const pbuf_length = b.option( + u16, + "pbuf_length", + "The size of each packet buffer in the pool", + ) orelse mtu + ethernet_header + pbuf_header_length; + + if (pbuf_length < mtu + ethernet_header + pbuf_header_length) { + @panic("Invalid lwip packet buffer length"); + } + + const lwip_mod = brk: { + var buf: [32]u8 = undefined; + + const foundation_dep = b.dependency("foundationlibc", .{ + .target = target, + .optimize = optimize, + .single_threaded = true, + }); + const lwip_dep = b.dependency("lwip", .{ + .target = target, + .optimize = optimize, + }); + const lwip_mod = lwip_dep.module("lwip"); + // Link libc + lwip_mod.linkLibrary(foundation_dep.artifact("foundation")); + + // MEM_ALIGNMENT of 4 bytes allows us to use packet buffers in u32 dma. + lwip_mod.addCMacro("MEM_ALIGNMENT", "4"); + lwip_mod.addCMacro("MEM_SIZE", to_s(&buf, mem_size)); + lwip_mod.addCMacro("PBUF_POOL_SIZE", to_s(&buf, pbuf_pool_size)); + lwip_mod.addCMacro("PBUF_LINK_HLEN", to_s(&buf, ethernet_header)); + lwip_mod.addCMacro("PBUF_POOL_BUFSIZE", to_s(&buf, pbuf_length)); + lwip_mod.addCMacro("PBUF_LINK_ENCAPSULATION_HLEN", to_s(&buf, pbuf_header_length)); + // 40 bytes IPv6 header, 20 bytes TCP header + lwip_mod.addCMacro("TCP_MSS", to_s(&buf, mtu - 40 - 20)); + + // Path to lwipopts.h + lwip_mod.addIncludePath(b.path("src/include")); + break :brk lwip_mod; + }; + + const net_mod = b.addModule("net", .{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/root.zig"), + .imports = &.{.{ + .name = "link", + .module = b.dependency("link", .{}).module("link"), + }}, + }); + net_mod.addImport("lwip", lwip_mod); + // Copy macros and include dirs from lwip to net, so we have same values + // when calling translate-c from cImport. + for (lwip_mod.c_macros.items) |m| { + net_mod.c_macros.append(b.allocator, m) catch @panic("out of memory"); + } + for (lwip_mod.include_dirs.items) |dir| { + net_mod.include_dirs.append(b.allocator, dir) catch @panic("out of memory"); + } + const options = b.addOptions(); + options.addOption(u16, "mtu", mtu); + options.addOption(u16, "pbuf_length", pbuf_length); + options.addOption(u16, "pbuf_header_length", pbuf_header_length); + net_mod.addOptions("config", options); +} + +fn to_s(buf: []u8, value: usize) []const u8 { + return std.fmt.bufPrint(buf, "{d}", .{value}) catch @panic("insufficient to_s buffer"); +} diff --git a/modules/network/build.zig.zon b/modules/network/build.zig.zon new file mode 100644 index 000000000..299247f83 --- /dev/null +++ b/modules/network/build.zig.zon @@ -0,0 +1,22 @@ +.{ + .name = .net, + .version = "0.0.0", + .fingerprint = 0xf2ea15ff2558583e, + .minimum_zig_version = "0.15.2", + .dependencies = .{ + .foundationlibc = .{ + .path = "../foundation-libc/", + }, + .lwip = .{ + .path = "../lwip/", + }, + .link = .{ + .path = "link/", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/modules/network/link/build.zig b/modules/network/link/build.zig new file mode 100644 index 000000000..d3972baa4 --- /dev/null +++ b/modules/network/link/build.zig @@ -0,0 +1,12 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + _ = b.addModule("link", .{ + .root_source_file = b.path("link.zig"), + .target = target, + .optimize = optimize, + }); +} diff --git a/modules/network/link/build.zig.zon b/modules/network/link/build.zig.zon new file mode 100644 index 000000000..25d7eb276 --- /dev/null +++ b/modules/network/link/build.zig.zon @@ -0,0 +1,11 @@ +.{ + .name = .link, + .version = "0.0.0", + .fingerprint = 0x36ac99f11956c760, + .minimum_zig_version = "0.15.2", + .paths = .{ + "build.zig", + "build.zig.zon", + "link.zig", + }, +} diff --git a/modules/network/link/link.zig b/modules/network/link/link.zig new file mode 100644 index 000000000..29213364b --- /dev/null +++ b/modules/network/link/link.zig @@ -0,0 +1,30 @@ +/// Physical/data-link layer interface for the network module. +/// +/// The type erased pointer to the data-link layer implementation. +ptr: *anyopaque, + +vtable: struct { + /// Receive data packet from the data link layer. + /// + /// Network module allocates and passes buffer to the recv function. If there + /// is no data waiting in the driver null is returned. + /// + /// If non null tuple is returned it contains start position of the Ethernet + /// header in that buffer and length of the data packet. + /// + /// Start position can be greater than zero if there is data link layer + /// specific header in the buffer after getting data from the driver. + recv: *const fn (*anyopaque, []u8) anyerror!?struct { usize, usize }, + + /// Send data packet to the data link layer. + send: *const fn (*anyopaque, []u8) Error!void, +}, + +pub const Error = error{ + /// Packet can't fit into link output buffer + OutOfMemory, + /// Link is disconnected + LinkDown, + /// All other errors + InternalError, +}; diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip/include/arch/cc.h b/modules/network/src/include/arch/cc.h similarity index 82% rename from examples/raspberrypi/rp2xxx/src/net/lwip/include/arch/cc.h rename to modules/network/src/include/arch/cc.h index 3b7e19576..7e974265f 100644 --- a/examples/raspberrypi/rp2xxx/src/net/lwip/include/arch/cc.h +++ b/modules/network/src/include/arch/cc.h @@ -6,7 +6,11 @@ typedef unsigned int sys_prot_t; +// Platform dependent methods, needs to be implemented for each chip. extern uint32_t lwip_rand(void); +extern uint32_t lwip_sys_now(void); + +// Implemented in network/root.zig extern void lwip_lock_interrupts(bool *state); extern void lwip_unlock_interrupts(bool state); extern void lwip_assert(const char *msg, const char *file, int line); @@ -41,4 +45,8 @@ extern void lwip_diag(const char *msg, const char *file, int line); #define SYS_ARCH_PROTECT(lev) lwip_lock_interrupts(&lev) #define SYS_ARCH_UNPROTECT(lev) lwip_unlock_interrupts(lev) +// Rename sys_now to lwip_sys_now +// https://github.com/lwip-tcpip/lwip/blob/6ca936f6b588cee702c638eee75c2436e6cf75de/src/include/lwip/sys.h#L446 +#define sys_now lwip_sys_now + #endif // lwip__cc_h diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip/include/lwipopts.h b/modules/network/src/include/lwipopts.h similarity index 65% rename from examples/raspberrypi/rp2xxx/src/net/lwip/include/lwipopts.h rename to modules/network/src/include/lwipopts.h index 9c2e2eaaf..c3b4ffac1 100644 --- a/examples/raspberrypi/rp2xxx/src/net/lwip/include/lwipopts.h +++ b/modules/network/src/include/lwipopts.h @@ -22,23 +22,6 @@ #define LWIP_DNS LWIP_UDP #define LWIP_MDNS_RESPONDER LWIP_UDP -// memory -#define MEM_ALIGNMENT 4 -#define MEM_SIZE (32 * 1024) // limit of dynamic mem alloc - -#define PBUF_POOL_SIZE 32 // number of preallocated buffers -#define PBUF_POOL_BUFSIZE 1540 // enough for 1500 MTU + headers (14 + 22 + 4) -#define PBUF_LINK_HLEN 14 -#define PBUF_LINK_ENCAPSULATION_HLEN 22 // CYW43 WiFi header space -// #define LWIP_NETIF_TX_SINGLE_PBUF 1 // reject chained TX (if available) - -#define MEMP_NUM_PBUF 32 -#define MEMP_NUM_RAW_PCB 32 -#define MEMP_NUM_TCP_PCB 8 -#define MEMP_NUM_TCP_SEG 32 -#define MEMP_NUM_UDP_PCB 8 -#define MEMP_NUM_SYS_TIMEOUT 16 - // callbacks #define LWIP_NETIF_LINK_CALLBACK 0 #define LWIP_NETIF_STATUS_CALLBACK 1 diff --git a/examples/raspberrypi/rp2xxx/src/net/lwip/net.zig b/modules/network/src/root.zig similarity index 87% rename from examples/raspberrypi/rp2xxx/src/net/lwip/net.zig rename to modules/network/src/root.zig index 2dce6479e..3e0e73fc9 100644 --- a/examples/raspberrypi/rp2xxx/src/net/lwip/net.zig +++ b/modules/network/src/root.zig @@ -1,6 +1,8 @@ /// Platform independent network library. Connects lwip with underlying network /// link interface. const std = @import("std"); +const config = @import("config"); +const Link = @import("link"); const assert = std.debug.assert; fn assert_panic(ok: bool, msg: []const u8) void { @@ -26,13 +28,7 @@ pub const Interface = struct { netif: lwip.netif = .{}, dhcp: lwip.dhcp = .{}, - - link: struct { - ptr: *anyopaque, - recv: *const fn (*anyopaque, []u8) anyerror!?struct { usize, usize }, - send: *const fn (*anyopaque, []u8) anyerror!void, - ready: *const fn (*anyopaque) bool, - }, + link: Link, pub const Options = struct { fixed: ?Fixed = null, @@ -94,7 +90,7 @@ pub const Interface = struct { netif.linkoutput = c_netif_linkoutput; netif.output = lwip.etharp_output; netif.output_ip6 = lwip.ethip6_output; - netif.mtu = sz.mtu; + netif.mtu = config.mtu; netif.flags = lwip.NETIF_FLAG_BROADCAST | lwip.NETIF_FLAG_ETHARP | lwip.NETIF_FLAG_ETHERNET | lwip.NETIF_FLAG_IGMP | lwip.NETIF_FLAG_MLD6; netif.hwaddr_len = lwip.ETH_HWADDR_LEN; @@ -104,11 +100,11 @@ pub const Interface = struct { fn c_on_netif_status(netif_c: [*c]lwip.netif) callconv(.c) void { const netif: *lwip.netif = netif_c; const self: *Self = @fieldParentPtr("netif", netif); - log.debug("netif status callback is_link_up: {}, is_up: {}, ready: {}, ip: {f}", .{ + log.debug("netif status callback is_link_up: {}, is_up: {}, ready: {}, ip: {s}", .{ netif.flags & lwip.NETIF_FLAG_LINK_UP > 0, netif.flags & lwip.NETIF_FLAG_UP > 0, self.ready(), - IPFormatter.new(netif.ip_addr), + lwip.ipaddr_ntoa(&netif.ip_addr), }); } @@ -119,12 +115,7 @@ pub const Interface = struct { var pbuf: *lwip.pbuf = pbuf_c; const self: *Self = @fieldParentPtr("netif", netif); - if (!self.link.ready(self.link.ptr)) { - log.err("linkouput link not ready", .{}); - return lwip.ERR_MEM; // lwip will try later - } - - if (lwip.pbuf_header(pbuf, sz.link_head) != 0) { + if (config.pbuf_header_length > 0 and lwip.pbuf_header(pbuf, config.pbuf_header_length) != 0) { log.err("can't get pbuf headroom len: {}, tot_len: {} ", .{ pbuf.len, pbuf.tot_len }); return lwip.ERR_ARG; } @@ -138,9 +129,13 @@ pub const Interface = struct { if (pbuf_c.*.next != null) _ = lwip.pbuf_free(pbuf); } - self.link.send(self.link.ptr, payload_bytes(pbuf)) catch |err| { + self.link.vtable.send(self.link.ptr, payload_bytes(pbuf)) catch |err| { log.err("link send {}", .{err}); - return lwip.ERR_ARG; + return switch (err) { + error.OutOfMemory => lwip.ERR_MEM, + error.LinkDown => lwip.ERR_IF, + else => lwip.ERR_ARG, + }; }; return lwip.ERR_OK; } @@ -159,22 +154,22 @@ pub const Interface = struct { // get packet buffer of the max size const pbuf: *lwip.pbuf = lwip.pbuf_alloc( lwip.PBUF_RAW, - sz.pbuf_pool, + config.pbuf_length, lwip.PBUF_POOL, ) orelse return error.OutOfMemory; assert_panic( - pbuf.next == null and pbuf.len == pbuf.tot_len and pbuf.len == sz.pbuf_pool, + pbuf.next == null and pbuf.len == pbuf.tot_len and pbuf.len == config.pbuf_length, "net.Interface.pool invalid pbuf allocation", ); // receive into that buffer - const head, const len = try self.link.recv(self.link.ptr, payload_bytes(pbuf)) orelse { + const head, const len = try self.link.vtable.recv(self.link.ptr, payload_bytes(pbuf)) orelse { // no data release packet buffer and exit loop _ = lwip.pbuf_free(pbuf); break; }; errdefer _ = lwip.pbuf_free(pbuf); // netif.input: takes ownership of pbuf on success // set payload header and len - if (lwip.pbuf_header(pbuf, -@as(lwip.s16_t, @intCast(head))) != 0) { + if (head > 0 and lwip.pbuf_header(pbuf, -@as(lwip.s16_t, @intCast(head))) != 0) { return error.InvalidPbufHead; } pbuf.len = @intCast(len); @@ -187,15 +182,23 @@ pub const Interface = struct { } } - pub fn log_stats(self: *Self) void { + pub fn log_mem_stats(self: *Self) void { _ = self; const stats = lwip.lwip_stats; - log.debug("stats ip_frag: {}", .{stats.ip_frag}); - log.debug("stats icpmp: {}", .{stats.icmp}); - log.debug("stats mem: {} ", .{stats.mem}); - for (stats.memp, 0..) |s, i| { - log.debug("stats memp {}: {}", .{ i, s.* }); - } + log.debug("stats mem: {}", .{stats.mem}); + log.debug("pbuf pool: {}", .{stats.memp[lwip.MEMP_PBUF_POOL].*}); + log.debug("memp upd pcb: {}", .{stats.memp[lwip.MEMP_UDP_PCB].*}); + log.debug("memp tcp pcb: {}", .{stats.memp[lwip.MEMP_TCP_PCB].*}); + log.debug("memp tcp listen pcb: {}", .{stats.memp[lwip.MEMP_TCP_PCB_LISTEN].*}); + log.debug("memp tcp seg: {}", .{stats.memp[lwip.MEMP_TCP_SEG].*}); + log.debug("memp reassdata: {}", .{stats.memp[lwip.MEMP_REASSDATA].*}); + log.debug("memp frag buf: {}", .{stats.memp[lwip.MEMP_FRAG_PBUF].*}); + log.debug("memp igmp group: {}", .{stats.memp[lwip.MEMP_IGMP_GROUP].*}); + log.debug("memp sys timeout: {}", .{stats.memp[lwip.MEMP_SYS_TIMEOUT].*}); + log.debug("memp nd6 queue: {}", .{stats.memp[lwip.MEMP_ND6_QUEUE].*}); + log.debug("memp ip6 reassdata: {}", .{stats.memp[lwip.MEMP_IP6_REASSDATA].*}); + log.debug("memp mld6 group: {}", .{stats.memp[lwip.MEMP_MLD6_GROUP].*}); + log.debug("memp pbuf: {}", .{stats.memp[lwip.MEMP_PBUF].*}); } }; @@ -562,48 +565,19 @@ fn c_err(res: anytype) Error!void { } } -const IPFormatter = struct { - addr: lwip.ip_addr_t, - - pub fn new(addr: lwip.ip_addr_t) IPFormatter { - return IPFormatter{ .addr = addr }; - } +export fn lwip_lock_interrupts(were_enabled: *bool) void { + _ = were_enabled; +} - pub fn format( - self: IPFormatter, - writer: anytype, - ) !void { - const ip4_addr: *const lwip.ip4_addr_t = @ptrCast(&self.addr); - try writer.writeAll(std.mem.sliceTo(lwip.ip4addr_ntoa(ip4_addr), 0)); - } -}; +export fn lwip_unlock_interrupts(enable: bool) void { + _ = enable; +} -// required buffer sizes -const sz = struct { - const pbuf_pool = lwip.PBUF_POOL_BUFSIZE; // 1540 = 1500 mtu + ethernet + link head/tail - const link_head = lwip.PBUF_LINK_ENCAPSULATION_HLEN; // 22 - const link_tail = 4; // reserved for the status in recv buffer - const ethernet = 14; // layer 2 ethernet header size - - // layer 3 (ip) mtu, - const mtu = pbuf_pool - link_head - link_tail - ethernet; // 1500 - - // ip v4 sizes - const v4 = struct { - // headers - const ip = 20; - const udp = 8; - const tcp = 20; - - const payload = struct { - const udp = mtu - ip - v4.udp; // 1472 - }; - }; -}; +export fn lwip_assert(msg: [*c]const u8, file: [*c]const u8, line: c_int) void { + log.err("assert: {s} in file: {s}, line: {}", .{ msg, file, line }); + @panic("lwip assert"); +} -// test lwipopts.h config -comptime { - assert(sz.pbuf_pool == 1540); - assert(sz.link_head == 22); - assert(sz.mtu == 1500); +export fn lwip_diag(msg: [*c]const u8, file: [*c]const u8, line: c_int) void { + log.debug("{s} in file: {s}, line: {}", .{ msg, file, line }); }