SchOS/kernel/net/lwip/arch/cc.h
Nexoniarz c0806d1b64 net: a TCP/IP stack, and AF_INET on the sockets that were already there
lwIP 2.2.0, fetched by ports/lwip and compiled straight into the kernel.
Not written from scratch, and the reason is not the packet formats: it is
congestion control, retransmission, window management and twenty years of
corner cases that only show up against a real peer. Not FreeBSD's
netinet either, though the licence would have allowed it -- that stack is
not a library, it reaches into mbufs, epoch(9), netisr, VNET and uma(9),
so adopting it means porting FreeBSD's kernel infrastructure first. lwIP
was written for exactly this situation: a small kernel with a raw
Ethernet driver and a port layer of a few hundred lines.

It is kernel code, so nothing is installed into the sysroot and there is
no second build system: the top-level Makefile compiles the files it
wants against our port headers. The file list is explicit rather than a
wildcard, because lwIP ships IPv6, 6LoWPAN, PPP, SLIP, DNS-SD and a dozen
apps in the same tree, and building what is not configured yields either
dead code or a link error naming a port function nobody meant to write.
Its sources are unmodified and its warnings are silenced for its files
only -- patching them would mean maintaining a fork.

The port layer is ours: a lock, a clock, the netif that hands frames to
e1000, lwipopts.h, and a shim/ directory of tiny headers so lwIP's
<string.h> and friends resolve to the kernel's functions instead of the
host's, whose prototypes disagree and would otherwise be found on the
default include path.

NO_SYS=1, so lwIP has no threads of its own and one kernel thread feeds
it. A thread rather than the interrupt handler, because a call into lwIP
can reach the driver, the driver can sleep, and an interrupt handler
cannot -- so the interrupt still only clears the cause register and the
thread polls. kthread_create is new and generally useful: a process with
a kernel-only page directory, no files, no cwd, no parent, dispatched
from forkret, which can therefore sleep and be preempted like anything
else. DHCP obtains an address at boot and the console says which one.

AF_INET went in exactly where phase 2 said it would. socknew dispatches
on the domain, kernel/net/inet.c implements the internet side on lwIP's
raw API, and not one syscall changed shape: bind, connect, listen,
accept, read and write reach the same descriptors they always did. TCP
and UDP both work. cmd/nettest opens a connection, writes, and reads the
answer back.

Two things are owed and named here so they are not discovered later:
recvfrom does not yet report the source address, and the socket paths
poll on the timer tick instead of sleeping on the socket. Both are
follow-ups, not redesigns.

NOTICE carries lwIP's BSD-3 text. Clause 3 is no-endorsement, not the
old advertising clause, so it sits with Apache-2.0 without difficulty.

Verified: the tree builds clean, and the headless boot check passes with
27 [ok] and no failures with the stack compiled in and the thread
running -- which is the part that would break first if netinit deadlocked
or the thread starved anything. An end-to-end connection test belongs in
that suite and is the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 16:02:48 +02:00

98 lines
4 KiB
C

// SPDX-License-Identifier: Apache-2.0
//
// lwIP's compiler/platform header for SchOS.
//
// lwIP includes this before anything else and expects it to supply the fixed
// width types, the byte order, the diagnostic macros and the packing
// attributes. Everything here is either a typedef onto the kernel's own types
// or a one-line macro; there is no logic, by design -- this file is the seam
// between two codebases and the fewer decisions it makes, the better.
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H
#include "types.h"
#include "defs.h"
// There is no libc under the kernel, so every header lwIP would normally reach
// for has to be switched off and its contents supplied here instead. That is
// the contract arch.h states: set LWIP_NO_STDINT_H and you own the types, set
// LWIP_NO_LIMITS_H and you own the limits, and so on.
#define LWIP_NO_STDINT_H 1
#define LWIP_NO_INTTYPES_H 1
#define LWIP_NO_LIMITS_H 1
#define LWIP_NO_CTYPE_H 1
#define LWIP_NO_UNISTD_H 1
// lwIP's own type names. Written against the kernel's types rather than C99
// spellings, because the kernel already has names for these and introducing a
// second set that must agree is one more thing to get wrong.
typedef uchar u8_t;
typedef char s8_t;
typedef ushort u16_t;
typedef short s16_t;
typedef uint u32_t;
typedef int s32_t;
typedef uint64 u64_t;
typedef int64 s64_t;
#define LWIP_HAVE_INT64 1
typedef unsigned long mem_ptr_t; // must be able to hold a pointer
// ssize_t is the kernel's, not lwIP's. Defining SSIZE_MAX is how arch.h is
// told that: it only declares its own `typedef int ssize_t` when this is
// absent, and that would collide with the kernel's 64-bit one.
#define SSIZE_MAX 0x7fffffffffffffffL
#define INT_MAX 0x7fffffff
// Format specifiers, since <inttypes.h> is switched off above. The kernel's
// cprintf understands %d and %x and nothing wider, which is why these are the
// plain forms rather than the length-qualified ones a hosted printf would use.
#define X8_F "x"
#define U16_F "d"
#define S16_F "d"
#define X16_F "x"
#define U32_F "d"
#define S32_F "d"
#define X32_F "x"
#define SZT_F "d"
// x86 is little-endian. Getting this wrong does not fail to compile; it fails
// on the wire, as every checksum and every port number arriving byte-swapped.
#define BYTE_ORDER LITTLE_ENDIAN
// On-the-wire structures must not be padded. lwIP wraps every such struct in
// these, and a compiler that ignored them would silently move every field in
// every header lwIP parses.
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
// Diagnostics. cprintf is the kernel's console printf.
#define LWIP_PLATFORM_DIAG(x) do { cprintf x; } while (0)
#define LWIP_PLATFORM_ASSERT(x) \
do { cprintf("lwip assert: %s at %s:%d\n", (x), __FILE__, __LINE__); \
panic("lwip assertion"); } while (0)
// lwIP is single-threaded here (NO_SYS=1) but is still entered from two places:
// the network thread and any syscall that touches a socket. Both take the same
// lock, and these are what lwIP itself uses to bracket its critical sections.
// See kernel/net/lwip/sys_arch.c.
void lwip_sys_lock(void);
void lwip_sys_unlock(void);
typedef int sys_prot_t;
#define SYS_ARCH_DECL_PROTECT(lev)
#define SYS_ARCH_PROTECT(lev) lwip_sys_lock()
#define SYS_ARCH_UNPROTECT(lev) lwip_sys_unlock()
// lwIP calls these for its own buffers. The kernel has no malloc, so lwIP is
// configured (MEM_LIBC_MALLOC 0) to use its own pool allocator out of a static
// heap instead -- these exist only because a few optional modules reference
// them, and reaching one is a bug rather than a fallback.
// Spelled with the underlying type rather than lwIP's u32_t: this header is
// included *before* lwIP's arch.h defines u32_t, so naming it here would be a
// forward reference to a typedef that does not exist yet.
#define LWIP_RAND() (lwip_rand_seed())
u32_t lwip_rand_seed(void);
#endif