flatbuffer base

This commit is contained in:
2026-03-30 16:18:21 +03:00
parent 9a4b78f272
commit d83cb7fe7b
41 changed files with 1553 additions and 491 deletions

28
AVRrc.toml Normal file
View File

@@ -0,0 +1,28 @@
[dev]
libpath = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc_build/device.so"
[mem]
[mem.ps]
start = 0
len = 1024
wordLen = 2
executable = true
[mem.reg_gp]
start = 2048
len = 32
wordLen = 1
[mem.reg_io]
start = 2080
len = 255
wordLen = 1
[mem.ds]
start = 2335
len = 65535
wordLen = 1

View File

@@ -4,13 +4,27 @@ SRC_DIR=src
INC_DIR=inc INC_DIR=inc
CC=gcc CC=gcc
OBJDUMP=objdump OBJDUMP=objdump
LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE) LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE)
# flatcc runtime and generated reader headers use const-dropping casts;
# include both as system headers so -Wcast-qual doesn't apply to them
SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/
STATIC_LIBS=crypto STATIC_LIBS=crypto
STANDART=c23 STANDART=c23
OPTIMIZE=-O3 OPTIMIZE=-O3
TARGET=main TARGET=main
FLATCC = deps/flatcc/bin/flatcc
FLATC = deps/flatbuffers/build/flatc
FBS_DIR = flatbuffers
PROTO_INC_DIR = inc/flatbuf_autogen
PROTO_SRC_DIR = src/flatbuf_autogen
PROTO_STAMP = $(BUILD_DIR)/.proto_stamp
PY_PROTO_DIR = hmmmm_scripts/flatbuf_autogen
PY_PROTO_STAMP = $(BUILD_DIR)/.py_proto_stamp
FBS_SOURCES := $(shell find $(FBS_DIR) -name '*.fbs')
DEFINES=#-DOPCODE_WORDSIZE=2 -DMEM_CELL_WORDS=1 -DPC_WORDSIZE=2 -DGP_REG_CELL_WORDS=1 -DIO_REG_CELL_WORDS=1 DEFINES=#-DOPCODE_WORDSIZE=2 -DMEM_CELL_WORDS=1 -DPC_WORDSIZE=2 -DGP_REG_CELL_WORDS=1 -DIO_REG_CELL_WORDS=1
@@ -24,9 +38,10 @@ DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno
PEDANTIC_FLAGS=-pedantic -pedantic-errors $(DISABLE_FLAGS) -Wall -Wcast-align -Wcast-qual -Wconversion -Wduplicated-branches -Wduplicated-cond -Werror -Wextra -Wfloat-equal -Wlogical-op -Wpedantic -Wredundant-decls -Wsign-conversion PEDANTIC_FLAGS=-pedantic -pedantic-errors $(DISABLE_FLAGS) -Wall -Wcast-align -Wcast-qual -Wconversion -Wduplicated-branches -Wduplicated-cond -Werror -Wextra -Wfloat-equal -Wlogical-op -Wpedantic -Wredundant-decls -Wsign-conversion
ANALYZER_FLAGS=-fanalyzer -fdiagnostics-show-option -fdiagnostics-color=always ANALYZER_FLAGS=-fanalyzer -fdiagnostics-show-option -fdiagnostics-color=always
LSECTIONS=-ffunction-sections -fdata-sections -Wl,--gc-sections LSECTIONS=-ffunction-sections -fdata-sections -Wl,--gc-sections
CFLAGS=$(C_DEFS) -g $(C_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP CFLAGS=$(C_DEFS) -g $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP # -fno-omit-frame-pointer -fno-inline -fno-lto
STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS)) STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS))
LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm # LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto
LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline
@@ -41,7 +56,7 @@ vpath %.c $(sort $(dir $(C_SOURCES)))
all: build all: build
build: date deps Dir target compile_commands build: date deps Dir proto target compile_commands
rebuild: clean | build rebuild: clean | build
@@ -54,7 +69,7 @@ $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ @$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
@$(OBJDUMP) -d -S $@ > $(BUILD_DIR)/$(notdir $(<:.c=.casm)) @$(OBJDUMP) -d -S $@ > $(BUILD_DIR)/$(notdir $(<:.c=.casm))
target: date $(BUILD_DIR)/$(TARGET).elf target: date proto $(BUILD_DIR)/$(TARGET).elf
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
@echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m' @echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m'
@@ -63,6 +78,16 @@ $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
deps: deps:
@make -C deps all @make -C deps all
proto: $(PROTO_STAMP)
$(PROTO_STAMP): $(FBS_SOURCES) | ProtoDir BuildDir
@echo -e '\033[1;32mFLATCC\tGenerating FlatBuffers headers\033[0m'
@for fbs in $(FBS_SOURCES); do \
echo -e '\033[1;32mFLATCC\t'$$fbs'\033[0m'; \
$(FLATCC) -a -I $(FBS_DIR) -o $(PROTO_INC_DIR) $$fbs || exit 1; \
done
@touch $@
BuildDir: BuildDir:
@@ -75,16 +100,34 @@ SrcDir:
IncDir: IncDir:
@mkdir -p $(INC_DIR) @mkdir -p $(INC_DIR)
Dir: BuildDir SrcDir IncDir ProtoDir:
@mkdir -p $(PROTO_INC_DIR)
@mkdir -p $(PROTO_SRC_DIR)
Dir: BuildDir SrcDir IncDir ProtoDir
.PHONY: clean deps python-proto: $(PY_PROTO_STAMP)
$(PY_PROTO_STAMP): $(FBS_SOURCES) $(FLATC) | $(PY_PROTO_DIR) BuildDir
@echo -e '\033[1;32mFLATC\tGenerating Python FlatBuffers bindings\033[0m'
@$(FLATC) --python --gen-all -I $(FBS_DIR) -o $(PY_PROTO_DIR) $(FBS_DIR)/proto.fbs
@touch $@
$(PY_PROTO_DIR):
@mkdir -p $@
$(FLATC):
@$(MAKE) -C deps flatbuffers/build/flatc
.PHONY: clean deps proto python-proto
clean: clean:
@rm -rf $(BUILD_DIR)/* @rm -rf $(BUILD_DIR)/* $(BUILD_DIR)/.* $(PROTO_INC_DIR)/* $(PROTO_SRC_DIR)/*
# @rm -f compile_commands.json # @rm -f compile_commands.json
@echo -e '\033[0;31mCleaned\033[0m' @echo -e '\033[0;31mCleaned\033[0m'
.NOTPARALLEL: date target rebuild deps .NOTPARALLEL: date target rebuild deps proto
date: date:
@echo -e '\033[1;32m'"Starting build at " | tr -d '\n' @echo -e '\033[1;32m'"Starting build at " | tr -d '\n'
@date @date

1
deps/.gitignore vendored
View File

@@ -3,3 +3,4 @@ ptQueue
tomlc99 tomlc99
wsServer wsServer
flatcc flatcc
flatbuffers

25
deps/Makefile vendored
View File

@@ -1,4 +1,4 @@
all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc/bin/flatcc flatbuffers/build/flatc
tomlc99: tomlc99:
@git clone https://github.com/cktan/tomlc99 @git clone https://github.com/cktan/tomlc99
@@ -7,13 +7,13 @@ tomlc99/libtoml.a: tomlc99
@make -C tomlc99 libtoml.a @make -C tomlc99 libtoml.a
flatcc_src: flatcc:
@git clone https://github.com/dvidelabs/flatcc @git clone https://github.com/dvidelabs/flatcc
flatcc: flatcc/bin/flatcc flatcc_src: flatcc/bin/flatcc
flatcc/lib/libflatccrt.a: flatcc/bin/flatcc flatcc/lib/libflatccrt.a: flatcc/bin/flatcc
flatcc/bin/flatcc: flatcc_src flatcc/bin/flatcc: flatcc
@flatcc/scripts/initbuild.sh make @flatcc/scripts/initbuild.sh make
@flatcc/scripts/build.sh @flatcc/scripts/build.sh
@@ -30,8 +30,23 @@ wsServer:
wsServer/libws.a: wsServer wsServer/libws.a: wsServer
@make -C wsServer @make -C wsServer
flatbuffers:
@git clone --depth=1 https://github.com/google/flatbuffers
flatbuffers/build/flatc: flatbuffers
@cmake -B flatbuffers/build -S flatbuffers \
-DCMAKE_BUILD_TYPE=Release \
-DFLATBUFFERS_BUILD_TESTS=OFF \
-DFLATBUFFERS_BUILD_FLATLIB=OFF \
-DFLATBUFFERS_BUILD_FLATHASH=OFF \
-DFLATBUFFERS_BUILD_GRPCTEST=OFF \
-DFLATBUFFERS_BUILD_SHAREDLIB=OFF \
-DFLATBUFFERS_INSTALL=OFF
@cmake --build flatbuffers/build --target flatc -j$(shell nproc)
clean: clean:
@rm -rf wsServer ptQueue tomlc99 flatcc @rm -rf wsServer ptQueue tomlc99 flatcc flatbuffers
.PHONY: all clean .PHONY: all clean

1
deps/flatbuffers vendored Submodule

Submodule deps/flatbuffers added at 4e582b0c1d

0
flamegraph.svg Normal file
View File

11
flatbuffers/auth/auth.fbs Normal file
View File

@@ -0,0 +1,11 @@
namespace hmmmm.auth;
// Client → Server: SHA-512(access_token + str(unix_timestamp / 30))
table AuthRequest {
hash: [ubyte]; // 64 bytes
}
// Server → Client: seat assigned, current emulation state echoed
table AuthResponse {
seat_id: uint64;
}

View File

@@ -0,0 +1,16 @@
namespace hmmmm.config;
// Global emulator clock constraints.
table ClockConfig {
// Maximum emulated ticks per real second.
// 0 = unlimited (run as fast as possible).
limiter: uint64;
}
// Clock source and frequency relationship for one device.
// Devices form a clock tree: each node derives its frequency from its src.
table DeviceClockConfig {
src: string; // id of the parent device; empty = root clock
divider: uint32 = 1;
multiplier: uint32 = 1;
}

View File

@@ -0,0 +1,20 @@
include "device.fbs";
include "mem_config.fbs";
include "clock.fbs";
namespace hmmmm.config;
// Top-level emulator / system configuration.
// Equivalent to glob.toml — describes a full composite device tree.
//
// File use: flatcc -a config/config.fbs → binary system config files
// WS use: embed in ConfigCtrlMessage when config-change protocol is ready
table SystemConfig {
devices: [DeviceEntry]; // ordered list; id must be unique
clock: ClockConfig;
projections: [Projection];
intercepts: [Intercept];
mem_setup: [MemSetup];
}
root_type SystemConfig;

View File

@@ -0,0 +1,39 @@
include "mem_config.fbs";
include "clock.fbs";
namespace hmmmm.config;
// Configuration for a single base device (leaf node in the device tree).
// Equivalent to a standalone device TOML (e.g. AVRrc.toml, gpio.toml).
table BaseDeviceConfig {
libpath: string;
mem_segments: [MemSegment];
}
// Override for one memory segment within a child device.
// Only fields that need changing from the base config must be set.
table MemSegOverride {
segment: string;
start: uint32;
len: uint32;
word_len: uint8;
executable: bool;
}
// One device entry in a composite configuration.
table DeviceEntry {
// Local identifier, unique within this composite (e.g. "core", "gpio_a").
id: string;
// Exactly one of config or config_path must be set.
// config: inline base device configuration.
// config_path: path to a serialised BaseDeviceConfig FlatBuffer file.
config: BaseDeviceConfig;
config_path: string;
clock: DeviceClockConfig;
overrides: [MemSegOverride];
}
// Root type when serialising a standalone device config to a binary file.
root_type BaseDeviceConfig;

View File

@@ -0,0 +1,73 @@
namespace hmmmm.config;
// Named offset within a segment — symbolic variable for protocol/tool use
// (e.g. PIN=0, PORT=1, DDR=2 in a GPIO device).
table NamedOffset {
name: string;
offset: uint32;
}
// A contiguous memory segment within a base device.
table MemSegment {
name: string;
start: uint32; // base address in device flat address space
len: uint32;
word_len: uint8 = 1; // word size in bytes
executable: bool = false;
variables: [NamedOffset]; // optional symbolic offsets within segment
}
// ── Projections ──────────────────────────────────────────────────────────────
// Maps a segment from one device into the flat address space of another.
// After projection, accesses to [base_seg + shift .. shift+len) in base_at
// are transparently forwarded to target_at:target_seg.
table Projection {
base_at: string; // device where the projection appears
base_seg: string; // segment in that device that is the projection window
target_at: string; // device owning the real data
target_seg: string;
shift: uint32 = 0; // offset from base_seg start
}
// ── Intercepts ───────────────────────────────────────────────────────────────
enum InterceptOp: byte {
op_read = 0,
op_write = 1,
op_both = 2,
}
enum InterceptMode: byte {
shadow_copy = 1, // duplicate write to point_at:point_addr as well
shadow_replace = 2, // redirect access to point_at:point_addr instead
callback = 3, // invoke a handler registered by point_at device
}
table Intercept {
name: string;
op: InterceptOp = op_both;
mode: InterceptMode = callback;
base_at: string; // device where the triggering access occurs
base_seg: string;
base_addr: uint32;
point_at: string; // device that handles/receives the intercept
point_seg: string;
point_addr: uint32;
}
// ── Memory initialisation ─────────────────────────────────────────────────────
enum OverflowBehaviour: byte {
overflow_error = 0, // abort if file is larger than segment
overflow_ignore = 1, // stop reading at segment end, no error
overflow_wrap = 2, // wrap around and continue writing from segment start
}
// Load initial segment contents from a file (e.g. firmware binary, /dev/urandom).
table MemSetup {
device: string;
segment: string;
filepath: string;
overflow: OverflowBehaviour = overflow_error;
}

View File

@@ -0,0 +1,8 @@
// Config change control — WIP.
// Schema stub; content will be defined when the config-change sub-protocol
// is specified (requires device-loading control in the protocol first).
namespace hmmmm.ctrl.config_ctrl;
table ConfigCtrlMessage {
// TODO
}

View File

@@ -0,0 +1,37 @@
namespace hmmmm.ctrl.config_notif;
// Notification subtype 0001 — a device's configuration was updated.
// Carries the path of the affected device; client should re-request full config.
table DeviceConfigUpdateNotif {
device_path: [string]; // hierarchical path, e.g. ["core"] or ["gpio_a"]
}
// Numeric ID assigned to one named segment within a device.
table SegIdEntry {
name: string;
id: uint32;
}
// Maps a hierarchical device path to a compact numeric device_id + segment IDs.
// Clients use these IDs in stream and mem packets instead of string paths.
table DeviceIdEntry {
path: [string]; // hierarchical device id, e.g. ["core"]
device_id: uint32;
seg_ids: [SegIdEntry];
}
// Notification subtype 0010 — full device-id / segment-id mapping table.
// Sent after auth and after any config change that affects the device tree.
table DeviceIdMappingNotif {
entries: [DeviceIdEntry];
}
union ConfigNotifPayload {
DeviceConfigUpdateNotif,
DeviceIdMappingNotif,
}
table ConfigNotifMessage {
tclk: uint64;
payload: ConfigNotifPayload;
}

View File

@@ -0,0 +1,39 @@
include "exec_ctrl.fbs";
include "exec_notif.fbs";
include "config_ctrl.fbs";
include "config_notif.fbs";
include "lost.fbs";
include "orphaned.fbs";
include "setup_buf.fbs";
include "qos.fbs";
namespace hmmmm.ctrl;
// ── Client → Server ──────────────────────────────────────────────────────────
union CtrlClientPayload {
hmmmm.ctrl.exec.ExecCtrlMessage,
hmmmm.ctrl.config_ctrl.ConfigCtrlMessage,
hmmmm.ctrl.lost.LostMessagesRequest,
hmmmm.ctrl.orphaned.OrphanedRequest,
hmmmm.ctrl.setup_buf.SetupBuf,
}
table CtrlClientMessage {
payload: CtrlClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union CtrlServerPayload {
hmmmm.ctrl.exec_notif.ExecNotifyMessage,
hmmmm.ctrl.config_notif.ConfigNotifMessage,
hmmmm.ctrl.lost.LostMessagesResponse,
hmmmm.ctrl.orphaned.OrphanedResponse,
hmmmm.ctrl.setup_buf.SetupBuf,
hmmmm.ctrl.qos.QosReport,
}
table CtrlServerMessage {
payload: CtrlServerPayload;
}

View File

@@ -0,0 +1,14 @@
namespace hmmmm.ctrl.exec;
enum ExecPrompt: byte {
_invalid = 0,
start = 1,
pause = 2,
resume = 3,
stop = 4,
reset = 5,
}
table ExecCtrlMessage {
prompt: ExecPrompt;
}

View File

@@ -0,0 +1,13 @@
namespace hmmmm.ctrl.exec_notif;
enum ExecState: byte {
still = 0,
executing = 1,
paused = 2,
stopped = 3,
}
table ExecNotifyMessage {
tclk: uint64;
state: ExecState;
}

View File

@@ -0,0 +1,19 @@
namespace hmmmm.ctrl.lost;
// Client → Server: request buffered (undelivered) messages for a seat.
table LostMessagesRequest {
seat_id: uint64;
clear: bool = false; // if true, drop the buffer after reading
}
// One buffered message that was not delivered before the seat disconnected.
table LostMessage {
original_nonce: uint64;
data: [ubyte]; // raw FlatBuffer bytes of the original ServerMessage
}
// Server → Client: buffered messages for the requested seat.
table LostMessagesResponse {
seat_id: uint64;
messages: [LostMessage];
}

View File

@@ -0,0 +1,16 @@
namespace hmmmm.ctrl.orphaned;
// Client → Server: enumerate disconnected seats that still have buffered messages.
// No body — request is the signal itself.
table OrphanedRequest {}
table OrphanedEntry {
seat_id: uint64;
disconnect_tclk: uint64; // virtual clock tick at time of disconnect
lost_count: uint32; // number of buffered messages waiting
}
// Server → Client: list of orphaned seats with pending buffers.
table OrphanedResponse {
entries: [OrphanedEntry];
}

View File

@@ -0,0 +1,10 @@
namespace hmmmm.ctrl.qos;
// Server → Client: connection quality snapshot.
// Sent periodically or on request (TBD).
table QosReport {
rtt_us: uint64; // measured round-trip time, microseconds
packets_sent: uint64;
packets_lost: uint32;
buf_pressure: float; // outgoing buffer fill level, 0.0 .. 1.0
}

View File

@@ -0,0 +1,8 @@
namespace hmmmm.ctrl.setup_buf;
// Client → Server: configure the lost-message buffer for this seat.
// Server → Client: echoes back the confirmed (possibly clamped) settings.
table SetupBuf {
lost_buf_size: uint32; // max buffered messages per seat
client_lifetime_ticks: uint64; // virtual ticks to keep buffer after disconnect
}

46
flatbuffers/mem/mem.fbs Normal file
View File

@@ -0,0 +1,46 @@
namespace hmmmm.mem;
// Client → Server: read a contiguous memory region. Server responds with
// MemReadResponse carrying the same nonce as the enclosing ClientMessage.
table MemReadRequest {
device_id: uint32;
seg_id: uint32;
offset: uint32;
length: uint32;
}
// Server → Client: memory region contents at the moment of the read.
table MemReadResponse {
tclk: uint64; // virtual clock tick at time of read
device_id: uint32;
seg_id: uint32;
offset: uint32;
data: [ubyte];
}
// Client → Server: write a contiguous memory region. No response.
table MemWriteRequest {
device_id: uint32;
seg_id: uint32;
offset: uint32;
data: [ubyte];
}
// ── Unions ───────────────────────────────────────────────────────────────────
union MemClientPayload {
MemReadRequest,
MemWriteRequest,
}
table MemClientMessage {
payload: MemClientPayload;
}
union MemServerPayload {
MemReadResponse,
}
table MemServerMessage {
payload: MemServerPayload;
}

42
flatbuffers/proto.fbs Normal file
View File

@@ -0,0 +1,42 @@
include "auth/auth.fbs";
include "control/control.fbs";
include "stream/stream.fbs";
include "mem/mem.fbs";
namespace hmmmm;
// ── Client → Server ──────────────────────────────────────────────────────────
union ClientPayload {
hmmmm.auth.AuthRequest,
hmmmm.ctrl.CtrlClientMessage,
hmmmm.stream.StreamClientMessage,
hmmmm.mem.MemClientMessage,
}
// Every frame sent by the client is a ClientMessage.
// nonce: client-chosen identifier echoed in the server response.
// Use 0xFFFFFFFFFFFFFFFF for fire-and-forget messages (no reply expected).
table ClientMessage {
nonce: uint64;
payload: ClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union ServerPayload {
hmmmm.auth.AuthResponse,
hmmmm.ctrl.CtrlServerMessage,
hmmmm.stream.StreamServerMessage,
hmmmm.mem.MemServerMessage,
}
// Every frame sent by the server is a ServerMessage.
// nonce: echoes the triggering ClientMessage nonce.
// 0xFFFFFFFFFFFFFFFF for unsolicited notifications (state changes, stream pushes).
table ServerMessage {
nonce: uint64;
payload: ServerPayload;
}
root_type ClientMessage;

View File

@@ -0,0 +1,27 @@
include "stream_reg.fbs";
include "stream_data.fbs";
namespace hmmmm.stream;
// ── Client → Server ──────────────────────────────────────────────────────────
union StreamClientPayload {
StreamRegRequest,
StreamDeregRequest,
StreamWritePush,
}
table StreamClientMessage {
payload: StreamClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union StreamServerPayload {
StreamRegConfirm,
StreamDataPush,
}
table StreamServerMessage {
payload: StreamServerPayload;
}

View File

@@ -0,0 +1,18 @@
namespace hmmmm.stream;
// Server → Client: memory region snapshot pushed by the server.
// tclk is the virtual clock tick at which this data became valid —
// clients may buffer and apply it at the appropriate simulation point.
table StreamDataPush {
stream_id: uint32;
tclk: uint64;
data: [ubyte];
}
// Client → Server: write into a registered write-mode stream.
// offset is relative to the registered region start.
table StreamWritePush {
stream_id: uint32;
offset: uint32;
data: [ubyte];
}

View File

@@ -0,0 +1,32 @@
namespace hmmmm.stream;
enum StreamMode: byte {
mode_read = 0,
mode_write = 1,
}
// Client → Server: subscribe to a memory region.
table StreamRegRequest {
device_id: uint32; // from DeviceIdMappingNotif
seg_id: uint32; // from DeviceIdMappingNotif
offset: uint32; // start offset within segment, in bytes
length: uint32; // region length, in bytes
mode: StreamMode;
}
// Client → Server: cancel a stream subscription.
// No confirmation is sent by the server.
table StreamDeregRequest {
stream_id: uint32;
}
// Server → Client: subscription confirmed, stream_id assigned.
// Echoes the original request fields for client-side reconciliation.
table StreamRegConfirm {
stream_id: uint32;
device_id: uint32;
seg_id: uint32;
offset: uint32;
length: uint32;
mode: StreamMode;
}

120
flatbuffers_example_main.c Normal file
View File

@@ -0,0 +1,120 @@
#include <stdio.h>
#include <stdlib.h>
#include "monster_reader.h"
#include "monster_verifier.h"
#include "monster_builder.h"
#undef ns
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x)
#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
int main()
{
flatcc_builder_t builder, *B = &builder;
flatcc_builder_init(B);
printf("[+] Builder initialized\n");
flatbuffers_string_ref_t weapon_one_name =
flatbuffers_string_create_str(B, "Sword");
flatbuffers_string_ref_t weapon_two_name =
flatbuffers_string_create_str(B, "Axe");
ns(Weapon_ref_t) sword =
ns(Weapon_create(B, weapon_one_name, 3));
ns(Weapon_ref_t) axe =
ns(Weapon_create(B, weapon_two_name, 5));
ns(Weapon_vec_start(B));
ns(Weapon_vec_push(B, sword));
ns(Weapon_vec_push(B, axe));
ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B));
uint8_t treasure[] = {0,1,2,3,4,5,6,7,8,9};
flatbuffers_uint8_vec_ref_t inventory =
flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
flatbuffers_string_ref_t name =
flatbuffers_string_create_str(B, "Orc");
uint16_t hp = 300;
uint16_t mana = 150;
ns(Vec3_t) pos = {1.0f, 2.0f, 3.0f};
ns(Vec3_vec_start(B));
ns(Vec3_vec_push(B, &pos));
ns(Vec3_vec_ref_t) path = ns(Vec3_vec_end(B));
ns(Equipment_union_ref_t) equipped =
ns(Equipment_as_Weapon(axe));
ns(Monster_create_as_root(
B, &pos, mana, hp, name,
inventory, ns(Color_Red),
weapons, equipped, path));
uint8_t *buf;
size_t size;
buf = flatcc_builder_finalize_buffer(B, &size);
printf("[+] Buffer built, size = %zu bytes\n", size);
// 🔍 Верификация
if (ns(Monster_verify_as_root(buf, size))) {
printf("[!] Buffer verification FAILED\n");
return -1;
}
printf("[+] Buffer verification OK\n");
// 📖 Чтение
ns(Monster_table_t) m = ns(Monster_as_root(buf));
printf("\n===== MONSTER DUMP =====\n");
printf("Name: %s\n", ns(Monster_name(m)));
printf("HP: %u\n", ns(Monster_hp(m)));
printf("Mana: %u\n", ns(Monster_mana(m)));
ns(Vec3_struct_t) p = ns(Monster_pos(m));
printf("Position: x=%.2f y=%.2f z=%.2f\n",
ns(Vec3_x(p)), ns(Vec3_y(p)), ns(Vec3_z(p)));
flatbuffers_uint8_vec_t inv = ns(Monster_inventory(m));
size_t inv_len = flatbuffers_uint8_vec_len(inv);
printf("Inventory (%zu): ", inv_len);
for (size_t i = 0; i < inv_len; i++) {
printf("%u ", flatbuffers_uint8_vec_at(inv, i));
}
printf("\n");
ns(Weapon_vec_t) wv = ns(Monster_weapons(m));
size_t wlen = ns(Weapon_vec_len(wv));
printf("Weapons (%zu):\n", wlen);
for (size_t i = 0; i < wlen; i++) {
ns(Weapon_table_t) w = ns(Weapon_vec_at(wv, i));
printf(" - %s (dmg=%u)\n",
ns(Weapon_name(w)),
ns(Weapon_damage(w)));
}
if (ns(Monster_equipped_type(m)) == ns(Equipment_Weapon)) {
ns(Weapon_table_t) w = ns(Monster_equipped(m));
printf("Equipped: %s (dmg=%u)\n",
ns(Weapon_name(w)),
ns(Weapon_damage(w)));
}
printf("========================\n\n");
free(buf);
flatcc_builder_clear(B);
return 0;
}

23
gpio.toml Normal file
View File

@@ -0,0 +1,23 @@
[dev]
libpath = "avr_gpio/out/device.so"
[mem.ext_reg_io]
start = 0
len = 255
wordLen = 1
[mem.extstate]
start = 256
len = 1
wordLen = 1
[mem.variables]
PIN = 0
PORT = 1
DDR = 2

View File

@@ -10,6 +10,7 @@
#include "sized_ptr.h" #include "sized_ptr.h"
#include "streamed.h" #include "streamed.h"
#include "flatcc/flatcc_builder.h"
typedef struct { typedef struct {
SizedPtr* bufs; SizedPtr* bufs;
@@ -30,6 +31,7 @@ typedef struct {
DeviceSegStreamReg** deviceStreamRegs; DeviceSegStreamReg** deviceStreamRegs;
uint8_t** devicesMem; uint8_t** devicesMem;
size_t devicesCount; size_t devicesCount;
flatcc_builder_t stream_builder;
} EmulContext; } EmulContext;

View File

@@ -1,10 +1,16 @@
#ifndef __PROTO_HANDLERS_CONTROL_H__ #ifndef __PROTO_HANDLERS_CONTROL_H__
#define __PROTO_HANDLERS_CONTROL_H__ #define __PROTO_HANDLERS_CONTROL_H__
#include "proto/msg.h" #include <stdint.h>
#include "client.h"
#include "context.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "control_reader.h"
void handleIncomingControlMessage(BaseMessage* msg, EmulContext* emulContext); void handleIncomingCtrlMessage(
hmmmm_ctrl_CtrlClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_CONTROL_H__
#endif //ifndef __PROTO_HANDLERS_CONTROL_H__

View File

@@ -1,10 +1,16 @@
#ifndef __PROTO_HANDLERS_MEM_H__ #ifndef __PROTO_HANDLERS_MEM_H__
#define __PROTO_HANDLERS_MEM_H__ #define __PROTO_HANDLERS_MEM_H__
#include "proto/msg.h" #include <stdint.h>
#include "context.h"
#include "client.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "mem_reader.h"
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext); void handleIncomingMemMessage(
hmmmm_mem_MemClientMessage_table_t msg,
#endif //ifndef __PROTO_HANDLERS_MEM_H__ uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_MEM_H__

View File

@@ -1,12 +1,19 @@
#ifndef __PROTO_HANDLERS_STREAM_H__ #ifndef __PROTO_HANDLERS_STREAM_H__
#define __PROTO_HANDLERS_STREAM_H__ #define __PROTO_HANDLERS_STREAM_H__
#include "proto/msg.h" #include <stdint.h>
#include "context.h"
#include "client.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "stream_reader.h"
void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId); void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId);
void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx); void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx);
void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext);
#endif //ifndef __PROTO_HANDLERS_STREAM_H__ void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_STREAM_H__

View File

@@ -2,25 +2,59 @@
#define __PROTO_MSG_H__ #define __PROTO_MSG_H__
#include "wsServer/include/ws.h" #include "wsServer/include/ws.h"
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "flatcc/flatcc_builder.h"
// Raw FlatBuffers frame copied from the WS thread, queued to the main loop.
typedef struct {
uint8_t* data;
size_t size;
} FbMessage;
typedef struct { typedef struct {
ws_cli_conn_t clientIdx; ws_cli_conn_t clientIdx;
uint8_t* msg; uint8_t* msg;
size_t msgLen; size_t msgLen;
} OutgoingMessage; } OutgoingMessage;
typedef struct { // Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer;
uint64_t nonce; // ownership is transferred to the caller (dispatchOutgoingMessage will free it).
uint8_t packetType; uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
uint8_t payloadHeader;
const void* payload;
size_t payloadLen;
} BaseMessage;
BaseMessage* parseMessage(const uint8_t* bytes, size_t size); // Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame.
uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut); uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out);
uint8_t* createDoneRegMessage(uint64_t nonce, uint8_t X, uint64_t devId, uint64_t segId, uint64_t startAddr, uint64_t segLength, uint32_t regId, size_t* lenOut);
uint8_t* createStreamSegmentPush(uint8_t mode, uint32_t regId, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut);
#endif //ifndef __PROTO_MSG_H__ // Build a ServerMessage{StreamServerMessage{StreamDataPush}} frame.
// B must be a pre-initialized flatcc_builder_t; it is reset before use
// so the caller does not need to reset it manually between calls.
uint8_t* fb_build_stream_data_push(
flatcc_builder_t *B,
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
const uint8_t* data, size_t data_len,
size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{SetupBuf}} frame (echo back to client).
uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size,
uint64_t client_lifetime_ticks, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{OrphanedResponse{entries=[]}}} frame.
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{LostMessagesResponse{seat_id, messages=[]}}} frame.
uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
// Build a ServerMessage{MemServerMessage{MemReadResponse}} frame.
uint8_t* fb_build_mem_read_response(
uint64_t nonce, uint64_t tclk,
uint32_t device_id, uint32_t seg_id, uint32_t offset,
const uint8_t* data, size_t data_len,
size_t* len_out);
// Build a ServerMessage{StreamServerMessage{StreamRegConfirm}} frame.
uint8_t* fb_build_stream_reg_confirm(
uint64_t nonce, uint32_t stream_id,
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
uint8_t mode, size_t* len_out);
#endif // __PROTO_MSG_H__

View File

@@ -185,9 +185,7 @@ void* outgoingMain(void* args)
{ {
panic("Got double read on buf %d while writing on buf %d\n", currBufIdx, currWritingIdxPtr); panic("Got double read on buf %d while writing on buf %d\n", currBufIdx, currWritingIdxPtr);
} }
// printf("\t%lu -> [%lu]\n", outMsg->msgLen, outMsg->clientIdx);
ws_sendframe_bin(outMsg->clientIdx, (const char*)outMsg->msg, outMsg->msgLen); ws_sendframe_bin(outMsg->clientIdx, (const char*)outMsg->msg, outMsg->msgLen);
// printf("running free on buf %d message %lu\n", currBufIdx, i);
free(outMsg->msg); free(outMsg->msg);
outMsg->msg = NULL; outMsg->msg = NULL;
// printf("done free on buf %d message %lu\n", currBufIdx, i); // printf("done free on buf %d message %lu\n", currBufIdx, i);
@@ -251,7 +249,10 @@ void mockDevice1(uint8_t* mem, uint64_t* readAddrs, size_t* readAddrsLen, uint64
void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, uint8_t* mem) void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, uint8_t* mem)
{ {
size_t mlen = 0; size_t mlen = 0;
uint8_t* msg = createStreamSegmentPush(reg->mode, reg->regId, *emulContext->clockCounter, mem + reg->startAddr, reg->segLen, &mlen); uint8_t* msg = fb_build_stream_data_push(
&emulContext->stream_builder,
UINT64_MAX, reg->regId, *emulContext->clockCounter,
mem + reg->startAddr, reg->segLen, &mlen);
dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen); dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen);
} }
@@ -320,17 +321,18 @@ int main(int argc, char** argv)
DeviceSegStreamReg* device0SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4); DeviceSegStreamReg* device0SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4);
NULL_GUARD(device0SegStreamRegs); NULL_GUARD(device0SegStreamRegs);
device0SegStreamRegs->allocatedSize = 0; device0SegStreamRegs->allocatedSize = 4;
device0SegStreamRegs->regCount = 0; device0SegStreamRegs->regCount = 0;
device0SegStreamRegs->regs = NULL; device0SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4);
NULL_GUARD(device0SegStreamRegs->regs);
DeviceSegStreamReg* device1SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4); DeviceSegStreamReg* device1SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4);
NULL_GUARD(device1SegStreamRegs); NULL_GUARD(device1SegStreamRegs);
device1SegStreamRegs->allocatedSize = 4;
device1SegStreamRegs->allocatedSize = 0;
device1SegStreamRegs->regCount = 0; device1SegStreamRegs->regCount = 0;
device1SegStreamRegs->regs = NULL; device1SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4);
NULL_GUARD(device1SegStreamRegs->regs);
DeviceSegStreamReg** deviceStreamRegs = malloc(sizeof(DeviceSegStreamReg*) * deviceCount); DeviceSegStreamReg** deviceStreamRegs = malloc(sizeof(DeviceSegStreamReg*) * deviceCount);
NULL_GUARD(deviceStreamRegs); NULL_GUARD(deviceStreamRegs);
@@ -409,8 +411,12 @@ int main(int argc, char** argv)
&outBufs, &outBufs,
deviceStreamRegs, deviceStreamRegs,
devicesMem, devicesMem,
deviceCount deviceCount,
{0} /* stream_builder — initialized below */
}; };
if (flatcc_builder_init(&emulContext.stream_builder)) {
panic("flatcc_builder_init failed\n");
}
uint8_t access_token[] = "jtd3dhBWG2p73mkRCyaZ63KLmOjl5OgItIDAPa8HcyGQI2SOFA6hsJVr1NiG2Y96AvwyYSShr4GAL3sjJEo1wsmCo2vuhiqRhj8HVRrOVocD2e1jtuMwMsCmizYakT63"; uint8_t access_token[] = "jtd3dhBWG2p73mkRCyaZ63KLmOjl5OgItIDAPa8HcyGQI2SOFA6hsJVr1NiG2Y96AvwyYSShr4GAL3sjJEo1wsmCo2vuhiqRhj8HVRrOVocD2e1jtuMwMsCmizYakT63";
@@ -443,6 +449,8 @@ int main(int argc, char** argv)
ptQueueElem* regQueueTail = regQ->tail; ptQueueElem* regQueueTail = regQ->tail;
uint8_t clients_try_timer = 100;
while(1) while(1)
{ {
ClientRegistrationEvent* payload = regQueueTail->payload; ClientRegistrationEvent* payload = regQueueTail->payload;
@@ -455,12 +463,19 @@ int main(int argc, char** argv)
} }
handleAllClients(&emulContext); if (clients_try_timer == 0)
{
handleAllClients(&emulContext);
clients_try_timer = 100;
}
else
{
clients_try_timer--;
}
uint8_t readReqIdx = atomic_load(&outBufs.readRequestIdx); uint8_t readReqIdx = atomic_load(&outBufs.readRequestIdx);
if(readReqIdx == outBufs.currWritingIdx || outBufs.bufs[outBufs.currWritingIdx].size >= outBufs.bufs[outBufs.currWritingIdx].allocatedSize / 2) if(readReqIdx == outBufs.currWritingIdx || outBufs.bufs[outBufs.currWritingIdx].size >= outBufs.bufs[outBufs.currWritingIdx].allocatedSize / 2)
{ {
uint8_t oldWriteIdx = outBufs.currWritingIdx;
uint8_t newWriteIdx = outBufs.currWritingIdx + 1; uint8_t newWriteIdx = outBufs.currWritingIdx + 1;
if(outBufs.bufs[outBufs.currWritingIdx].size != 0 || readReqIdx != newWriteIdx) if(outBufs.bufs[outBufs.currWritingIdx].size != 0 || readReqIdx != newWriteIdx)
{ {
@@ -468,13 +483,6 @@ int main(int argc, char** argv)
{ {
newWriteIdx = 0; newWriteIdx = 0;
} }
// printf("Switching write idx %d->%d\n", oldWriteIdx, newWriteIdx);
// if(readReqIdx == newWriteIdx)
// {
// printf("Flood control breach, waiting until outgoing buffers empty!\n");
// }
while(readReqIdx == newWriteIdx) while(readReqIdx == newWriteIdx)
{ {
my_sleep(1000); my_sleep(1000);
@@ -487,7 +495,7 @@ int main(int argc, char** argv)
if(emulState == EMUL_STATE_EXEC) if(emulState == EMUL_STATE_EXEC)
{ {
printf("Running...\n"); // printf("Running...\n");
device0readAddrsLen = 0; device0readAddrsLen = 0;
device0writeAddrsLen = 0; device0writeAddrsLen = 0;
@@ -507,7 +515,7 @@ int main(int argc, char** argv)
} }
else if(!utilizedFlag) else if(!utilizedFlag)
{ {
my_sleep(100); my_sleep(1000);
} }
} }

View File

@@ -32,7 +32,7 @@ void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ws_cli_conn_t clientIdx,
SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx]; SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
if(p->size + 1 >= p->allocatedSize) if(p->size + 1 >= p->allocatedSize)
{ {
printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx); // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2); OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2);
NULL_GUARD(newPtr); NULL_GUARD(newPtr);
p->ptr = newPtr; p->ptr = newPtr;

View File

@@ -1,19 +1,25 @@
#include "proto/handlers.h" #include "proto/handlers.h"
#include <stdio.h> #include <stdio.h>
#include "proto/enums.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include "proto/handlers/stream.h" #include "proto/handlers/stream.h"
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/handlers/mem.h" #include "proto/handlers/mem.h"
#include "proto/msg.h"
#include "proto_reader.h"
#include "proto_verifier.h"
#include "control_reader.h"
#include "stream_reader.h"
#include "mem_reader.h"
void handleCloseClient(EmulContext* emulContext, ClientContext* ctx) void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
{ {
if(ctx->streamRegIterator > 0) if (ctx->streamRegIterator > 0) {
{
unregisterClientStreams(emulContext, ctx); unregisterClientStreams(emulContext, ctx);
} }
} }
@@ -21,29 +27,25 @@ void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev) void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
{ {
if(ev->regType == REG_EVTYPE_CONNECT) if (ev->regType == REG_EVTYPE_CONNECT)
{ {
printf("open client %lu\n", ev->ctx->clientId); printf("open client %lu\n", ev->ctx->clientId);
LinkedListEntry* newClientsLinkedListHead = malloc(sizeof(LinkedListEntry)); LinkedListEntry* entry = malloc(sizeof(LinkedListEntry));
NULL_GUARD(newClientsLinkedListHead); NULL_GUARD(entry);
newClientsLinkedListHead->payload = ev->ctx; entry->payload = ev->ctx;
if(*emulContext->clientsHead != NULL) if (*emulContext->clientsHead != NULL) {
{ (*emulContext->clientsHead)->prevEntry = entry;
(*emulContext->clientsHead)->prevEntry = newClientsLinkedListHead;
} }
newClientsLinkedListHead->prevEntry = NULL; entry->prevEntry = NULL;
newClientsLinkedListHead->nextEntry = *emulContext->clientsHead; entry->nextEntry = *emulContext->clientsHead;
*emulContext->clientsHead = newClientsLinkedListHead; *emulContext->clientsHead = entry;
} }
else if (ev->regType == REG_EVTYPE_CLOSE) else if (ev->regType == REG_EVTYPE_CLOSE)
{ {
LinkedListEntry* clientEntry = *emulContext->clientsHead; LinkedListEntry* clientEntry = *emulContext->clientsHead;
while (clientEntry != NULL) {
while(clientEntry != NULL) if (clientEntry->payload == ev->ctx) {
{
if(clientEntry->payload == ev->ctx)
{
printf("close client %lu\n", ev->ctx->clientId); printf("close client %lu\n", ev->ctx->clientId);
handleCloseClient(emulContext, ev->ctx); handleCloseClient(emulContext, ev->ctx);
removeLinkedListEntry(emulContext->clientsHead, clientEntry); removeLinkedListEntry(emulContext->clientsHead, clientEntry);
@@ -61,33 +63,47 @@ void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
} }
static void handleIncomingMessage(
hmmmm_ClientMessage_table_t cm, ClientContext* ctx, EmulContext* emulContext)
void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{ {
switch (msg->packetType) uint64_t nonce = hmmmm_ClientMessage_nonce(cm);
hmmmm_ClientPayload_union_type_t ptype = hmmmm_ClientMessage_payload_type(cm);
printf("client %lu: nonce=%lu payload=%s\n",
ctx->clientId, nonce, hmmmm_ClientPayload_type_name(ptype));
switch (ptype)
{ {
case PACKET_TYPE_CTRL: case hmmmm_ClientPayload_CtrlClientMessage:
{ {
printf("CTRL packet\n"); hmmmm_ctrl_CtrlClientMessage_table_t ctrl =
handleIncomingControlMessage(msg, emulContext); (hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext);
break; break;
} }
case PACKET_TYPE_STREAM: case hmmmm_ClientPayload_StreamClientMessage:
{ {
printf("STREAM packet\n"); hmmmm_stream_StreamClientMessage_table_t stream =
handleIncomingStreamMessage(msg, ctx, emulContext); (hmmmm_stream_StreamClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingStreamMessage(stream, nonce, ctx, emulContext);
break; break;
} }
case PACKET_TYPE_MEM: case hmmmm_ClientPayload_MemClientMessage:
{ {
printf("MEM packet\n"); hmmmm_mem_MemClientMessage_table_t mem =
handleIncomingMemMessage(msg, ctx, emulContext); (hmmmm_mem_MemClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingMemMessage(mem, nonce, ctx, emulContext);
break;
}
case hmmmm_ClientPayload_AuthRequest:
{
// AuthRequest must only arrive before auth; drop if seen here
printf("client %lu: unexpected AuthRequest after auth\n", ctx->clientId);
break; break;
} }
default: default:
{ {
printf("Unsupported packet type: %u\n", msg->packetType); printf("client %lu: unknown payload type %u\n", ctx->clientId, ptype);
break; break;
} }
} }
@@ -97,46 +113,34 @@ void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* em
void handleAllClients(EmulContext* emulContext) void handleAllClients(EmulContext* emulContext)
{ {
LinkedListEntry* clientEntry = *emulContext->clientsHead; LinkedListEntry* clientEntry = *emulContext->clientsHead;
size_t handleLimit = 128; size_t handleLimit = 128;
while(clientEntry != NULL && handleLimit > 0) while (clientEntry != NULL && handleLimit > 0) {
{
handleLimit--; handleLimit--;
ClientContext* ctx = clientEntry->payload; ClientContext* ctx = clientEntry->payload;
if(!ctx->isAuthed)
{ if (!ctx->isAuthed) {
clientEntry = disconnectDueTimeout(emulContext, clientEntry); clientEntry = disconnectDueTimeout(emulContext, clientEntry);
if(clientEntry == NULL) if (clientEntry == NULL) break;
{ if (*emulContext->utilizedFlag) continue;
break; } else {
} FbMessage* fbmsg = ctx->incomeQ->head->payload;
if(*emulContext->utilizedFlag) if (fbmsg != NULL) {
{
continue;
}
}
else
{
void* payload = ctx->incomeQ->head->payload;
if(payload != NULL)
{
*emulContext->utilizedFlag = 1; *emulContext->utilizedFlag = 1;
BaseMessage* msg = payload;
printf("client %lu sent data: \nnonce %lu, ptype %4u, ph: %u\n", ctx->clientId, msg->nonce, msg->packetType, msg->payloadHeader);
handleIncomingMessage(msg, ctx, emulContext); if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
hmmmm_ClientMessage_table_t cm =
hmmmm_ClientMessage_as_root(fbmsg->data);
handleIncomingMessage(cm, ctx, emulContext);
} else {
printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId);
}
free(fbmsg->data);
free(payload); free(fbmsg);
ctx->incomeQ->head = ctx->incomeQ->head->nextEl; ctx->incomeQ->head = ctx->incomeQ->head->nextEl;
} }
clientEntry = clientEntry->nextEntry; clientEntry = clientEntry->nextEntry;
} }
} }
} }

View File

@@ -1,97 +1,105 @@
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include "my_mutex.h" #include "my_mutex.h"
#include "proto/enums.h"
#include "events.h" #include "events.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "proto/msg.h"
#include "proto/pack.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "proto/handlers/control.h"
uint8_t validateAccessTokenDeterministic(const uint8_t* data, const uint8_t* token, uint64_t timestamp) #include "proto_reader.h"
#include "proto_verifier.h"
#include "auth_reader.h"
static uint8_t validateAccessTokenDeterministic(
const uint8_t* data, const uint8_t* token, uint64_t timestamp)
{ {
char buf[1024]; char buf[1024];
sprintf(buf, "%s%lu", token, timestamp); (void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp);
uint8_t hash[SHA512_DIGEST_LENGTH]; uint8_t hash[SHA512_DIGEST_LENGTH];
SHA512((uint8_t*)buf, strlen(buf), hash); SHA512((const uint8_t*)buf, strlen(buf), hash);
uint8_t valid = 1; uint8_t valid = 1;
for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) if (data[i] != hash[i]) {
{
if (data[i] != hash[i])
{
valid = 0; valid = 0;
} }
} }
return valid; return valid;
} }
uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token) static uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token)
{ {
uint64_t t = (uint64_t)time(NULL) / 30; uint64_t t = (uint64_t)time(NULL) / 30;
return validateAccessTokenDeterministic(data, access_token, t)
uint8_t valid1 = validateAccessTokenDeterministic(data, access_token, t); || validateAccessTokenDeterministic(data, access_token, t - 1);
uint8_t valid2 = validateAccessTokenDeterministic(data, access_token, t - 1);
return valid1 || valid2;
} }
uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* msg, uint64_t msgSize, int msgType) uint8_t handle_auth(
ClientContext* cctx, ws_cli_conn_t client,
const uint8_t* msg, uint64_t msgSize, int msgType)
{ {
(void)msgType;
ServerContext* ctx = ws_get_server_context(client); ServerContext* ctx = ws_get_server_context(client);
if(msgSize != SHA512_DIGEST_LENGTH)
{ // Verify it's a valid ClientMessage FlatBuffer
int ret = ws_close_client(client); if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) {
if(ret == -1) printf("Auth: invalid FlatBuffer from client %lu\n", client);
{ ws_close_client(client);
printf("Unable to close client %lu\n", client);
}
return 0; return 0;
} }
uint8_t isValid = validateAccessToken(msg, (const uint8_t*)ctx->accessToken);
if(!isValid) hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg);
{
printf("Auth token invalid\n"); if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) {
int ret = ws_close_client(client); printf("Auth: expected AuthRequest, got type %u from client %lu\n",
if(ret == -1) hmmmm_ClientMessage_payload_type(cm), client);
{ ws_close_client(client);
printf("Unable to close client %lu\n", client);
}
return 0; return 0;
} }
hmmmm_auth_AuthRequest_table_t ar =
(hmmmm_auth_AuthRequest_table_t)hmmmm_ClientMessage_payload(cm);
flatbuffers_uint8_vec_t hash_vec = hmmmm_auth_AuthRequest_hash(ar);
size_t hash_len = flatbuffers_uint8_vec_len(hash_vec);
if (hash_len != SHA512_DIGEST_LENGTH) {
printf("Auth: wrong hash length %zu from client %lu\n", hash_len, client);
ws_close_client(client);
return 0;
}
if (!validateAccessToken(hash_vec, (const uint8_t*)ctx->accessToken)) {
printf("Auth: token invalid for client %lu\n", client);
ws_close_client(client);
return 0;
}
printf("Auth: token valid for client %lu\n", client);
char errbuf[1024]; char errbuf[1024];
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
printf("Auth token is valid\n"); if (ev == NULL) {
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
if(ev == NULL)
{
panic("Unable to allocate register event"); panic("Unable to allocate register event");
} }
ev->regType = REG_EVTYPE_AUTH; ev->regType = REG_EVTYPE_AUTH;
ev->ctx = cctx; ev->ctx = cctx;
ws_set_connection_context(client, cctx);
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
printf("Writing auth event\n"); printf("Writing auth event\n");
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
if(exitCode) if (exitCode) {
{
panic("Unable to push to reg queue: %s\n", errbuf); panic("Unable to push to reg queue: %s\n", errbuf);
} }
} }
@@ -99,42 +107,38 @@ uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* ms
} }
LinkedListEntry* disconnectDueTimeout(EmulContext* emulContext, LinkedListEntry* clientEntry) LinkedListEntry* disconnectDueTimeout(
EmulContext* emulContext, LinkedListEntry* clientEntry)
{ {
ClientContext* ctx = clientEntry->payload; ClientContext* cctx = clientEntry->payload;
uint64_t now = (uint64_t)time(NULL); uint64_t now = (uint64_t)time(NULL);
if(now - ctx->connectedAt <= 30) if (now - cctx->connectedAt <= 30) {
{
return clientEntry->nextEntry; return clientEntry->nextEntry;
} }
printf("Timeout on connection %lu\n", ctx->clientId); printf("Timeout on connection %lu\n", cctx->clientId);
int ret = ws_close_client(ctx->clientId); int ret = ws_close_client(cctx->clientId);
if(ret == -1) if (ret == -1) {
{
printf("Unable to close client\n"); printf("Unable to close client\n");
} }
LinkedListEntry* nextEntry = clientEntry->nextEntry; LinkedListEntry* nextEntry = clientEntry->nextEntry;
removeLinkedListEntry(emulContext->clientsHead, clientEntry); removeLinkedListEntry(emulContext->clientsHead, clientEntry);
clientEntry = nextEntry;
*emulContext->utilizedFlag = 1; *emulContext->utilizedFlag = 1;
return clientEntry; return nextEntry;
} }
void handleOnClientAuthDone(ClientContext* ctx, EmulContext* emulContext)
void handleOnClientAuthDone(ClientContext* cctx, EmulContext* emulContext)
{ {
uint8_t* framedata = malloc(sizeof(uint8_t) * 8); // Send AuthResponse with assigned seat_id
NULL_GUARD(framedata);
encodeUintToBytes(ctx->clientId, framedata);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, framedata, 8);
size_t len = 0; size_t len = 0;
uint8_t* msg = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, *emulContext->emulState, &len); uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, msg, len); dispatchOutgoingMessage(emulContext->outBufs, cctx->clientId, authResp, len);
// Send current execution state
len = 0;
uint8_t* stateMsg = fb_build_exec_notify(
UINT64_MAX, *emulContext->clockCounter, *emulContext->emulState, &len);
dispatchOutgoingMessage(emulContext->outBufs, cctx->clientId, stateMsg, len);
} }

View File

@@ -1,33 +1,107 @@
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/pack.h" #include <stdio.h>
#include "proto/enums.h"
#include "panic.h"
#include "state.h" #include "state.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "control_reader.h"
#include "exec_ctrl_reader.h"
#include "setup_buf_reader.h"
#include "orphaned_reader.h"
#include "lost_reader.h"
// Maps FlatBuffers ExecPrompt values to EMUL_STATE_OP_* constants.
// ExecPrompt: start=1, pause=2, resume=3, stop=4, reset=5
void handleIncomingControlMessage(BaseMessage* msg, EmulContext* emulContext) // EMUL_STATE_OP: START=1, PAUSE=2, RESUME=3, RESET=4, STOP=5
static uint8_t prompt_to_state_op(hmmmm_ctrl_exec_ExecPrompt_enum_t prompt)
{ {
if(msg->payloadHeader == CTRL_TYPE_EXEC) switch (prompt) {
{ case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START;
printf("ctrl exec\n"); case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE;
uint8_t stateOp = ((const uint8_t*)(msg->payload))[0]; case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME;
printf("state operation: %u\n", stateOp); case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP;
uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, stateOp); case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET;
if(newEmulState != *emulContext->emulState) default: return 0;
{ }
printf("Switch state %u -> %u\n", *emulContext->emulState, newEmulState); }
*emulContext->emulState = newEmulState;
size_t len = 0; void handleIncomingCtrlMessage(
uint8_t* notify = createControlNotifyMessage(msg->nonce, *emulContext->clockCounter, newEmulState, &len); hmmmm_ctrl_CtrlClientMessage_table_t msg,
broadcastClients(emulContext, notify, len); uint64_t nonce,
} ClientContext* ctx,
} EmulContext* emulContext)
else {
{ hmmmm_ctrl_CtrlClientPayload_union_type_t ptype =
printf("invalid payload header: %u\n", msg->payloadHeader); hmmmm_ctrl_CtrlClientMessage_payload_type(msg);
printf("[CTRL] client=%lu nonce=%lu type=%s\n",
ctx->clientId, nonce,
hmmmm_ctrl_CtrlClientPayload_type_name(ptype));
if (ptype == hmmmm_ctrl_CtrlClientPayload_ExecCtrlMessage)
{
hmmmm_ctrl_exec_ExecCtrlMessage_table_t exec =
(hmmmm_ctrl_exec_ExecCtrlMessage_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
hmmmm_ctrl_exec_ExecPrompt_enum_t prompt =
hmmmm_ctrl_exec_ExecCtrlMessage_prompt(exec);
printf("[CTRL/EXEC] prompt=%s\n", hmmmm_ctrl_exec_ExecPrompt_name(prompt));
uint8_t state_op = prompt_to_state_op(prompt);
if (state_op == 0) {
printf("[CTRL/EXEC] unknown prompt, ignoring\n");
return;
}
uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op);
*emulContext->emulState = new_state;
printf("[CTRL/EXEC] state -> %u\n", new_state);
size_t msg_len;
uint8_t* out = fb_build_exec_notify(0, *emulContext->clockCounter, new_state, &msg_len);
broadcastClients(emulContext, out, msg_len);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_SetupBuf)
{
hmmmm_ctrl_setup_buf_SetupBuf_table_t sb =
(hmmmm_ctrl_setup_buf_SetupBuf_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
uint32_t lost_buf_size = hmmmm_ctrl_setup_buf_SetupBuf_lost_buf_size(sb);
uint64_t lifetime_ticks = hmmmm_ctrl_setup_buf_SetupBuf_client_lifetime_ticks(sb);
printf("[CTRL/SETUP] lost_buf_size=%u lifetime_ticks=%lu\n",
lost_buf_size, lifetime_ticks);
size_t msg_len;
uint8_t* out = fb_build_setup_buf(nonce, lost_buf_size, lifetime_ticks, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_OrphanedRequest)
{
printf("[CTRL/ORPHANED] returning empty list\n");
size_t msg_len;
uint8_t* out = fb_build_orphaned_response(nonce, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest)
{
hmmmm_ctrl_lost_LostMessagesRequest_table_t req =
(hmmmm_ctrl_lost_LostMessagesRequest_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
uint64_t seat_id = hmmmm_ctrl_lost_LostMessagesRequest_seat_id(req);
printf("[CTRL/LOST] seat_id=%lu returning empty list\n", seat_id);
size_t msg_len;
uint8_t* out = fb_build_lost_messages_response(nonce, seat_id, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
} }
} }

View File

@@ -1,80 +1,92 @@
#include "proto/handlers/mem.h" #include "proto/handlers/mem.h"
#include "proto/enums.h"
#include "proto/pack.h" #include <stdio.h>
#include "panic.h"
#include <string.h> #include <string.h>
#include "proto/msg.h"
#include "proto/dial.h"
#include "mem_reader.h"
#define DEVICE_MEM_SIZE ((size_t)(128 * 1024))
uint8_t* createMemReadResponseMessage(uint64_t nonce, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut) void handleIncomingMemMessage(
hmmmm_mem_MemClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
*lenOut = 9 + 8 + payloadLen; hmmmm_mem_MemClientPayload_union_type_t ptype =
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); hmmmm_mem_MemClientMessage_payload_type(msg);
NULL_GUARD(outmsg);
encodeUintToBytes(nonce, outmsg); printf("[MEM] client=%lu nonce=%lu type=%s\n",
ctx->clientId, nonce,
hmmmm_mem_MemClientPayload_type_name(ptype));
outmsg[8] = PACKET_TYPE_MEM << 4; if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest)
outmsg[8] |= MEM_TYPE_READ_RESP;
encodeUintToBytes(clockCounter, outmsg + 9);
memcpy(outmsg + 9 + 8, payload, payloadLen);
return outmsg;
}
void handleIncomingMemReadReq(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{
uint64_t devId = decodeBytesToU64(msg->payload);
uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
uint64_t readLen = decodeBytesToU64(msg->payload + 24);
if(devId >= emulContext->devicesCount)
{ {
return; hmmmm_mem_MemReadRequest_table_t req =
(hmmmm_mem_MemReadRequest_table_t)
hmmmm_mem_MemClientMessage_payload(msg);
uint32_t dev_id = hmmmm_mem_MemReadRequest_device_id(req);
uint32_t seg_id = hmmmm_mem_MemReadRequest_seg_id(req);
uint32_t offset = hmmmm_mem_MemReadRequest_offset(req);
uint32_t length = hmmmm_mem_MemReadRequest_length(req);
printf("[MEM/READ] device=%u seg=%u offset=%u len=%u\n",
dev_id, seg_id, offset, length);
if (dev_id >= (uint32_t)emulContext->devicesCount) {
printf("[MEM/READ] invalid device %u\n", dev_id);
return;
}
if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
printf("[MEM/READ] out of bounds\n");
return;
}
const uint8_t* base = emulContext->devicesMem[dev_id];
size_t out_len;
uint8_t* out = fb_build_mem_read_response(
nonce, *emulContext->clockCounter,
dev_id, seg_id, offset,
base + offset, (size_t)length,
&out_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, out_len);
} }
else if (ptype == hmmmm_mem_MemClientPayload_MemWriteRequest)
//TODO: lookup config for global addr conversion
uint64_t globalAddr = startAddr + segId * 128;
uint8_t* readPtr = emulContext->devicesMem[devId] + globalAddr;
size_t outLen = 0;
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, readPtr, readLen, &outLen);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, outMsg, outLen);
}
void handleIncomingMemWrite(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{
uint64_t devId = decodeBytesToU64(msg->payload);
uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
if(devId >= emulContext->devicesCount)
{ {
return; hmmmm_mem_MemWriteRequest_table_t req =
} (hmmmm_mem_MemWriteRequest_table_t)
hmmmm_mem_MemClientMessage_payload(msg);
//TODO: lookup config for global addr conversion uint32_t dev_id = hmmmm_mem_MemWriteRequest_device_id(req);
uint64_t globalAddr = startAddr + segId * 128; uint32_t seg_id = hmmmm_mem_MemWriteRequest_seg_id(req);
uint32_t offset = hmmmm_mem_MemWriteRequest_offset(req);
flatbuffers_uint8_vec_t data = hmmmm_mem_MemWriteRequest_data(req);
size_t data_len = flatbuffers_uint8_vec_len(data);
uint8_t* writePtr = emulContext->devicesMem[devId] + globalAddr; printf("[MEM/WRITE] device=%u seg=%u offset=%u len=%zu\n",
dev_id, seg_id, offset, data_len);
memcpy(writePtr, msg->payload + 24, msg->payloadLen - 24); if (dev_id >= (uint32_t)emulContext->devicesCount) {
} printf("[MEM/WRITE] invalid device %u\n", dev_id);
return;
}
if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
printf("[MEM/WRITE] out of bounds\n");
return;
}
uint8_t* base = emulContext->devicesMem[dev_id];
memcpy(base + offset, data, data_len);
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) size_t out_len;
{ uint8_t* out = fb_build_mem_read_response(
if(msg->payloadHeader == MEM_TYPE_READ_REQ) nonce, *emulContext->clockCounter,
{ dev_id, seg_id, offset,
handleIncomingMemReadReq(msg, ctx, emulContext); base + offset, data_len,
} &out_len);
else if(msg->payloadHeader == MEM_TYPE_WRITE_PUSH) dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, out_len);
{
handleIncomingMemWrite(msg, ctx, emulContext);
} }
} }

View File

@@ -1,26 +1,31 @@
#include "proto/handlers/stream.h" #include "proto/handlers/stream.h"
#include <stdio.h>
#include <stdlib.h>
#include "streamed.h" #include "streamed.h"
#include "proto/pack.h"
#include "proto/enums.h"
#include "panic.h" #include "panic.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "stream_reader.h"
#include "stream_reg_reader.h"
// ── Stream registration bookkeeping (unchanged logic) ────────────────────────
void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId) void unregisterClientStream(
EmulContext* emulContext, ClientContext* ctx, uint32_t regId)
{ {
for(size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) {
{
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId]; DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId];
for(size_t i = 0; i < deviceRegs->regCount; i++) for (size_t i = 0; i < deviceRegs->regCount; i++) {
{
StreamReg* iReg = &deviceRegs->regs[i]; StreamReg* iReg = &deviceRegs->regs[i];
if(iReg->clientContext->clientId == ctx->clientId && iReg->regId == regId) if (iReg->clientContext->clientId == ctx->clientId
&& iReg->regId == regId)
{ {
printf("Discard stream %u register for client %lu\n", regId, ctx->clientId); printf("Discard stream %u for client %lu\n", regId, ctx->clientId);
deviceRegs->regCount -= 1; deviceRegs->regCount--;
for(size_t j = i; j < deviceRegs->regCount; j++) for (size_t j = i; j < deviceRegs->regCount; j++) {
{
deviceRegs->regs[j] = deviceRegs->regs[j + 1]; deviceRegs->regs[j] = deviceRegs->regs[j + 1];
} }
break; break;
@@ -31,94 +36,112 @@ void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32
void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx) void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx)
{ {
for(size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) {
{
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId]; DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId];
StreamReg* newStreamRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount); StreamReg* newRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount);
NULL_GUARD(newStreamRegs); NULL_GUARD(newRegs);
size_t newStreamRegsId = 0; size_t newCount = 0;
for(size_t i = 0; i < deviceRegs->regCount; i++) for (size_t i = 0; i < deviceRegs->regCount; i++) {
{
StreamReg* reg = &deviceRegs->regs[i]; StreamReg* reg = &deviceRegs->regs[i];
if(reg->clientContext->clientId != ctx->clientId) if (reg->clientContext->clientId != ctx->clientId) {
{ newRegs[newCount++] = *reg;
newStreamRegs[newStreamRegsId] = *reg; } else {
newStreamRegsId++; printf("Removing stream reg [%u] for client %lu\n",
} reg->regId, ctx->clientId);
else
{
printf("Removing reg %d mode stream for %lu/%lu+%lu: [%u]\n", reg->mode, deviceId, reg->startAddr, reg->segLen, reg->regId);
} }
} }
free(deviceRegs->regs); free(deviceRegs->regs);
deviceRegs->regCount = newStreamRegsId; deviceRegs->regCount = newCount;
deviceRegs->regs = newStreamRegs;
}
}
void handleStreamRegMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext, uint8_t X)
{
uint64_t devId = decodeBytesToU64(msg->payload);
uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
uint64_t segLen = decodeBytesToU64(msg->payload + 24);
if(devId >= emulContext->devicesCount)
{
return;
}
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[devId];
if(deviceRegs->regCount + 1 >= deviceRegs->allocatedSize)
{
size_t newAllocatedSize = deviceRegs->allocatedSize;
if(newAllocatedSize <= deviceRegs->regCount + 1)
{
newAllocatedSize = deviceRegs->regCount + 1;
}
StreamReg* newRegs = realloc(deviceRegs->regs, sizeof(StreamReg) * newAllocatedSize);
NULL_GUARD(newRegs);
deviceRegs->allocatedSize = newAllocatedSize;
deviceRegs->regs = newRegs; deviceRegs->regs = newRegs;
} }
//TODO: lookup config for global segment addr conversion
uint64_t globalStartAddr = startAddr + segId * 128;
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount];
reg->clientContext = ctx;
reg->segLen = segLen;
reg->startAddr = globalStartAddr;
reg->regId = ctx->streamRegIterator;
reg->mode = X;
ctx->streamRegIterator++;
deviceRegs->regCount++;
printf("Done registering %d mode stream for %lu/%lu+%lu: [%u]\n", X, devId, globalStartAddr, segLen, reg->regId);
size_t len = 0;
uint8_t* notifMsg = createDoneRegMessage(msg->nonce, X, devId, segId, startAddr, segLen, reg->regId, &len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, notifMsg, len);
} }
void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
// ── Incoming stream messages ──────────────────────────────────────────────────
void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
uint8_t X = msg->payloadHeader >> 3; hmmmm_stream_StreamClientPayload_union_type_t ptype =
uint8_t streamType = msg->payloadHeader & 0b111; hmmmm_stream_StreamClientMessage_payload_type(msg);
if(streamType == STREAM_TYPE_REG_REQUEST) printf("[STREAM] client=%lu nonce=%lu type=%s\n",
ctx->clientId, nonce,
hmmmm_stream_StreamClientPayload_type_name(ptype));
if (ptype == hmmmm_stream_StreamClientPayload_StreamRegRequest)
{ {
handleStreamRegMessage(msg, ctx, emulContext, X); hmmmm_stream_StreamRegRequest_table_t req =
(hmmmm_stream_StreamRegRequest_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
uint32_t dev_id = hmmmm_stream_StreamRegRequest_device_id(req);
uint32_t seg_id = hmmmm_stream_StreamRegRequest_seg_id(req);
uint32_t offset = hmmmm_stream_StreamRegRequest_offset(req);
uint32_t length = hmmmm_stream_StreamRegRequest_length(req);
hmmmm_stream_StreamMode_enum_t mode = hmmmm_stream_StreamRegRequest_mode(req);
printf("[STREAM/REG] device=%u seg=%u offset=%u len=%u mode=%d\n",
dev_id, seg_id, offset, length, (int)mode);
if (dev_id >= (uint32_t)emulContext->devicesCount) {
printf("[STREAM/REG] invalid device %u\n", dev_id);
return;
}
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[dev_id];
if (deviceRegs->regCount >= deviceRegs->allocatedSize) {
size_t new_size = deviceRegs->allocatedSize * 2;
StreamReg* new_regs = realloc(deviceRegs->regs, sizeof(StreamReg) * new_size);
NULL_GUARD(new_regs);
deviceRegs->regs = new_regs;
deviceRegs->allocatedSize = new_size;
}
uint32_t reg_id = ctx->streamRegIterator++;
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
reg->clientContext = ctx;
reg->regId = reg_id;
reg->startAddr = (uint64_t)offset;
reg->segLen = (uint64_t)length;
reg->mode = (uint8_t)mode;
printf("[STREAM/REG] assigned reg_id=%u to client=%lu\n", reg_id, ctx->clientId);
size_t msg_len;
uint8_t* out = fb_build_stream_reg_confirm(
nonce, reg_id, dev_id, seg_id, offset, length, (uint8_t)mode, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
} }
else if(streamType == STREAM_TYPE_REG_DISCARD) else if (ptype == hmmmm_stream_StreamClientPayload_StreamDeregRequest)
{ {
uint32_t regId = decodeBytesToU32(msg->payload); hmmmm_stream_StreamDeregRequest_table_t req =
unregisterClientStream(emulContext, ctx, regId); (hmmmm_stream_StreamDeregRequest_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
uint32_t stream_id = hmmmm_stream_StreamDeregRequest_stream_id(req);
printf("[STREAM/DEREG] stream_id=%u\n", stream_id);
unregisterClientStream(emulContext, ctx, stream_id);
}
else if (ptype == hmmmm_stream_StreamClientPayload_StreamWritePush)
{
hmmmm_stream_StreamWritePush_table_t push =
(hmmmm_stream_StreamWritePush_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
printf("[STREAM/WRITE] stream_id=%u offset=%u len=%zu\n",
hmmmm_stream_StreamWritePush_stream_id(push),
hmmmm_stream_StreamWritePush_offset(push),
flatbuffers_uint8_vec_len(
hmmmm_stream_StreamWritePush_data(push)));
// TODO: apply write to device memory
} }
} }

View File

@@ -1,12 +1,17 @@
#include "proto/handlers/ws.h" #include "proto/handlers/ws.h"
#include "proto/handlers.h" #include "proto/handlers.h"
#include <stdio.h>
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "my_mutex.h" #include "my_mutex.h"
#include "proto/enums.h"
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include "proto/msg.h" #include "proto/msg.h"
#include "proto_verifier.h"
#include "proto_reader.h"
void onWsOpen(ws_cli_conn_t client) void onWsOpen(ws_cli_conn_t client)
{ {
char errbuf[1024]; char errbuf[1024];
@@ -15,34 +20,30 @@ void onWsOpen(ws_cli_conn_t client)
NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf); NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf);
ptQueue* outcomeQueue = ptQueueCreate(errbuf); ptQueue* outcomeQueue = ptQueueCreate(errbuf);
NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf); NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf);
ClientContext* cctx = malloc(sizeof(ClientContext)); ClientContext* cctx = malloc(sizeof(ClientContext));
if(cctx == NULL) if (cctx == NULL) {
{
ptQueueFree(incomeQueue); ptQueueFree(incomeQueue);
ptQueueFree(outcomeQueue); ptQueueFree(outcomeQueue);
panic("Unable to allocate client context\n"); panic("Unable to allocate client context\n");
} }
cctx->clientId = client; cctx->clientId = client;
cctx->isAuthed = 0; cctx->isAuthed = 0;
cctx->streamRegIterator = 0; cctx->streamRegIterator = 0;
cctx->incomeQ = incomeQueue; cctx->incomeQ = incomeQueue;
cctx->outcomeQ = outcomeQueue; cctx->outcomeQ = outcomeQueue;
cctx->connectedAt = (uint64_t)time(NULL); cctx->connectedAt = (uint64_t)time(NULL);
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
if(ev == NULL) if (ev == NULL) {
{
ptQueueFree(incomeQueue); ptQueueFree(incomeQueue);
ptQueueFree(outcomeQueue); ptQueueFree(outcomeQueue);
free(cctx); free(cctx);
panic("Unable to allocate register event"); panic("Unable to allocate register event");
} }
ev->regType = REG_EVTYPE_CONNECT; ev->regType = REG_EVTYPE_CONNECT;
ev->ctx = cctx; ev->ctx = cctx;
ws_set_connection_context(client, cctx); ws_set_connection_context(client, cctx);
@@ -51,90 +52,87 @@ void onWsOpen(ws_cli_conn_t client)
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
if(exitCode) if (exitCode) {
{
ptQueueFree(incomeQueue); ptQueueFree(incomeQueue);
ptQueueFree(outcomeQueue); ptQueueFree(outcomeQueue);
free(cctx); free(cctx);
panic("Unable to push to reg queue: %s\n", errbuf); panic("Unable to push to reg queue: %s\n", errbuf);
} }
} }
char *cli; char* cli = ws_getaddress(client);
cli = ws_getaddress(client);
printf("Connection %lu opened, addr: %s\n", client, cli); printf("Connection %lu opened, addr: %s\n", client, cli);
} }
void onWsClose(ws_cli_conn_t client) void onWsClose(ws_cli_conn_t client)
{ {
pthread_t tid = pthread_self(); char* cli = ws_getaddress(client);
char *cli; printf("Connection %lu closed, addr: %s\n", client, cli);
cli = ws_getaddress(client);
printf("Connection %lu closed, addr: %s, thread id: %lu\n", client, cli, tid);
char errbuf[1024]; char errbuf[1024];
ClientContext* cctx = ws_get_connection_context(client); ClientContext* cctx = ws_get_connection_context(client);
if(cctx == NULL) if (cctx == NULL) {
{ printf("Unable to get client context for %lu\n", client);
cctx = NULL;
printf("Unable to get client context\n");
} }
ServerContext* ctx = ws_get_server_context(client); ServerContext* ctx = ws_get_server_context(client);
if(ctx == NULL) if (ctx == NULL) {
{ printf("Unable to get server context for %lu\n", client);
printf("Unable to get server context\n");
return; return;
} }
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
NULL_GUARD(ev); NULL_GUARD(ev);
ev->regType = REG_EVTYPE_CLOSE; ev->regType = REG_EVTYPE_CLOSE;
ev->ctx = cctx; ev->ctx = cctx;
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
if(exitCode) if (exitCode) {
{
panic("Unable to push to reg queue: %s\n", errbuf); panic("Unable to push to reg queue: %s\n", errbuf);
} }
} }
} }
void onWsMessage(
ws_cli_conn_t client, const unsigned char* msg, uint64_t size, int type)
void onWsMessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type)
{ {
ClientContext* cctx = ws_get_connection_context(client); ClientContext* cctx = ws_get_connection_context(client);
if(cctx == NULL) if (cctx == NULL) {
{
panic("Unable to get client context\n"); panic("Unable to get client context\n");
} }
if (cctx->isAuthed == 0) if (!cctx->isAuthed) {
{
handle_auth(cctx, client, msg, size, type); handle_auth(cctx, client, msg, size, type);
return;
} }
else
{ // Verify the FlatBuffer before queuing
char errbuf[1024]; if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)size)) {
BaseMessage* baseMsg = parseMessage(msg, size); printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
printf("msg\n"); return;
int isErr = ptQueuePush(cctx->incomeQ, baseMsg, errbuf); }
if(isErr)
{ // Copy bytes — the WS buffer is only valid for this callback's duration
panic("Unable to dispatch client message: %s\n", errbuf); FbMessage* fbmsg = malloc(sizeof(FbMessage));
} NULL_GUARD(fbmsg);
fbmsg->data = malloc((size_t)size);
if (fbmsg->data == NULL) {
free(fbmsg);
panic("Unable to allocate FbMessage buffer\n");
}
fbmsg->size = (size_t)size;
memcpy(fbmsg->data, msg, (size_t)size);
char errbuf[1024];
int isErr = ptQueuePush(cctx->incomeQ, fbmsg, errbuf);
if (isErr) {
free(fbmsg->data);
free(fbmsg);
panic("Unable to queue client message: %s\n", errbuf);
} }
} }

View File

@@ -1,85 +1,250 @@
#include "proto/msg.h" #include "proto/msg.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h" #include "flatcc/flatcc_builder.h"
#include "proto/pack.h" #include "proto_builder.h"
#include "control_builder.h"
#include "exec_notif_builder.h"
#include "auth_builder.h"
#include "stream_builder.h"
#include "stream_data_builder.h"
#include <string.h>
BaseMessage* parseMessage(const uint8_t* bytes, size_t size) uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
{ {
const uint8_t headerSize = 9; flatcc_builder_t B;
BaseMessage* msg = malloc(sizeof(BaseMessage)); if (flatcc_builder_init(&B)) {
NULL_GUARD(msg); panic("flatcc_builder_init failed\n");
uint64_t nonce = decodeBytesToU64(bytes); }
hmmmm_auth_AuthResponse_ref_t ar =
hmmmm_auth_AuthResponse_create(&B, seat_id);
msg->nonce = nonce; hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_AuthResponse(ar);
msg->packetType = bytes[8] >> 4; hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
msg->payloadHeader = bytes[8] & 0b1111;
msg->payloadLen = size - headerSize; uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
uint8_t* payload = malloc(sizeof(uint8_t) * (msg->payloadLen)); NULL_GUARD(buf);
NULL_GUARD(payload); return buf;
memcpy(payload, bytes + headerSize, msg->payloadLen);
msg->payload = payload;
return msg;
} }
uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut) uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out)
{ {
*lenOut = 9 + 10; flatcc_builder_t B;
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); if (flatcc_builder_init(&B)) {
NULL_GUARD(outmsg, "Unable to allocate message"); panic("flatcc_builder_init failed\n");
}
encodeUintToBytes(nonce, outmsg); hmmmm_ctrl_exec_notif_ExecNotifyMessage_ref_t notif =
hmmmm_ctrl_exec_notif_ExecNotifyMessage_create(
&B, tclk, (hmmmm_ctrl_exec_notif_ExecState_enum_t)(int8_t)state);
outmsg[8] = PACKET_TYPE_CTRL << 4; hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
outmsg[8] |= CTRL_TYPE_NOTIF_STATE; hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
outmsg[9] = NOTIF_TYPE_EXEC; hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
encodeUintToBytes(clockCounter, outmsg + 10); hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
outmsg[18] = newEmulState;
return outmsg; hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
} }
uint8_t* createDoneRegMessage(uint64_t nonce, uint8_t X, uint64_t devId, uint64_t segId, uint64_t startAddr, uint64_t segLength, uint32_t regId, size_t* lenOut) uint8_t* fb_build_stream_data_push(
flatcc_builder_t *B,
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
const uint8_t* data, size_t data_len,
size_t* len_out)
{ {
*lenOut = 36 + 9; flatcc_builder_reset(B);
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg);
encodeUintToBytes(nonce, outmsg); flatbuffers_uint8_vec_ref_t data_vec =
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (X << 3) | STREAM_TYPE_REG_CONFIRM); flatbuffers_uint8_vec_create(B, data, data_len);
encodeUintToBytes(devId, outmsg + 9);
encodeUintToBytes(segId, outmsg + 9 + 8); hmmmm_stream_StreamDataPush_ref_t push =
encodeUintToBytes(startAddr, outmsg + 9 + 8 + 8); hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
encodeUintToBytes(segLength, outmsg + 9 + 8 + 8 + 8);
encodeUintToBytes(regId, outmsg + 9 + 8 + 8 + 8 + 8); hmmmm_stream_StreamServerPayload_union_ref_t stream_payload =
return outmsg; hmmmm_stream_StreamServerPayload_as_StreamDataPush(push);
hmmmm_stream_StreamServerMessage_ref_t stream_msg =
hmmmm_stream_StreamServerMessage_create(B, stream_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_StreamServerMessage(stream_msg);
hmmmm_ServerMessage_create_as_root(B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(B, len_out);
NULL_GUARD(buf);
return buf;
} }
uint8_t* createStreamSegmentPush(uint8_t mode, uint32_t regId, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut) uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size,
uint64_t client_lifetime_ticks, size_t* len_out)
{ {
*lenOut = 9 + 4 + 8 + payloadLen; flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb =
NULL_GUARD(outmsg); hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks);
uint64_t nonce = (uint64_t)~0; hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
encodeUintToBytes(nonce, outmsg); hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb);
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (mode << 3) | STREAM_TYPE_SEND);
encodeUintToBytes(regId, outmsg + 9); hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
encodeUintToBytes(clockCounter, outmsg + 9 + 4); hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
memcpy(outmsg + 9 + 4 + 8, payload, payloadLen);
return outmsg; hmmmm_ServerPayload_union_ref_t setup_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, setup_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Use start/end directly: passing 0 to _create causes entries_add to return -1
hmmmm_ctrl_orphaned_OrphanedResponse_start(&B);
hmmmm_ctrl_orphaned_OrphanedResponse_ref_t resp =
hmmmm_ctrl_orphaned_OrphanedResponse_end(&B);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_OrphanedResponse(resp);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t orphan_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, orphan_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Use start/end directly: passing 0 for messages vec causes messages_add to return -1
hmmmm_ctrl_lost_LostMessagesResponse_start(&B);
hmmmm_ctrl_lost_LostMessagesResponse_seat_id_add(&B, seat_id);
hmmmm_ctrl_lost_LostMessagesResponse_ref_t resp =
hmmmm_ctrl_lost_LostMessagesResponse_end(&B);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_LostMessagesResponse(resp);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t lost_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, lost_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_mem_read_response(
uint64_t nonce, uint64_t tclk,
uint32_t device_id, uint32_t seg_id, uint32_t offset,
const uint8_t* data, size_t data_len,
size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
flatbuffers_uint8_vec_ref_t data_vec =
flatbuffers_uint8_vec_create(&B, data, data_len);
hmmmm_mem_MemReadResponse_ref_t resp =
hmmmm_mem_MemReadResponse_create(&B, tclk, device_id, seg_id, offset, data_vec);
hmmmm_mem_MemServerPayload_union_ref_t mem_payload =
hmmmm_mem_MemServerPayload_as_MemReadResponse(resp);
hmmmm_mem_MemServerMessage_ref_t mem_msg =
hmmmm_mem_MemServerMessage_create(&B, mem_payload);
hmmmm_ServerPayload_union_ref_t mem_outer =
hmmmm_ServerPayload_as_MemServerMessage(mem_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, mem_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_stream_reg_confirm(
uint64_t nonce, uint32_t stream_id,
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
uint8_t mode, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_stream_StreamRegConfirm_ref_t confirm =
hmmmm_stream_StreamRegConfirm_create(
&B, stream_id, device_id, seg_id, offset, length,
(hmmmm_stream_StreamMode_enum_t)(int8_t)mode);
hmmmm_stream_StreamServerPayload_union_ref_t sreg_payload =
hmmmm_stream_StreamServerPayload_as_StreamRegConfirm(confirm);
hmmmm_stream_StreamServerMessage_ref_t sreg_msg =
hmmmm_stream_StreamServerMessage_create(&B, sreg_payload);
hmmmm_ServerPayload_union_ref_t sreg_outer =
hmmmm_ServerPayload_as_StreamServerMessage(sreg_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, sreg_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
} }