Compare commits

..
Author SHA1 Message Date
t3hn3rd d3c164e8af Merge branch 'feature/vtop-fix' into feature/tcp
continuous-integration/drone/push Build is passing
2026-03-03 19:10:38 +00:00
t3hn3rd 5e9d0004d7 feature: implement TCP (RFC 793) protocol driver
continuous-integration/drone/push Build is passing
Add full TCP/IP stack implementation at L4 alongside UDP and ICMP.

Core Protocol Engine:
- TCP types in nettypes.pas (header, TCB, state enum, error codes,
  callbacks, OOO entry, socket structures)
- 3-way handshake (active & passive OPEN)
- Reliable data transfer with ACK and sequence number tracking
- Graceful close (FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT)
- RST send/receive handling
- Retransmission with exponential backoff (max 5 attempts)
- Pseudo-header based checksum (RFC 793)

Server Support:
- LISTEN → SYN_RECEIVED → ESTABLISHED passive open path
- Listen backlog enforcement (configurable per-socket)
- accept() to retrieve established child connections

Robustness & Hardening:
- Delayed ACK timer (~200ms)
- Nagle's algorithm (buffer small sends while unACKed data pending)
- Jacobson/Karels RTT estimation with adaptive RTO
- Congestion control (slow start / congestion avoidance)
- Zero-window probing with exponential backoff
- MSS option negotiation in SYN/SYN-ACK (Kind=2, Len=4)
- Keep-alive (30s idle, 10s interval, 5 probes)
- Out-of-order segment reassembly (4-slot buffer)

Terminal & Testing:
- tcpconnect: active open + send "Hello, World!"
- tcplisten: passive open with syslog output
- tcphttp: HTTP/1.0 GET client

Also add push_trace() instrumentation to net, eth2, arp, ipv4,
icmp, udp, and dhcp entry points for call tracing, and register
tcp in net.init.
2026-03-03 10:55:50 +00:00
47 changed files with 1059 additions and 5950 deletions
+8 -22
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# compile_lvgl.sh — Download LVGL v9.2 source and compile into lib/liblvgl.a
# Clone & compile in /tmp (fast container-local fs).
# Cache liblvgl.a on host mount (/code/lvgl/) to skip rebuild.
# Copy source + objects to /lvgl (host mount) for debugging.
set -e
LVGL_VERSION="v9.2.2"
@@ -10,7 +10,6 @@ LVGL_DIR="/tmp/lvgl"
OBJ_DIR="/tmp/lvgl_obj"
CONF_DIR="$(pwd)/lvglh"
OUT_DIR="$(pwd)/lib"
CACHE_DIR="/code/lvgl"
CC="gcc"
CFLAGS="-m32 -march=i686 -ffreestanding \
@@ -27,17 +26,6 @@ echo " "
echo "Compiling LVGL..."
echo " "
# If cached liblvgl.a exists on host mount, just copy it and skip everything
if [ -f "${CACHE_DIR}/liblvgl.a" ]; then
echo "Found cached liblvgl.a in ${CACHE_DIR}, copying to ${OUT_DIR}..."
cp "${CACHE_DIR}/liblvgl.a" "${OUT_DIR}/liblvgl.a"
echo "(Delete lvgl/liblvgl.a to force a full rebuild.)"
echo " "
echo "LVGL compilation complete (cached)."
echo " "
exit 0
fi
# Clone LVGL into /tmp (container-local)
rm -rf "$LVGL_DIR"
echo "Downloading LVGL ${LVGL_VERSION}..."
@@ -117,16 +105,14 @@ else
echo "No custom LVGL files in lvglh/."
fi
# Cache liblvgl.a and source to host mount for next build
echo "Caching LVGL to host mount..."
mkdir -p "$CACHE_DIR"
rm -rf ${CACHE_DIR}/* ${CACHE_DIR}/.[!.]* ${CACHE_DIR}/..?* 2>/dev/null || true
cp "${OUT_DIR}/liblvgl.a" "${CACHE_DIR}/liblvgl.a"
cp -a "$LVGL_DIR/src" "${CACHE_DIR}/src"
cp -a "$LVGL_DIR"/*.h "${CACHE_DIR}/" 2>/dev/null || true
cp -a "$OBJ_DIR" "${CACHE_DIR}/obj"
# Copy source + objects to /lvgl host mount for debugging
echo "Copying LVGL source and objects to host mount..."
mkdir /code/lvgl 2>/dev/null || true
rm -rf /code/lvgl/* /code/lvgl/.[!.]* /code/lvgl/..?* 2>/dev/null || true
cp -a "$LVGL_DIR/src" /code/lvgl/src
cp -a "$OBJ_DIR" /code/lvgl/obj
echo "Done."
echo " "
echo "LVGL compilation complete."
echo " "
echo " "
+1 -1
View File
@@ -4,4 +4,4 @@ echo "======================="
echo " "
echo "Compiling FPC Sources..."
echo " "
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/include/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
+5 -108
View File
@@ -12,118 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
{
ContextSwitcher - Custom IRQ0 handler for preemptive context switching.
Replaces the default ISR_32 with a naked assembly stub that saves the
interrupted context (PUSHAD + segment regs), calls a Pascal helper to
dispatch timer hooks, send EOI, and run the scheduler, then restores
the next process context (or idles) via the returned ESP.
@author(Kieron Morris <[email protected]>)
{
ContextSwitcher - Switch Process Contexts when preempted.
@author(Kieron Morris <[email protected]>)
}
unit contextswitcher;
{$ASMMODE intel}
interface
uses
idt, isrmanager, processmanager, proctypes, util, tracer;
{ Initialise: overwrite IDT gate 32 with our custom ISR. }
procedure init;
implementation
{ -----------------------------------------------------------------------
do_context_switch_work
Pascal helper called from the assembly ISR. Receives the current ESP
(pointing to the GS..EFLAGS save area), dispatches timer hooks, sends
EOI, runs the scheduler and returns the ESP for the next context.
Uses register calling convention: saved_esp in EAX, result in EAX.
----------------------------------------------------------------------- }
function do_context_switch_work(saved_esp : uint32) : uint32;
var
next : PProcessContext;
begin
{ 1. Save ESP into the current process }
processmanager.CurrentProcess^.SavedESP := saved_esp;
{ 2. Dispatch all hooks registered on interrupt 32 (timer hooks).
This fires TMR_0_ISR.Main which in turn fires BDA tick,
graphics refresh, USB hotplug, etc. }
isrmanager.dispatchHooks(32);
{ 3. Send End-Of-Interrupt to the master PIC }
outb($20, $20);
{ 4. Pick the next process to run (always returns non-nil; idle as fallback) }
next := processmanager.scheduler_pick_next;
processmanager.CurrentProcess := next;
{ 5. Return the selected process ESP }
do_context_switch_work := next^.SavedESP;
end;
{ -----------------------------------------------------------------------
context_switch_isr
Naked assembly ISR that replaces ISR_32 (IRQ0 / PIT timer).
Stack on entry (pushed by CPU):
[ESP+8] EFLAGS
[ESP+4] CS
[ESP+0] EIP
We push general-purpose and segment registers, call the Pascal helper,
switch ESP to the returned value, pop registers and IRETD.
----------------------------------------------------------------------- }
procedure context_switch_isr; assembler; nostackframe;
asm
{ Save all general-purpose registers (EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI) }
pushad
{ Save segment registers }
push ds
push es
push fs
push gs
{ Load kernel data segments }
mov ax, $10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
{ Call Pascal helper: EAX = current ESP (register calling convention).
Returns new ESP in EAX. }
mov eax, esp
call do_context_switch_work
mov esp, eax
{ Restore segment registers }
pop gs
pop fs
pop es
pop ds
{ Restore general-purpose registers }
popad
{ Return from interrupt }
iretd
end;
{ -----------------------------------------------------------------------
init
Override IDT gate 32 with our custom ISR. Must be called AFTER
isrmanager.init so that the default gate has been set up first.
----------------------------------------------------------------------- }
procedure init;
begin
idt.set_gate(32, uint32(@context_switch_isr), $08, ISR_RING_0);
tracer.push_trace('contextswitcher.init');
end;
implementation;
end.
+5 -2
View File
@@ -22,7 +22,7 @@ unit cpu;
interface
uses
util, RTC, stdio;
util, RTC;
type
PCapabilities_Old = ^TCapabilities_Old;
@@ -113,10 +113,12 @@ var
CAP_OLD, CAP_NEW : uint32;
procedure init();
procedure Terminal_Command_CPU(Params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
implementation
uses
stdio;
procedure getCPUIdentifier;
var
id0, id1, id2 : uint32;
@@ -323,6 +325,7 @@ end;
procedure init();
begin
stdio.registerCommand('CPU', @Terminal_Command_CPU, 'CPU Info.');
CPUID.Capabilities0:= PCapabilities_Old(@CAP_OLD);
CPUID.Capabilities1:= PCapabilities_New(@CAP_NEW);
getCPUIdentifier;
+2 -7
View File
@@ -158,9 +158,6 @@ procedure init;
{ Unit Tests }
procedure UnitTest;
{ Terminal command }
procedure terminal_command_usb(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
implementation
{ ========================= Globals ========================= }
@@ -957,14 +954,11 @@ end;
procedure usb_check_hotplug;
var
i : uint32;
count: uint32;
hc : PUSBHCDriver;
port : uint8;
begin
if HCList = nil then exit;
count := LL_Size(HCList);
if count = 0 then exit;
for i := 0 to count - 1 do begin
for i := 0 to LL_Size(HCList) - 1 do begin
hc := PUSBHCDriver(LL_Get(HCList, i));
if (hc <> nil) and hc^.PortChangePending then begin
hc^.PortChangePending := false;
@@ -1106,6 +1100,7 @@ begin
CompletionHookCount := 0;
for i := 0 to MAX_COMPLETION_HOOKS - 1 do
CompletionHooks[i] := nil;
stdio.registerCommand('USB', @terminal_command_usb, 'USB subsystem information.');
syslog.logln('USB Core', 'INIT END.');
pop_trace;
end;
+1 -5
View File
@@ -119,12 +119,8 @@ var
x32, y32 : sint32;
r : pchar;
begin
{ mouse_wait(0) checks OBF with a short timeout (100 iterations).
When it returns true, data is ready — read it directly with inb($60).
Do NOT call mouse_read here: it uses mouse_wait_long (100k iterations)
and would spin-block the ISR if the next packet byte hasn't arrived yet. }
while mouse_wait(0) do begin
b := inb($60);
b := mouse_read;
if Cycle = 0 then begin
if (b AND $08) = $08 then begin
Mouse_Byte[Cycle] := b;
-1
View File
@@ -256,7 +256,6 @@ type
OnReceive : TTCPReceiveCallback;
OnEvent : TTCPEventCallback;
UserData : void;
OwnerPID : uint32; { PID of owning process, 0 if none }
end;
TTCB = record
+3 -3
View File
@@ -25,7 +25,7 @@ uses
tracer, lmemorymanager,
util, lists,
net, nettypes, netutils,
eth2, ipv4, stdio;
eth2, ipv4;
type
PARPCacheRecord = ^TARPCacheRecord;
@@ -41,12 +41,11 @@ procedure sendGratuitous;
procedure sendRequest(ip : puint8);
procedure send(hType : uint16; pType : uint16; op : uint16; p_context : PPacketContext);
function resolveIP(ip : puint8) : puint8;
procedure terminal_command_arp(Params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
implementation
uses
strings;
stdio, strings;
var
Registered : Boolean = false;
@@ -310,6 +309,7 @@ begin
writeToLogLn(' L3/ARP: register');
Cache:= LL_New(sizeof(TARPCacheRecord));
eth2.registerTypePromisc($0806, @recv);
stdio.registerCommand('ARP', @terminal_command_arp, 'Get ARP Table.');
Registered:= true;
end;
pop_trace;
+3 -3
View File
@@ -26,18 +26,17 @@ uses
util, strings,
net, nettypes, netutils,
lists,
eth2, stdio;
eth2;
procedure send(p_data : void; p_len : uint16; p_context : PPacketContext);
procedure registerProtocol(Protocol_ID : uint8; recv_callback : TRecvCallback);
function getIPv4Config : PIPv4Configuration;
procedure register;
procedure terminal_command_ifconfig(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
implementation
uses
arp;
arp, stdio;
var
Registered : Boolean = false;
@@ -212,6 +211,7 @@ begin
end;
Config.UP:= false;
eth2.registerType($0800, @recv);
stdio.registerCommand('IFCONFIG', @terminal_command_ifconfig, 'Configure Network Settings.');
Registered:= true;
end;
pop_trace;
File diff suppressed because it is too large Load Diff
+5 -39
View File
@@ -22,7 +22,7 @@ unit tcp;
interface
uses
tracer, stdio,
tracer,
nettypes, netutils,
ipv4;
@@ -33,17 +33,13 @@ function accept(listener : PTCPSocket) : PTCPSocket;
function send(socket : PTCPSocket; p_data : void; p_len : uint16) : TTCPError;
function close(socket : PTCPSocket) : TTCPError;
function abort_connection(socket : PTCPSocket) : TTCPError;
procedure terminal_command_tcpconnect(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
procedure terminal_command_tcplisten(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
procedure terminal_command_tcphttp(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
implementation
uses
lmemorymanager, util, syslog, rand, lists,
net, TMR_0_ISR, bios_data_area,
strings, arp,
processmanager, proctypes;
stdio, strings, arp;
const
TCP_PROTOCOL_ID = $06;
@@ -229,7 +225,6 @@ begin
sock^.OnReceive := onRecv;
sock^.OnEvent := onEvt;
sock^.UserData := userData;
sock^.OwnerPID := 0;
tcb^.Socket := sock;
CreateSocket := sock;
end;
@@ -256,29 +251,10 @@ begin
end;
end;
procedure socket_cleanup(handle : void);
var
sock : PTCPSocket;
begin
sock := PTCPSocket(handle);
if sock = nil then exit;
sock^.OwnerPID := 0; { Prevent DestroySocket from trying to unbind again }
abort_connection(sock);
end;
procedure DestroySocket(sock : PTCPSocket);
var
owner : PProcessContext;
begin
push_trace('tcp.DestroySocket');
if sock <> nil then begin
{ Remove resource binding from owning process }
if sock^.OwnerPID <> 0 then begin
owner := processmanager.findByID(sock^.OwnerPID);
sock^.OwnerPID := 0;
if owner <> nil then
processmanager.unbindResourceNoCleanup(owner, void(sock));
end;
if sock^.TCB <> nil then begin
sock^.TCB^.Socket := nil;
DestroyTCB(sock^.TCB);
@@ -1393,12 +1369,6 @@ begin
sock := CreateSocket(tcb, context^.OnReceive, context^.OnEvent, context^.UserData);
{ Auto-bind socket to calling process for cleanup on process death }
if processmanager.CurrentProcess <> nil then begin
sock^.OwnerPID := processmanager.CurrentProcess^.ProcessID;
processmanager.bindResource(processmanager.CurrentProcess, rkSocket, void(sock), @socket_cleanup);
end;
AddTCB(tcb);
{ Send SYN (includes MSS option via SendSegment) }
@@ -1432,13 +1402,6 @@ begin
if tcb^.BacklogMax = 0 then tcb^.BacklogMax := 5; { default backlog }
sock := CreateSocket(tcb, context^.OnReceive, context^.OnEvent, context^.UserData);
{ Auto-bind socket to calling process for cleanup on process death }
if processmanager.CurrentProcess <> nil then begin
sock^.OwnerPID := processmanager.CurrentProcess^.ProcessID;
processmanager.bindResource(processmanager.CurrentProcess, rkSocket, void(sock), @socket_cleanup);
end;
AddTCB(tcb);
listen := sock;
@@ -1903,6 +1866,9 @@ begin
Connections := DL_New(sizeof(uint32));
ipv4.registerProtocol(TCP_PROTOCOL_ID, @ProcessPacket);
TMR_0_ISR.hook(uint32(@TimerTick));
stdio.registerCommand('TCPCONNECT', @terminal_command_tcpconnect, 'Connect to a TCP host and send Hello World.');
stdio.registerCommand('TCPLISTEN', @terminal_command_tcplisten, 'Listen on a TCP port and log received data.');
stdio.registerCommand('TCPHTTP', @terminal_command_tcphttp, 'Send HTTP GET to a host IP (port 80 default).');
Registered := true;
syslog.logln('TCP', 'TCP registered.');
end;
+1 -1
View File
@@ -112,7 +112,6 @@ function get_device_list() : PLinkedListBase;
procedure register_filesystem(filesystem : PFilesystem);
procedure register_volume(device : PStorage_Device; volume : PStorage_Volume);
procedure disk_command(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
//function writeNewFile(fileName : pchar; extension : pchar; buffer : puint32; size : uint32) : uint32;
//function readFile(fileName : pchar; extension : pchar; buffer : puint32; byteCount : puint32) : puint32;
@@ -232,6 +231,7 @@ begin
setworkingdirectory('.');
storageDevices:= ll_new(sizeof(TStorage_Device));
fileSystems:= ll_New(sizeof(TFilesystem));
stdio.registerCommand('DISK', @disk_command, 'Disk utility');
pop_trace();
end;
+5 -67
View File
@@ -104,10 +104,6 @@ function GetDirectories(Handle : uint32; Path : pchar) : PHashMap;
function PathValid(Path : pchar) : TIsPathValid;
function changeDirectory(Path : pchar) : TIsPathValid;
function getWorkingDirectory : pchar;
function makeAbsolutePathFrom(Path : pchar; BaseDir : pchar) : pchar;
function resolvePathFrom(Path : pchar; BaseDir : pchar) : TIsPathValid;
function GetDirectoryListingFrom(Path : pchar; BaseDir : pchar) : PHashMap;
function changeDirectoryFrom(Path : pchar; BaseDir : pchar; var NewDir : pchar) : TIsPathValid;
//VFS Functions
function newVirtualDirectory(Path : pchar) : TError;
@@ -612,67 +608,6 @@ begin
tracer.push_trace('vfs.getWorkingDirectory.exit');
end;
function makeAbsolutePathFrom(Path : pchar; BaseDir : pchar) : pchar;
var
AbsPath : pchar;
TempPath : pchar;
begin
if Path[0] = '/' then
AbsPath := stringCopy(Path)
else begin
if BaseDir[StringSize(BaseDir)-1] <> '/' then
TempPath := StringConcat(BaseDir, '/')
else
TempPath := stringCopy(BaseDir);
AbsPath := StringConcat(TempPath, Path);
kfree(void(TempPath));
end;
makeAbsolutePathFrom := AbsPath;
end;
function resolvePathFrom(Path : pchar; BaseDir : pchar) : TIsPathValid;
var
TempPath : pchar;
AbsPath : pchar;
begin
TempPath := makeAbsolutePathFrom(Path, BaseDir);
AbsPath := evaluatePath(TempPath);
kfree(void(TempPath));
resolvePathFrom := PathValid(AbsPath);
kfree(void(AbsPath));
end;
function GetDirectoryListingFrom(Path : pchar; BaseDir : pchar) : PHashMap;
var
TempPath : pchar;
AbsPath : pchar;
begin
TempPath := makeAbsolutePathFrom(Path, BaseDir);
AbsPath := evaluatePath(TempPath);
kfree(void(TempPath));
GetDirectoryListingFrom := GetDirectoryListing(AbsPath);
kfree(void(AbsPath));
end;
function changeDirectoryFrom(Path : pchar; BaseDir : pchar; var NewDir : pchar) : TIsPathValid;
var
TempPath : pchar;
AbsPath : pchar;
Validity : TIsPathValid;
begin
TempPath := makeAbsolutePathFrom(Path, BaseDir);
AbsPath := evaluatePath(TempPath);
kfree(void(TempPath));
Validity := PathValid(AbsPath);
if Validity = pvDirectory then
NewDir := AbsPath
else begin
NewDir := nil;
kfree(void(AbsPath));
end;
changeDirectoryFrom := Validity;
end;
{ Terminal Commands }
procedure VFS_COMMAND_PUSHD(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
@@ -811,8 +746,11 @@ begin
//outputln('VFS', makeRelative('/test/mydisk/mything', '/test/mydisk'));
//while true do begin end;
{ LS, CD, PUSHD, POPD are now handled as per-terminal builtins
in vterminal.pas so each terminal has its own working directory. }
{ Register Terminal Commands }
stdio.registerCommand('LS', @VFS_COMMAND_LS, 'List directory contents.');
stdio.registerCommand('CD', @VFS_COMMAND_CD, 'Set working directory.');
stdio.registerCommand('PUSHD', @VFS_COMMAND_PUSHD, 'Push the working directory.');
stdio.registerCommand('POPD', @VFS_COMMAND_POPD, 'Pop the working directory.');
//ht:= PHashMap(Root^.Reference);
//hashmap.add(ht, 'VDirectory', void(newDummyObject(otVDIRECTORY)));
+3 -3
View File
@@ -13,7 +13,7 @@
// limitations under the License.
{
Driver->Timer->TMR_0_ISR - 8khz Timer Driver.
Driver->Timer->TMR_0_ISR - 1024hz Timer Driver.
@author(Kieron Morris <[email protected]>)
}
@@ -37,7 +37,7 @@ var
Hooks : Array[1..MAX_HOOKS] of pp_hook_method;
Registered : boolean = false;
procedure Main; //IRQ0, ~8001hz
procedure Main; //IRQ0, 1024.19hz aprox
var
i : integer;
@@ -54,7 +54,7 @@ procedure register();
begin
if not registered then begin
asm
mov ax, 149
mov ax, 1193
out $40, al
mov al, ah
out $40, al
+52
View File
@@ -0,0 +1,52 @@
{
Driver->Timer->GraphicsRefresh - Timer-driven graphics refresh at ~120FPS.
Hooks into the 1024Hz timer (TMR_0_ISR) and calls the desktop, uidebug,
LVGL and video flush routines every ~9 ticks (1024/9 ≈ 113.8 FPS).
@author(Kieron Morris <[email protected]>)
}
unit graphicsrefresh;
interface
procedure init;
implementation
uses
util,
TMR_0_ISR,
desktop,
uidebug,
lvgl,
video,
syslog;
const
{ 1024 / 4 ≈ 256 FPS }
TICKS_PER_FRAME = 4;
var
TickCounter : uint32;
procedure on_tick(data : void);
begin
TickCounter := TickCounter + 1;
if TickCounter >= TICKS_PER_FRAME then begin
TickCounter := 0;
desktop.update;
uidebug.update;
lvgl_handler;
video.Flush;
end;
end;
procedure init;
begin
TickCounter := 0;
TMR_0_ISR.hook(uint32(@on_tick));
syslog.logln('GFXREFRESH', 'Hooked into 1024Hz timer (~120 FPS).');
end;
end.
+46
View File
@@ -0,0 +1,46 @@
{
Driver->Timer->USBHotplug - Timer-driven USB hotplug polling at ~1Hz.
Hooks into the 1024Hz timer (TMR_0_ISR) and calls
usbcore.usb_check_hotplug every 1024 ticks (approximately once per second).
@author(Kieron Morris <[email protected]>)
}
unit usbhotplug;
interface
procedure init;
implementation
uses
util,
TMR_0_ISR,
usbcore,
syslog;
const
{ 1024 ticks at 1024Hz ≈ 1 second }
TICKS_PER_POLL = 1024;
var
TickCounter : uint32;
procedure on_tick(data : void);
begin
TickCounter := TickCounter + 1;
if TickCounter >= TICKS_PER_POLL then begin
TickCounter := 0;
usbcore.usb_check_hotplug;
end;
end;
procedure init;
begin
TickCounter := 0;
TMR_0_ISR.hook(uint32(@on_tick));
syslog.logln('USBHOTPLUG', 'Hooked into 1024Hz timer (~1Hz polling).');
end;
end.
-11
View File
@@ -467,7 +467,6 @@ const
LV_KEY_PREV = 11;
LV_KEY_HOME = 2;
LV_KEY_END = 3;
LV_KEY_CTRLC = 128; { Ctrl+C — terminal interrupt }
{ Arc mode }
LV_ARC_MODE_NORMAL = 0;
@@ -1862,16 +1861,6 @@ begin
exit;
end;
{ Ctrl+C — inject custom key code so focused terminal can handle it }
if key_info.is_down_code and key_info.CTRL_DOWN and (key_info.key_code = ord('c')) then begin
next_head := (kb_head + 1) mod KB_BUF_SIZE;
if next_head <> kb_tail then begin
kb_buf[kb_head] := LV_KEY_CTRLC;
kb_head := next_head;
end;
exit;
end;
{ Map Asuro key codes to LVGL key codes }
case key_info.key_code of
$1B: k := LV_KEY_ESC;
@@ -365,7 +365,7 @@ begin
now_tick := lvgl_get_ticks;
if (now_tick - last_tick) >= FPS_INTERVAL then begin
current_fps := safeDiv32(frame_count * 8000, now_tick - last_tick);
current_fps := safeDiv32(frame_count * 1000, now_tick - last_tick);
frame_count := 0;
last_tick := now_tick;
+1 -45
View File
@@ -11,7 +11,7 @@ unit windows;
interface
uses
lvgl, mouse, serial, tracer, processmanager, proctypes;
lvgl, mouse, serial, tracer;
const
MAX_WINDOWS = 16;
@@ -55,7 +55,6 @@ type
on_close : TWinCloseCallback;
on_resize : TWinResizeCallback;
cursor_obj : Plv_obj; { cursor to keep on top }
owner_pid : uint32; { owning process PID; 0 = unowned }
end;
{ Create a new window. Returns window ID (0 = failure). }
@@ -82,15 +81,6 @@ function getWindowCount: uint32;
{ Set a callback to be notified when a window is resized. }
procedure setWindowResizeCallback(win_id: uint32; cb: TWinResizeCallback);
{ Assign an owning process to a window.
When reapOrphanedWindows detects the process has died, the window
is automatically closed via its on_close callback. }
procedure setWindowOwner(win_id: uint32; pid: uint32);
{ Check all windows for dead owner processes and close them.
Called once per frame from graphicsrefresh. }
procedure reapOrphanedWindows;
implementation
var
@@ -651,7 +641,6 @@ begin
wins[id].on_close := closeCB;
wins[id].on_resize := nil;
wins[id].cursor_obj := cursor;
wins[id].owner_pid := 0;
{ Bring to front }
bringToFront(id);
@@ -709,7 +698,6 @@ begin
wins[win_id].on_close := nil;
wins[win_id].on_resize := nil;
wins[win_id].cursor_obj := nil;
wins[win_id].owner_pid := 0;
tracer.pop_trace;
end;
@@ -758,36 +746,4 @@ begin
wins[win_id].on_resize := cb;
end;
{ ============================================================
Public: set window owner PID
============================================================ }
procedure setWindowOwner(win_id: uint32; pid: uint32);
begin
if (win_id < 1) or (win_id > MAX_WINDOWS) then exit;
if wins[win_id].state = wsNone then exit;
wins[win_id].owner_pid := pid;
end;
{ ============================================================
Public: reap windows whose owning process has died
============================================================ }
procedure reapOrphanedWindows;
var
i : uint32;
ctx : PProcessContext;
begin
for i := 1 to MAX_WINDOWS do begin
if wins[i].state = wsNone then continue;
if wins[i].owner_pid = 0 then continue;
ctx := processmanager.findByID(wins[i].owner_pid);
if (ctx = nil) or (ctx^.State = psFinished) or (ctx^.State = psError) then begin
{ Owner is dead — trigger close callback or destroy directly }
if wins[i].on_close <> nil then
wins[i].on_close(i)
else
destroyWindow(i);
end;
end;
end;
end.
+4 -2
View File
@@ -72,8 +72,6 @@ procedure register_driver(Driver_Name : PChar; DeviceID : PDeviceIdentifier; Loa
procedure register_driver_ex(Driver_Name : PChar; DeviceID : PDeviceIdentifier; Load_Callback : TDriverLoadCallback; force_load : boolean);
procedure register_device(Device_Name : PChar; DeviceID : PDeviceIdentifier; ptr : void);
procedure terminal_command_dev(Params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
var
Root : PDriverRegistration = nil;
Dev : PDeviceRegistration = nil;
@@ -330,6 +328,10 @@ end;
procedure init;
begin
push_trace('driver_management.init');
stdio.registerCommand('DEV', @terminal_command_dev, 'Driver Management Interface.');
//stdio.registerCommand('DRIVERSEX', @terminal_command_driversex, 'List all available drivers.');
//stdio.registerCommand('DRIVERS', @terminal_command_drivers, 'List loaded drivers.');
//stdio.registerCommand('DEVICES', @terminal_command_devices, 'List devices.');
pop_trace;
end;

Some files were not shown because too many files have changed in this diff Show More