Compare commits
12 Commits
master
...
flatbuffer
| Author | SHA1 | Date | |
|---|---|---|---|
| 26e594cee6 | |||
| 7a272788f8 | |||
| 14b005dbb3 | |||
| c907d45763 | |||
| b5a4fdf755 | |||
| e053d445da | |||
| d8804c9ae2 | |||
| 71ec472510 | |||
| eb7f4b7eb0 | |||
| 589bc8d620 | |||
| d83cb7fe7b | |||
| 9a4b78f272 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build
|
||||
devices
|
||||
.cache
|
||||
*.bin
|
||||
inc/flatbuf_autogen
|
||||
|
||||
89
Makefile
89
Makefile
@@ -4,13 +4,28 @@ SRC_DIR=src
|
||||
INC_DIR=inc
|
||||
CC=gcc
|
||||
OBJDUMP=objdump
|
||||
LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a
|
||||
LIBS=deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
|
||||
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
|
||||
STANDART=c23
|
||||
OPTIMIZE=-O3
|
||||
# OPTIMIZE=-Og #3 -march=native
|
||||
OPTIMIZE=-O3 -march=native
|
||||
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
|
||||
|
||||
@@ -20,32 +35,43 @@ C_INCLUDES=-I$(INC_DIR)/ $(addprefix -I,$(LIBS_HEADERS))
|
||||
|
||||
C_DEFS=-D_POSIX_C_SOURCE=199309L
|
||||
|
||||
DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop
|
||||
DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop -Wno-unused-function -Wno-unused-but-set-variable
|
||||
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
|
||||
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 -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
|
||||
CFLAGS=$(C_DEFS) -g -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
|
||||
STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS))
|
||||
LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm
|
||||
# LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto
|
||||
# LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
|
||||
LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
|
||||
|
||||
|
||||
|
||||
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
|
||||
|
||||
DEP = $(filter %.d, $(OBJECTS:.o=.d))
|
||||
$(info $(DEP))
|
||||
#$(info $(DEP))
|
||||
|
||||
OBJECTS += $(LIBS)
|
||||
vpath %.c $(sort $(dir $(C_SOURCES)))
|
||||
|
||||
# asan: CFLAGS := $(filter-out -fanalyzer,$(CFLAGS)) -fsanitize=address,undefined
|
||||
# asan: LFLAGS := $(filter-out -flto -fuse-linker-plugin,$(LFLAGS)) -fsanitize=address,undefined
|
||||
# asan: build
|
||||
|
||||
# tsan: CFLAGS := $(filter-out -fanalyzer,$(CFLAGS)) -fsanitize=thread
|
||||
# tsan: LFLAGS := $(filter-out -flto -fuse-linker-plugin,$(LFLAGS)) -fsanitize=thread
|
||||
# tsan: build
|
||||
|
||||
|
||||
all: build
|
||||
|
||||
build: date deps Dir target compile_commands
|
||||
build: date deps Dir proto python-proto target
|
||||
|
||||
rebuild: clean | build
|
||||
|
||||
$(info $(DEP))
|
||||
#$(info $(DEP))
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
@@ -54,20 +80,30 @@ $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
|
||||
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
|
||||
@$(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)
|
||||
@echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m'
|
||||
@$(CC) $(LFLAGS) $(OBJECTS) -o $(BUILD_DIR)/$(TARGET).elf
|
||||
@$(CC) $(LFLAGS) $(OBJECTS) $(LDLIBS) -o $(BUILD_DIR)/$(TARGET).elf
|
||||
|
||||
deps:
|
||||
@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:
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(shell mkdir -p $(dir $(OBJECTS)))
|
||||
@$(shell mkdir -p $(dir $(OBJECTS)))
|
||||
|
||||
SrcDir:
|
||||
@mkdir -p $(SRC_DIR)
|
||||
@@ -75,21 +111,36 @@ SrcDir:
|
||||
IncDir:
|
||||
@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 asan tsan clean
|
||||
|
||||
clean:
|
||||
@rm -rf $(BUILD_DIR)/*
|
||||
# @rm -f compile_commands.json
|
||||
@rm -rf $(BUILD_DIR)/* $(BUILD_DIR)/.* $(PROTO_INC_DIR)/* $(PROTO_SRC_DIR)/*
|
||||
@echo -e '\033[0;31mCleaned\033[0m'
|
||||
|
||||
.NOTPARALLEL: date target rebuild deps
|
||||
.NOTPARALLEL: date target rebuild deps proto
|
||||
date:
|
||||
@echo -e '\033[1;32m'"Starting build at " | tr -d '\n'
|
||||
@date
|
||||
@echo -e '\033[0m'
|
||||
|
||||
|
||||
compile_commands:
|
||||
# @bear -- ./.gen_compile_commands.sh $(TARGET) $(CC) "$(CFLAGS)" "$(LFLAGS)" "$(OBJECTS)"
|
||||
|
||||
250
README.md
Normal file
250
README.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# hmmmm
|
||||
|
||||
**Модульный эмулятор микроконтроллеров с сетевым интерфейсом**
|
||||
|
||||
hmmmm — это эмулятор микроконтроллеров, который позволяет эмулировать AVR и другие архитектуры с возможностью управления через WebSocket. Эмулятор поддерживает динамическую загрузку устройств, композицию сложных устройств и потоковую передачу изменений памяти в реальном времени.
|
||||
|
||||
## Возможности
|
||||
|
||||
- **Эмуляция AVR-микроконтроллеров** — поддержка ~99 инструкций AVR
|
||||
- **Модульная архитектура** — устройства загружаются как динамические библиотеки (.so)
|
||||
- **Композиция устройств** — создание сложных устройств через TOML-конфигурацию
|
||||
- **WebSocket интерфейс** — управление эмуляцией и получение данных в реальном времени
|
||||
- **Потоковая передача** — подписка на изменения памяти (чтение/запись)
|
||||
- **Аутентификация** — SHA-512 токен с временным окном (TOTP-подобный)
|
||||
- **Сегментированная память** — PS (программная), GP_REGS, IO_REGS, DS (данные)
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
hmmmm/
|
||||
├── src/ # Исходный код эмулятора
|
||||
│ ├── main.c # WebSocket сервер, главный цикл
|
||||
│ ├── hmmmm.c # Загрузка динамических библиотек
|
||||
│ ├── config.c # Парсинг TOML-конфигураций
|
||||
│ └── proto/ # Обработчики WebSocket-протокола
|
||||
├── inc/ # Заголовочные файлы
|
||||
├── devices/ # Реализации эмулируемых устройств
|
||||
│ ├── avr_generic/ # Универсальный эмулятор AVR
|
||||
│ └── avr_gpio/ # GPIO устройство
|
||||
├── flatbuffers/ # FlatBuffers схемы протокола
|
||||
├── deps/ # Внешние зависимости
|
||||
├── hmmmm_scripts/ # Python скрипты и утилиты
|
||||
└── Makefile # Основной файл сборки
|
||||
```
|
||||
|
||||
## Требования
|
||||
|
||||
- **GCC** с поддержкой C23
|
||||
- **OpenSSL** (для SHA-512)
|
||||
- **Git** (для клонирования зависимостей)
|
||||
- **Python 3.12+** (опционально, для скриптов)
|
||||
|
||||
## Сборка
|
||||
|
||||
### 1. Инициализация зависимостей
|
||||
|
||||
```bash
|
||||
# Клонирование и сборка внешних зависимостей
|
||||
make deps
|
||||
```
|
||||
|
||||
Это клонирует следующие зависимости в `deps/`:
|
||||
- `tomlc99` — TOML парсер
|
||||
- `ptQueue` — lock-free очередь
|
||||
- `wsServer` — WebSocket сервер
|
||||
- `flatcc` — FlatBuffers runtime для C
|
||||
- `flatbuffers` — Google FlatBuffers (flatc компилятор)
|
||||
|
||||
### 2. Генерация FlatBuffers кода
|
||||
|
||||
```bash
|
||||
# Генерация C заголовков из .fbs схем
|
||||
make proto
|
||||
|
||||
# Генерация Python bindings (опционально)
|
||||
make python-proto
|
||||
```
|
||||
|
||||
### 3. Сборка эмулятора
|
||||
|
||||
```bash
|
||||
# Основная сборка
|
||||
make build
|
||||
|
||||
# Полная пересборка
|
||||
make rebuild
|
||||
|
||||
# Очистка
|
||||
make clean
|
||||
```
|
||||
|
||||
После успешной сборки исполняемый файл будет находиться в `out/main.elf`.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### 1. Подготовка конфигурации
|
||||
|
||||
Создайте TOML-файл с конфигурацией эмулятора:
|
||||
|
||||
```toml
|
||||
# example.toml
|
||||
[dev]
|
||||
avr = "devices/avr_generic/AVRrc.toml"
|
||||
|
||||
[clock]
|
||||
limiter = 16000000 # 16 MHz
|
||||
|
||||
[mem]
|
||||
# Настройка памяти
|
||||
```
|
||||
|
||||
### 2. Запуск эмулятора
|
||||
|
||||
```bash
|
||||
./out/main.elf example.toml
|
||||
```
|
||||
|
||||
Эмулятор запустит WebSocket сервер на порту **8181**.
|
||||
|
||||
### 3. Подключение клиента
|
||||
|
||||
Используйте Python-клиент для подключения:
|
||||
|
||||
```bash
|
||||
cd ../hmmmm_client_py
|
||||
poetry install
|
||||
poetry run python -m model.client
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### TOML-конфигурация устройства
|
||||
|
||||
Пример конфигурации AVR устройства:
|
||||
|
||||
```toml
|
||||
[device]
|
||||
type = "avr_generic"
|
||||
libpath = "devices/avr_generic/AVRrc_build/device.so"
|
||||
firmware = "test.bin"
|
||||
|
||||
[memory]
|
||||
ps_size = 65536 # Program space
|
||||
ds_size = 4096 # Data space
|
||||
io_regs = 256 # I/O registers
|
||||
gp_regs = 32 # General purpose registers
|
||||
```
|
||||
|
||||
### Композиция устройств
|
||||
|
||||
Сложные устройства создаются через композицию:
|
||||
|
||||
```toml
|
||||
[dev]
|
||||
core = "avr_generic.toml"
|
||||
gpio_a = "avr_gpio.toml"
|
||||
|
||||
[clock]
|
||||
limiter = 16000000
|
||||
|
||||
[mem.intercept.PORTA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
addr = 0
|
||||
seg = "reg_io"
|
||||
```
|
||||
|
||||
## Протокол
|
||||
|
||||
Эмулятор использует **FlatBuffers over WebSocket** для обмена сообщениями.
|
||||
|
||||
### Типы сообщений
|
||||
|
||||
#### Клиент → Сервер:
|
||||
- **AuthRequest** — аутентификация (SHA-512 токен + timestamp)
|
||||
- **ExecCtrlMessage** — управление эмуляцией (старт/стоп/сброс)
|
||||
- **MemReadRequest/MemWriteRequest** — операции с памятью
|
||||
- **StreamRegRequest** — подписка на изменения памяти
|
||||
|
||||
#### Сервер → Клиент:
|
||||
- **AuthResponse** — подтверждение аутентификации (seat_id)
|
||||
- **ExecNotifyMessage** — уведомления о состоянии эмуляции
|
||||
- **MemReadResponse** — данные из памяти
|
||||
- **StreamDataPush** — потоковые изменения памяти
|
||||
|
||||
### Аутентификация
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
def generate_auth(access_token: str) -> bytes:
|
||||
now = int(time.time())
|
||||
prepared = f'{access_token}{now // 30}'
|
||||
return hashlib.sha512(prepared.encode()).digest()
|
||||
```
|
||||
|
||||
Токен действителен в течение 30 секунд.
|
||||
|
||||
## API устройств
|
||||
|
||||
Каждое устройство должно экспортировать интерфейс `device_lib_t`:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t devType;
|
||||
device_public_context_t* (*init)(void*, char* errbuf);
|
||||
uint8_t (*makeDeviceTick)(device_public_context_t* devInfo);
|
||||
void* extendedHandlers;
|
||||
void* (*parseSpecsFromConfig)(const conf_dev_t* devConf, char* errbuf);
|
||||
void (*freeSpecs)(void* specs);
|
||||
void (*freeDevMem)(device_mem_t* mem);
|
||||
void (*fillSmartReadSpecs)(void* specs, smart_read_spec_t*, uint64_t);
|
||||
void (*fillSmartWriteSpecs)(void* specs, smart_write_spec_t*, uint64_t);
|
||||
} device_lib_t;
|
||||
```
|
||||
|
||||
## Отладка
|
||||
|
||||
### Генерация compile_commands.json
|
||||
|
||||
Для интеграции с clangd:
|
||||
|
||||
```bash
|
||||
./.gen_compile_commands.sh
|
||||
```
|
||||
|
||||
### Логирование
|
||||
|
||||
Эмулятор выводит отладочную информацию в stdout. Для подробного логирования можно модифицировать `src/main.c`.
|
||||
|
||||
### GDB
|
||||
|
||||
```bash
|
||||
gdb ./out/main.elf
|
||||
(gdb) run example.toml
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
- **Тактовая частота** — до 16 MHz (эмулируемая)
|
||||
- **WebSocket** — асинхронная отправка сообщений
|
||||
- **Lock-free очереди** — эффективный межпоточный обмен
|
||||
- **FlatBuffers** — сериализация с нулевым копированием
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Абсолютные пути в конфигурационных файлах (требует замены на относительные)
|
||||
- Хардкод токена аутентификации (требует выноса в ENV)
|
||||
- Отсутствие graceful shutdown (обработка SIGINT/SIGTERM)
|
||||
- Нет unit-тестов для основного кода
|
||||
|
||||
## Лицензия
|
||||
|
||||
Проект разработан @nikto_b. Все права защищены.
|
||||
|
||||
## Контакты
|
||||
|
||||
- Документация: https://about.hmmmm.nikto-b.ru/
|
||||
- Автор: @nikto_b
|
||||
2
deps/.gitignore
vendored
2
deps/.gitignore
vendored
@@ -2,3 +2,5 @@ tomlc99
|
||||
ptQueue
|
||||
tomlc99
|
||||
wsServer
|
||||
flatcc
|
||||
flatbuffers
|
||||
|
||||
40
deps/Makefile
vendored
40
deps/Makefile
vendored
@@ -1,26 +1,52 @@
|
||||
all: tomlc99.a ptQueue.a libws.a
|
||||
all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc/bin/flatcc flatbuffers/build/flatc
|
||||
|
||||
tomlc99:
|
||||
@git clone https://github.com/cktan/tomlc99
|
||||
|
||||
tomlc99.a: tomlc99
|
||||
tomlc99/libtoml.a: tomlc99
|
||||
@make -C tomlc99 libtoml.a
|
||||
|
||||
|
||||
ptQueue:
|
||||
@git clone git@192.168.8.167:nikto_b/ptQueue.git
|
||||
flatcc:
|
||||
@git clone https://github.com/dvidelabs/flatcc
|
||||
|
||||
ptQueue.a: ptQueue
|
||||
flatcc_src: flatcc/bin/flatcc
|
||||
flatcc/lib/libflatccrt.a: flatcc/bin/flatcc
|
||||
|
||||
flatcc/bin/flatcc: flatcc
|
||||
@flatcc/scripts/initbuild.sh make
|
||||
@flatcc/scripts/build.sh
|
||||
|
||||
|
||||
ptQueue:
|
||||
@git clone https://git.nikto-b.ru/nikto_b/ptQueue
|
||||
|
||||
ptQueue/out/ptQueue.a: ptQueue
|
||||
@make -C ptQueue
|
||||
|
||||
wsServer:
|
||||
@git clone https://github.com/Theldus/wsServer
|
||||
|
||||
libws.a: wsServer
|
||||
wsServer/libws.a: 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:
|
||||
@rm -rf wsServer ptQueue tomlc99
|
||||
@rm -rf wsServer ptQueue tomlc99 flatcc flatbuffers
|
||||
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
1
deps/flatbuffers
vendored
Submodule
1
deps/flatbuffers
vendored
Submodule
Submodule deps/flatbuffers added at e223d69b36
72
devices/avr_generic/inc/instr.h
Normal file
72
devices/avr_generic/inc/instr.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __INSTR_H__
|
||||
#define __INSTR_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "mem.h"
|
||||
|
||||
|
||||
#ifndef OPCODE_WORDSIZE
|
||||
#error OPCODE_WORDSIZE must be provided
|
||||
// #define OPCODE_WORDSIZE 1
|
||||
#endif
|
||||
#if OPCODE_WORDSIZE == 1
|
||||
typedef uint8_t opcode_t;
|
||||
#elif OPCODE_WORDSIZE == 2
|
||||
typedef uint16_t opcode_t;
|
||||
#elif OPCODE_WORDSIZE == 3
|
||||
typedef uint32_t opcode_t;
|
||||
#elif OPCODE_WORDSIZE == 4
|
||||
typedef uint32_t opcode_t;
|
||||
#else
|
||||
#error OPCODE_WORDSIZE must be one of 1,2,3,4
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef PC_WORDSIZE
|
||||
#error PC_WORDSIZE must be provided
|
||||
// #define PC_WORDSIZE 1
|
||||
#endif
|
||||
#if PC_WORDSIZE == 1
|
||||
typedef uint8_t prog_counter_t;
|
||||
#elif PC_WORDSIZE == 2
|
||||
typedef uint16_t prog_counter_t;
|
||||
#elif PC_WORDSIZE == 3
|
||||
typedef uint32_t prog_counter_t;
|
||||
#elif PC_WORDSIZE == 4
|
||||
typedef uint32_t prog_counter_t;
|
||||
#else
|
||||
#error PC_WORDSIZE must be one of 1,2,3,4
|
||||
#endif
|
||||
|
||||
typedef uint8_t (*instr_h_func)(prog_counter_t*, device_mem_t*);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
instr_h_func h;
|
||||
opcode_t base;
|
||||
opcode_t argsMask;
|
||||
uint8_t opcodeLen;
|
||||
|
||||
} instruction_metadata_t;
|
||||
|
||||
instr_h_func* genInstrArray(char* errbuf);
|
||||
void setOpcodeSizes(uint8_t* opcodeSizes);
|
||||
opcode_t extractOpcode(device_mem_t* devMem, prog_counter_t programCounter);
|
||||
|
||||
|
||||
|
||||
|
||||
#define AVRe 1
|
||||
#define AVRxm 2
|
||||
#define AVRxt 3
|
||||
#define AVRrc 4
|
||||
|
||||
#ifndef ARCH
|
||||
#define ARCH AVRe
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // ifndef __INSTR_H__
|
||||
833
devices/avr_generic/src/device.c
Normal file
833
devices/avr_generic/src/device.c
Normal file
@@ -0,0 +1,833 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "libhmmmm/mem.h"
|
||||
#include "mem.h"
|
||||
#include "mem_seg.h"
|
||||
#include "runner.h"
|
||||
#include "instr.h"
|
||||
#include "addrs.h"
|
||||
|
||||
|
||||
uint64_t memSegToGlobal(device_specs_t* spec, uint8_t seg, uint64_t localaddr)
|
||||
{
|
||||
// uint64_t offset = 0;
|
||||
// for (uint8_t i = 0; i < seg; i++)
|
||||
// {
|
||||
// offset += spec->memSpecs[i]->len * spec->memSpecs[i]->wordLen;
|
||||
// }
|
||||
return spec->memSpecs[seg]->start + localaddr;
|
||||
}
|
||||
|
||||
|
||||
void freeDevMem(device_mem_t* devMem)
|
||||
{
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem->memsegSizes);
|
||||
free(devMem->memsegCellSizes);
|
||||
free(devMem->rawCells);
|
||||
free(devMem->memreadCellAddrs);
|
||||
free(devMem->memwriteCellAddrs);
|
||||
free(devMem->memwriteCellSegments);
|
||||
free(devMem->memwriteValues);
|
||||
free(devMem->cells);
|
||||
free(devMem->smartAddrReadHandlers);
|
||||
free(devMem->smartAddrWriteHandlers);
|
||||
free(devMem);
|
||||
}
|
||||
|
||||
void freeDevSpec(void* _specs)
|
||||
{
|
||||
device_specs_t* specs = _specs;
|
||||
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
|
||||
{
|
||||
free(specs->memSpecs[i]);
|
||||
}
|
||||
free(specs->memSpecs);
|
||||
free(specs->executableSegments);
|
||||
free(specs);
|
||||
}
|
||||
|
||||
|
||||
device_mem_t* genDevMem(device_specs_t* devSpec, char* errbuf)
|
||||
{
|
||||
device_mem_t* devMem = (device_mem_t*)calloc(1, sizeof(device_mem_t));
|
||||
if (devMem == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate dev memory struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t memTotalSize = 0;
|
||||
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
|
||||
{
|
||||
size_t tmp = devSpec->memSpecs[i]->start + (devSpec->memSpecs[i]->len * devSpec->memSpecs[i]->wordLen);
|
||||
if (memTotalSize < tmp)
|
||||
{
|
||||
memTotalSize = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void* rawCells = (void*)calloc(memTotalSize, sizeof(void*));
|
||||
if (rawCells == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate raw memory buf %lu bytes", memTotalSize);
|
||||
free(devMem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < memTotalSize; i++)
|
||||
{
|
||||
((uint8_t*)rawCells)[i] = 0;
|
||||
}
|
||||
|
||||
devMem->memsegShifts = calloc(devSpec->memSpecsCount, sizeof(uint64_t));
|
||||
|
||||
if(devMem->memsegShifts == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate segment shift buffers");
|
||||
free(rawCells);
|
||||
free(devMem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void** cells = calloc(devSpec->memSpecsCount, sizeof(void*));
|
||||
|
||||
if (cells == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate segment pointers");
|
||||
free(rawCells);
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char** cellNames = calloc(devSpec->memSpecsCount, sizeof(char*));
|
||||
|
||||
if(cellNames == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate segment names map");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(cells);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
|
||||
{
|
||||
printf("init seg \"%s\": %lu:%lu\n", devSpec->memSpecs[i]->name, devSpec->memSpecs[i]->start, devSpec->memSpecs[i]->len);
|
||||
cells[i] = &(((uint8_t*)rawCells)[devSpec->memSpecs[i]->start]);
|
||||
cellNames[i] = devSpec->memSpecs[i]->name;
|
||||
// cells[i] = (void*)((size_t)rawCells + (size_t)devSpec->memSpecs[i]->start);
|
||||
}
|
||||
|
||||
uint64_t* memreadCellAddrs = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
|
||||
if (memreadCellAddrs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate read interception addrs");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
return NULL;
|
||||
}
|
||||
uint64_t* memwriteCellAddrs = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
|
||||
|
||||
if (memwriteCellAddrs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate write interception addrs");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* memwriteCellSegments = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint8_t));
|
||||
if(memwriteCellSegments == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate write interception addrs");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
free(memwriteCellAddrs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t* memwriteValues = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
|
||||
if(memwriteValues == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate write interception addrs");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
free(memwriteCellAddrs);
|
||||
free(memwriteCellSegments);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
uint64_t smartAddrReadMask = 0;
|
||||
uint64_t smartAddrWriteMask = 0;
|
||||
mem_h_read_handler* smartAddrReadHandlers = calloc(memTotalSize, sizeof(mem_h_read_handler));
|
||||
|
||||
if(memwriteCellSegments == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate read interception handlers");
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
free(memwriteCellAddrs);
|
||||
free(memwriteCellSegments);
|
||||
free(memwriteValues);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem_h_write_handler* smartAddrWriteHandlers = calloc(memTotalSize, sizeof(mem_h_write_handler));
|
||||
|
||||
|
||||
if (smartAddrWriteHandlers == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate write interception handlers");
|
||||
free(smartAddrReadHandlers);
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(memwriteCellAddrs);
|
||||
free(memwriteCellSegments);
|
||||
free(memwriteValues);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t* memsegSizes = calloc(devSpec->memSpecsCount, sizeof(uint64_t));
|
||||
if(memsegSizes == NULL)
|
||||
{
|
||||
|
||||
sprintf(errbuf, "unable to allocate memseg sizes");
|
||||
free(smartAddrWriteHandlers);
|
||||
free(smartAddrReadHandlers);
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(memwriteCellAddrs);
|
||||
free(memwriteCellSegments);
|
||||
free(memwriteValues);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* memsegCellSizes = calloc(devSpec->memSpecsCount, sizeof(uint8_t));
|
||||
if(memsegCellSizes == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate memseg cell sizes");
|
||||
free(memsegSizes);
|
||||
free(smartAddrWriteHandlers);
|
||||
free(smartAddrReadHandlers);
|
||||
free(devMem->memsegShifts);
|
||||
free(devMem);
|
||||
free(rawCells);
|
||||
free(memreadCellAddrs);
|
||||
free(memwriteCellAddrs);
|
||||
free(memwriteCellSegments);
|
||||
free(memwriteValues);
|
||||
free(cells);
|
||||
free(cellNames);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// for(uint64_t i = 0; i < memTotalSize; i++)
|
||||
// {
|
||||
// smartAddrReadHandlers[i].func = NULL;
|
||||
// smartAddrReadHandlers[i].ident = 0;
|
||||
|
||||
// smartAddrWriteHandlers[i].func = NULL;
|
||||
// smartAddrWriteHandlers[i].ident = 0;
|
||||
// }
|
||||
|
||||
|
||||
// for(uint64_t i = 0; i < memTotalSize; i++)
|
||||
// {
|
||||
// if((i & smartAddrReadMask) == smartAddrReadMask)
|
||||
// {
|
||||
// smartAddrReadHandlers[i].func = NULL;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
if (devSpec->smartReadSpecsCount > 0)
|
||||
{
|
||||
for(uint64_t i = 0; i < devSpec->smartReadSpecsCount; i++)
|
||||
{
|
||||
smart_read_spec_t t = devSpec->smartReadSpecs[i];
|
||||
uint64_t addr;
|
||||
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
|
||||
{
|
||||
addr = t.addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
|
||||
}
|
||||
smartAddrReadHandlers[addr].func = *t.handler;
|
||||
smartAddrReadHandlers[addr].ident = t.ident;
|
||||
smartAddrReadMask |= addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (devSpec->smartWriteSpecsCount > 0)
|
||||
{
|
||||
for(uint64_t i = 0; i < devSpec->smartWriteSpecsCount; i++)
|
||||
{
|
||||
smart_write_spec_t t = devSpec->smartWriteSpecs[i];
|
||||
|
||||
uint64_t addr;
|
||||
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
|
||||
{
|
||||
addr = t.addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
|
||||
}
|
||||
smartAddrWriteHandlers[addr].func = *t.handler;
|
||||
smartAddrWriteHandlers[addr].ident = t.ident;
|
||||
smartAddrWriteMask |= t.addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
devMem->cells = cells;
|
||||
devMem->rawCells = rawCells;
|
||||
devMem->memreadCellAddrs = memreadCellAddrs;
|
||||
devMem->memwriteCellAddrs = memwriteCellAddrs;
|
||||
devMem->memwriteCellSegments = memwriteCellSegments;
|
||||
devMem->memwriteValues = memwriteValues;
|
||||
devMem->memreadLen = 0;
|
||||
devMem->memwriteLen = 0;
|
||||
devMem->smartAddrReadMask = smartAddrReadMask;
|
||||
devMem->smartAddrReadHandlers = smartAddrReadHandlers;
|
||||
devMem->smartAddrWriteMask = smartAddrWriteMask;
|
||||
devMem->smartAddrWriteHandlers = smartAddrWriteHandlers;
|
||||
devMem->memsegNames = cellNames;
|
||||
devMem->memsegSizes = memsegSizes;
|
||||
devMem->memsegCellSizes = memsegCellSizes;
|
||||
|
||||
memseg_metadata_t requiredSegments[] = MEMSEG_DEFINES;
|
||||
|
||||
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
|
||||
{
|
||||
|
||||
for(uint8_t j = 0; j < sizeof(requiredSegments) / sizeof(memseg_metadata_t); j++)
|
||||
{
|
||||
const memseg_metadata_t seg_def = requiredSegments[j];
|
||||
|
||||
if(strcmp(seg_def.name, devSpec->memSpecs[i]->name) == 0)
|
||||
{
|
||||
const uint8_t seg_id = seg_def.seg_id;
|
||||
devMem->memsegShifts[seg_id] = memSegToGlobal(devSpec, i, 0);
|
||||
devMem->memsegSizes[seg_id] = devSpec->memSpecs[i]->len;
|
||||
devMem->memsegCellSizes[seg_id] = devSpec->memSpecs[i]->wordLen;
|
||||
printf("set mem segment %d meta: +%lu/%lu[%d] \n", seg_id, devMem->memsegShifts[seg_id], devMem->memsegSizes[seg_id], devMem->memsegCellSizes[seg_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// for(uint8_t i = 0; i < devSpec->memSpecsCount; i++)
|
||||
// {
|
||||
// devMem->memsegShifts[i] = memSegToGlobal(devSpec, i, 0);
|
||||
// devMem->memsegSizes[i] = devSpec->memSpecs[i]->len;
|
||||
// }
|
||||
|
||||
setOpcodeSizes((uint8_t*)(devMem->cells[MEMDATA_OPSIZE]));
|
||||
|
||||
return devMem;
|
||||
}
|
||||
|
||||
|
||||
uint8_t makeDeviceTick(device_public_context_t* devContext)
|
||||
{
|
||||
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
|
||||
prog_counter_t _pc;
|
||||
READ_MEM(_pc, devInfo->deviceMem, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR, prog_counter_t)
|
||||
// printf("old PC is %d\n", _pc);
|
||||
if(_pc >= devInfo->deviceMem->memsegSizes[MEMSEG_PS])
|
||||
{
|
||||
_pc = 0;
|
||||
}
|
||||
uint8_t ticks = makeTick(&_pc, devInfo->instr, devInfo->deviceMem);
|
||||
WRITE_MEM(devInfo->deviceMem, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR, prog_counter_t, _pc);
|
||||
// printf("new PC is %d\n", _pc);
|
||||
return ticks;
|
||||
}
|
||||
|
||||
device_info_t* initSpecs(device_specs_t* specs, char* errbuf)
|
||||
{
|
||||
device_info_t* devInfo = calloc(1, sizeof(device_info_t));
|
||||
if (devInfo == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate dev info");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char genErrBuf[200];
|
||||
device_mem_t* devMem = genDevMem(specs, genErrBuf);
|
||||
|
||||
if (devMem == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to generate device memory: %s", genErrBuf);
|
||||
free(devInfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devInfo->pc = &((prog_counter_t*)(devMem->cells[MEMSEG_PC_SEG_NUM]))[MEMSEG_PC_ADDR];
|
||||
*(devInfo->pc) = 0;
|
||||
devInfo->specs = specs;
|
||||
|
||||
devInfo->deviceMem = devMem;
|
||||
|
||||
|
||||
instr_h_func* instrs = genInstrArray(genErrBuf);
|
||||
|
||||
if (instrs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to initialize instructions: %s", genErrBuf);
|
||||
freeDevMem(devInfo->deviceMem);
|
||||
free(devInfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devInfo->instr = instrs;
|
||||
|
||||
return devInfo;
|
||||
}
|
||||
|
||||
void error(const char* err, char* errbuf)
|
||||
{
|
||||
strcpy(errbuf, err);
|
||||
}
|
||||
|
||||
device_specs_t* parseSpecsFromConfig(const conf_dev_t* devConf, char* errbuf)
|
||||
{
|
||||
conf_mem_seg_t** segments = devConf->memConf->memSegConfs;
|
||||
device_specs_t* specs = calloc(1, sizeof(device_specs_t));
|
||||
if (specs == NULL)
|
||||
{
|
||||
error("unable to allocate mem specs struct", errbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
memseg_metadata_t requiredSegments[] = MEMSEG_DEFINES;
|
||||
uint8_t* requiredSegmentsFoundMap = (uint8_t*)calloc(sizeof(requiredSegments) / sizeof(memseg_metadata_t), sizeof(uint8_t));
|
||||
|
||||
if(requiredSegmentsFoundMap == NULL)
|
||||
{
|
||||
error("unable to allocate found map", errbuf);
|
||||
free(specs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
uint8_t specCount = 0;
|
||||
specs->executableSegmentsCount = 0;
|
||||
|
||||
while (segments[specCount] != NULL)
|
||||
{
|
||||
if(segments[specCount]->isExecutable)
|
||||
{
|
||||
specs->executableSegmentsCount++;
|
||||
}
|
||||
specCount++;
|
||||
}
|
||||
|
||||
const uint8_t providedSpecCount = specCount;
|
||||
|
||||
if(specCount < sizeof(requiredSegments)/sizeof(memseg_metadata_t))
|
||||
{
|
||||
specCount = sizeof(requiredSegments)/sizeof(memseg_metadata_t);
|
||||
}
|
||||
|
||||
specs->memSpecsCount = specCount;
|
||||
|
||||
specs->memSpecs = calloc(specCount, sizeof(memseg_spec_t*));
|
||||
if (specs->memSpecs == NULL)
|
||||
{
|
||||
free(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
error("unable to allocate mem segment specs", errbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
specs->executableSegments = calloc(specs->executableSegmentsCount, sizeof(uint8_t));
|
||||
if (specs->executableSegments == NULL)
|
||||
{
|
||||
error("unable to allocate mem executable specs", errbuf);
|
||||
free(specs->memSpecs);
|
||||
free(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
for (uint8_t i = 0; i < specCount; i++)
|
||||
{
|
||||
memseg_spec_t* spec = (memseg_spec_t*)calloc(1, sizeof(memseg_spec_t));
|
||||
if (spec == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate spec %d", i);
|
||||
|
||||
for (uint8_t j = 0; j < i; j++)
|
||||
{
|
||||
free(specs->memSpecs[j]);
|
||||
}
|
||||
free(specs->memSpecs);
|
||||
free(specs->executableSegments);
|
||||
free(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
spec->name = NULL;
|
||||
|
||||
specs->memSpecs[i] = spec;
|
||||
}
|
||||
|
||||
uint8_t executableSegmentsFound = 0;
|
||||
|
||||
for (uint8_t i = 0; i < providedSpecCount; i++)
|
||||
{
|
||||
uint8_t specNum = 0xFF;
|
||||
uint8_t is_error = 0;
|
||||
uint8_t seek_found = 0;
|
||||
|
||||
for(uint8_t j = 0; j < sizeof(requiredSegments) / sizeof(memseg_metadata_t); j++)
|
||||
{
|
||||
const memseg_metadata_t seg_def = requiredSegments[j];
|
||||
|
||||
if(strcmp(seg_def.name, segments[i]->name) == 0)
|
||||
{
|
||||
printf("found seg \"%s\"\n", seg_def.name);
|
||||
const uint8_t is_found = requiredSegmentsFoundMap[j];
|
||||
if(is_found)
|
||||
{
|
||||
is_error = 1;
|
||||
sprintf(errbuf, "duplicate segment %s", seg_def.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(seg_def.is_executable == segments[i]->isExecutable || segments[i]->isExecutable)
|
||||
{
|
||||
printf("seg executable req satisfyed (%d)\n", seg_def.is_executable);
|
||||
requiredSegmentsFoundMap[j] = 1;
|
||||
specNum = seg_def.seg_id;
|
||||
seek_found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_error = 1;
|
||||
sprintf(errbuf, "segment %s must be executable", seg_def.name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(seek_found == 0)
|
||||
{
|
||||
is_error = 1;
|
||||
sprintf(errbuf, "unsupported memory segment: %s", segments[i]->name);
|
||||
}
|
||||
|
||||
if(is_error)
|
||||
{
|
||||
freeDevSpec(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
specs->memSpecs[specNum]->name = calloc(strlen(segments[i]->name) + 1, sizeof(char));
|
||||
if(specs->memSpecs[specNum]->name == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate spec %d name", i);
|
||||
freeDevSpec(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(specs->memSpecs[specNum]->name, segments[i]->name);
|
||||
specs->memSpecs[specNum]->start = segments[i]->start;
|
||||
specs->memSpecs[specNum]->len = segments[i]->len;
|
||||
specs->memSpecs[specNum]->wordLen = segments[i]->wordLen;
|
||||
printf("set segment %s :%lu->%lu/%d\n", segments[i]->name, segments[i]->start, segments[i]->len, segments[i]->wordLen);
|
||||
if(segments[i]->isExecutable)
|
||||
{
|
||||
specs->executableSegments[executableSegmentsFound] = specNum;
|
||||
executableSegmentsFound++;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(requiredSegments)/sizeof(memseg_metadata_t); i++)
|
||||
{
|
||||
if(requiredSegmentsFoundMap[i] == 0)
|
||||
{
|
||||
const memseg_metadata_t seg_def = requiredSegments[i];
|
||||
printf("found segment that not specified: \"%s\"\n", seg_def.name);
|
||||
|
||||
specs->memSpecs[seg_def.seg_id]->start = seg_def.default_addr;
|
||||
specs->memSpecs[seg_def.seg_id]->len = seg_def.default_size;
|
||||
specs->memSpecs[seg_def.seg_id]->wordLen = seg_def.word_len;
|
||||
|
||||
specs->memSpecs[seg_def.seg_id]->name = calloc(strlen(seg_def.name) + 1, sizeof(char));
|
||||
|
||||
if(specs->memSpecs[seg_def.seg_id]->name == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate spec %d name", i);
|
||||
freeDevSpec(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(specs->memSpecs[seg_def.seg_id]->name, seg_def.name);
|
||||
|
||||
|
||||
if(seg_def.is_executable)
|
||||
{
|
||||
specs->executableSegments[executableSegmentsFound] = seg_def.seg_id;
|
||||
executableSegmentsFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(executableSegmentsFound < specs->executableSegmentsCount)
|
||||
{
|
||||
sprintf(errbuf, "Not all executable segments found");
|
||||
freeDevSpec(specs);
|
||||
free(requiredSegmentsFoundMap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("set all segments\n");
|
||||
|
||||
#ifdef MEMSEG_PC_SEG_NUM
|
||||
#ifndef MEMSEG_PC_ADDR
|
||||
specs->pcAddr = memSegToGlobal(specs, MEMSEG_PC_SEG_NUM, 0);
|
||||
#else
|
||||
specs->pcAddr = memSegToGlobal(specs, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR);
|
||||
#endif
|
||||
#endif
|
||||
free(requiredSegmentsFoundMap);
|
||||
|
||||
printf("parse specs done\n");
|
||||
return specs;
|
||||
}
|
||||
|
||||
void freeDevSpecs(device_specs_t* specs)
|
||||
{
|
||||
for(size_t k = 0; k < specs->memSpecsCount; k++)
|
||||
{
|
||||
if(specs->memSpecs[k]->name != NULL)
|
||||
{
|
||||
free(specs->memSpecs[k]->name);
|
||||
}
|
||||
free(specs->memSpecs[k]);
|
||||
}
|
||||
free(specs->executableSegments);
|
||||
free(specs->memSpecs);
|
||||
free(specs);
|
||||
}
|
||||
|
||||
void fillSmartReadSpecs(device_specs_t* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount)
|
||||
{
|
||||
specs->smartReadSpecs = smartReadSpecs;
|
||||
specs->smartReadSpecsCount = smartReadSpecsCount;
|
||||
}
|
||||
|
||||
void fillSmartWriteSpecs(device_specs_t* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount)
|
||||
{
|
||||
specs->smartWriteSpecs = smartWriteSpecs;
|
||||
specs->smartWriteSpecsCount = smartWriteSpecsCount;
|
||||
}
|
||||
|
||||
device_public_context_t* initDefault(smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount, char* errbuf)
|
||||
{
|
||||
return NULL;
|
||||
//TODO
|
||||
device_specs_t* specs = calloc(1, sizeof(device_specs_t));
|
||||
if (specs == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
specs->memSpecsCount = 4;
|
||||
specs->memSpecs = calloc(specs->memSpecsCount, sizeof(memseg_spec_t*));
|
||||
if (specs->memSpecs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate default mem segment specs");
|
||||
free(specs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
specs->executableSegmentsCount = 1;
|
||||
specs->executableSegments = calloc(1, sizeof(uint8_t));
|
||||
if (specs->executableSegments == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate default executable segments");
|
||||
free(specs->memSpecs);
|
||||
free(specs);
|
||||
return NULL;
|
||||
}
|
||||
specs->executableSegments[0] = 0;
|
||||
|
||||
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
|
||||
{
|
||||
specs->memSpecs[i] = calloc(1, sizeof(memseg_spec_t));
|
||||
if (specs->memSpecs[i] == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate default mem seg spec %u", i);
|
||||
for (uint8_t j = 0; j < i; j++)
|
||||
{
|
||||
free(specs->memSpecs[j]);
|
||||
}
|
||||
free(specs->memSpecs);
|
||||
free(specs->executableSegments);
|
||||
free(specs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
specs->smartReadSpecs = smartReadSpecs;
|
||||
specs->smartReadSpecsCount = smartReadSpecsCount;
|
||||
|
||||
specs->smartWriteSpecs = smartWriteSpecs;
|
||||
specs->smartWriteSpecsCount = smartWriteSpecsCount;
|
||||
|
||||
specs->memSpecs[0]->len = 1024;
|
||||
specs->memSpecs[0]->start = 0;
|
||||
specs->memSpecs[0]->wordLen = OPCODE_WORDSIZE;
|
||||
|
||||
specs->memSpecs[1]->len = 32;
|
||||
specs->memSpecs[1]->start = (1024 * OPCODE_WORDSIZE);
|
||||
specs->memSpecs[1]->wordLen = GP_REG_CELL_WORDS;
|
||||
|
||||
specs->memSpecs[2]->len = 0xFF;
|
||||
specs->memSpecs[2]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS);
|
||||
specs->memSpecs[2]->wordLen = IO_REG_CELL_WORDS;
|
||||
|
||||
specs->memSpecs[3]->len = 0xFFFF;
|
||||
specs->memSpecs[3]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS) + (0xFF * IO_REG_CELL_WORDS);
|
||||
specs->memSpecs[3]->wordLen = RAM_CELL_WORDS;
|
||||
|
||||
char initErrbuf[200];
|
||||
device_public_context_t* ret = init(specs, initErrbuf);
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to init default: %s", initErrbuf);
|
||||
freeDevSpec(specs);
|
||||
return NULL;
|
||||
}
|
||||
free(specs->memSpecs);
|
||||
free(specs->executableSegments);
|
||||
free(specs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
device_public_context_t* init(device_specs_t* specs, char* errbuf)
|
||||
{
|
||||
device_public_context_t* pubDevContext = calloc(1, sizeof(device_public_context_t));
|
||||
if (pubDevContext == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate public context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char initErrbuf[200];
|
||||
device_info_t* devInfo = initSpecs(specs, initErrbuf);
|
||||
if (devInfo == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to init specs: %s", initErrbuf);
|
||||
free(pubDevContext);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pubDevContext->deviceInfo = (void*)devInfo;
|
||||
pubDevContext->deviceMem = devInfo->deviceMem;
|
||||
|
||||
return pubDevContext;
|
||||
}
|
||||
|
||||
uint8_t pubDeviceType()
|
||||
{
|
||||
return DEVICE_TYPE;
|
||||
}
|
||||
|
||||
size_t pubExtractPcounter(device_public_context_t* devContext)
|
||||
{
|
||||
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
|
||||
return (size_t)(*devInfo->pc);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void* pubExtractPcounterPtr(device_public_context_t* devContext)
|
||||
{
|
||||
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
|
||||
return (void*)(devInfo->pc);
|
||||
|
||||
}
|
||||
uint8_t pubExtractPcounterSizeWords()
|
||||
{
|
||||
return sizeof(prog_counter_t);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void reset (device_specs_t* specs, device_public_context_t* devInfo)
|
||||
{
|
||||
// printf("reset device\n");
|
||||
// uint8_t fuck = 0;
|
||||
for(size_t i = 0; i < specs->memSpecsCount; i++)
|
||||
{
|
||||
if(i != MEMDATA_OPSIZE)
|
||||
{
|
||||
const memseg_spec_t* spec = specs->memSpecs[i];
|
||||
for(size_t j = 0; j < spec->len; j++)
|
||||
{
|
||||
((uint8_t*)devInfo->deviceMem->cells[i])[j] = 0;
|
||||
// fuck++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
devInfo->deviceMem->memwriteLen = 0;
|
||||
devInfo->deviceMem->memreadLen = 0;
|
||||
}
|
||||
|
||||
11
flatbuffers/auth/auth.fbs
Normal file
11
flatbuffers/auth/auth.fbs
Normal 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;
|
||||
}
|
||||
12
flatbuffers/compacted.fbs
Normal file
12
flatbuffers/compacted.fbs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace hmmmm;
|
||||
|
||||
// A single serialized ServerMessage frame packed into a byte vector.
|
||||
table CompactedFrame {
|
||||
data: [ubyte];
|
||||
}
|
||||
|
||||
// A batch of ServerMessage frames sent as a single WebSocket frame to reduce
|
||||
// per-frame syscall overhead when message rate exceeds the batching threshold.
|
||||
table CompactedMessage {
|
||||
frames: [CompactedFrame];
|
||||
}
|
||||
16
flatbuffers/config/clock.fbs
Normal file
16
flatbuffers/config/clock.fbs
Normal 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;
|
||||
}
|
||||
25
flatbuffers/config/config.fbs
Normal file
25
flatbuffers/config/config.fbs
Normal file
@@ -0,0 +1,25 @@
|
||||
include "device.fbs";
|
||||
include "mem_config.fbs";
|
||||
include "clock.fbs";
|
||||
|
||||
namespace hmmmm.config;
|
||||
|
||||
// Top-level emulation configuration sent by the client via ConfigCtrlMessage.
|
||||
// Contains the root composite device and simulation parameters.
|
||||
table EmulationConfig {
|
||||
sim_rate_limit: uint64; // max virtual-seconds per real-second (0 = unlimited)
|
||||
root_id: string; // ID of the root compose device
|
||||
root: ComposeDeviceConfig; // the root device tree
|
||||
mem_setup: [MemSetup]; // firmware / memory init instructions
|
||||
}
|
||||
|
||||
// Legacy: flat system configuration for standalone config files.
|
||||
table SystemConfig {
|
||||
devices: [DeviceEntry];
|
||||
clock: ClockConfig;
|
||||
projections: [Projection];
|
||||
intercepts: [Intercept];
|
||||
mem_setup: [MemSetup];
|
||||
}
|
||||
|
||||
root_type EmulationConfig;
|
||||
50
flatbuffers/config/device.fbs
Normal file
50
flatbuffers/config/device.fbs
Normal file
@@ -0,0 +1,50 @@
|
||||
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];
|
||||
}
|
||||
|
||||
// A composite device that contains other devices.
|
||||
table ComposeDeviceConfig {
|
||||
devices: [DeviceEntry]; // child devices (base or compose)
|
||||
projections: [Projection]; // memory projections between children
|
||||
intercepts: [Intercept]; // memory intercepts between children
|
||||
}
|
||||
|
||||
// A device can be either a leaf (base) or a container (compose).
|
||||
union DeviceConfig {
|
||||
BaseDeviceConfig,
|
||||
ComposeDeviceConfig,
|
||||
}
|
||||
|
||||
// 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;
|
||||
addr: uint32;
|
||||
len: uint32;
|
||||
word_len: uint8;
|
||||
executable: bool;
|
||||
}
|
||||
|
||||
// One device entry in a device tree.
|
||||
table DeviceEntry {
|
||||
// Local identifier, unique within this composite (e.g. "core", "gpio_a").
|
||||
id: string;
|
||||
|
||||
// Inline device configuration — either base or compose.
|
||||
config: DeviceConfig;
|
||||
|
||||
//clock: DeviceClockConfig;
|
||||
clock_divider: uint64 = 1;
|
||||
overrides: [MemSegOverride];
|
||||
}
|
||||
|
||||
// Root type when serialising a standalone device config to a binary file.
|
||||
root_type BaseDeviceConfig;
|
||||
73
flatbuffers/config/mem_config.fbs
Normal file
73
flatbuffers/config/mem_config.fbs
Normal 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;
|
||||
addr: 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;
|
||||
}
|
||||
10
flatbuffers/control/config_ctrl.fbs
Normal file
10
flatbuffers/control/config_ctrl.fbs
Normal file
@@ -0,0 +1,10 @@
|
||||
include "config/config.fbs";
|
||||
|
||||
namespace hmmmm.ctrl.config_ctrl;
|
||||
|
||||
// Client → Server: load a new emulation configuration.
|
||||
// Only allowed when the emulator is in STILL state.
|
||||
// Completely replaces any previously loaded configuration.
|
||||
table ConfigCtrlMessage {
|
||||
config: hmmmm.config.EmulationConfig;
|
||||
}
|
||||
43
flatbuffers/control/config_notif.fbs
Normal file
43
flatbuffers/control/config_notif.fbs
Normal file
@@ -0,0 +1,43 @@
|
||||
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. ["sys", "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];
|
||||
}
|
||||
|
||||
// Sent when a config load request fails.
|
||||
table ConfigLoadError {
|
||||
message: string;
|
||||
}
|
||||
|
||||
union ConfigNotifPayload {
|
||||
DeviceConfigUpdateNotif,
|
||||
DeviceIdMappingNotif,
|
||||
ConfigLoadError,
|
||||
}
|
||||
|
||||
table ConfigNotifMessage {
|
||||
tclk: uint64;
|
||||
payload: ConfigNotifPayload;
|
||||
}
|
||||
39
flatbuffers/control/control.fbs
Normal file
39
flatbuffers/control/control.fbs
Normal 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;
|
||||
}
|
||||
15
flatbuffers/control/exec_ctrl.fbs
Normal file
15
flatbuffers/control/exec_ctrl.fbs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace hmmmm.ctrl.exec;
|
||||
|
||||
enum ExecPrompt: byte {
|
||||
_invalid = 0,
|
||||
start = 1,
|
||||
pause = 2,
|
||||
resume = 3,
|
||||
stop = 4,
|
||||
reset = 5,
|
||||
}
|
||||
|
||||
table ExecCtrlMessage {
|
||||
prompt: ExecPrompt;
|
||||
tick_count: uint64 = 0; // 0 = run indefinitely, >0 = auto-pause after N ticks
|
||||
}
|
||||
13
flatbuffers/control/exec_notif.fbs
Normal file
13
flatbuffers/control/exec_notif.fbs
Normal 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;
|
||||
}
|
||||
19
flatbuffers/control/lost.fbs
Normal file
19
flatbuffers/control/lost.fbs
Normal 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];
|
||||
}
|
||||
16
flatbuffers/control/orphaned.fbs
Normal file
16
flatbuffers/control/orphaned.fbs
Normal 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];
|
||||
}
|
||||
10
flatbuffers/control/qos.fbs
Normal file
10
flatbuffers/control/qos.fbs
Normal 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
|
||||
}
|
||||
8
flatbuffers/control/setup_buf.fbs
Normal file
8
flatbuffers/control/setup_buf.fbs
Normal 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
46
flatbuffers/mem/mem.fbs
Normal 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;
|
||||
}
|
||||
44
flatbuffers/proto.fbs
Normal file
44
flatbuffers/proto.fbs
Normal file
@@ -0,0 +1,44 @@
|
||||
include "auth/auth.fbs";
|
||||
include "control/control.fbs";
|
||||
include "stream/stream.fbs";
|
||||
include "mem/mem.fbs";
|
||||
include "compacted.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,
|
||||
CompactedMessage,
|
||||
}
|
||||
|
||||
// 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;
|
||||
27
flatbuffers/stream/stream.fbs
Normal file
27
flatbuffers/stream/stream.fbs
Normal 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;
|
||||
}
|
||||
18
flatbuffers/stream/stream_data.fbs
Normal file
18
flatbuffers/stream/stream_data.fbs
Normal 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];
|
||||
}
|
||||
32
flatbuffers/stream/stream_reg.fbs
Normal file
32
flatbuffers/stream/stream_reg.fbs
Normal 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
120
flatbuffers_example_main.c
Normal 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;
|
||||
}
|
||||
60
glob.toml
60
glob.toml
@@ -1,60 +0,0 @@
|
||||
[dev]
|
||||
core = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc.toml"
|
||||
# gpio_a = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_gpio/gpio.toml"
|
||||
|
||||
[config.core.mem.ps]
|
||||
len = 128
|
||||
|
||||
|
||||
[clock]
|
||||
limiter = 16000000
|
||||
|
||||
[clock.core]
|
||||
src = ""
|
||||
divider = 1
|
||||
|
||||
[clock.gpio_a]
|
||||
src = "core"
|
||||
divider = 2
|
||||
|
||||
|
||||
# [mem.projection.gpio_a.ext_reg_io]
|
||||
# base_at = "core"
|
||||
# base_seg = "reg_io"
|
||||
# projection_shift = 0
|
||||
|
||||
|
||||
[mem.intercept.PORTA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
addr = 0
|
||||
seg = "reg_io"
|
||||
|
||||
|
||||
[mem.intercept.PINA]
|
||||
base_at = "core"
|
||||
point_at = "gpio_a"
|
||||
base_addr = 1
|
||||
point_addr = 1
|
||||
base_seg = "reg_io"
|
||||
point_seg = "ext_reg_io"
|
||||
|
||||
[mem.setup.core.ps]
|
||||
filepath = "/home/nikto_b/Documents/avr_selftests/test.bin"
|
||||
|
||||
|
||||
[mem.setup.core.ds]
|
||||
filepath = "/dev/urandom"
|
||||
overflow_behaviour = "ignore"
|
||||
|
||||
# - почему си (адресная арифметика, отсутствие мешающих фичей языка, широкое распространение среди разработчеков-железячников)
|
||||
# - подход к модульности (всё базовое устройство, запускаем комбинированные устройства из базовых и комбинированных)
|
||||
# - структура базового модуля эмулятора (какие есть варианты полей конфига)
|
||||
# - структура комбинированного модуля эмулятора (какие есть варианты полей конфига)
|
||||
# - структура комбинированного модуля эмулятора (какие конфиг вырождается в эмулятор)
|
||||
# - процесс инициализации модуля (6 этапов инициализации)
|
||||
# - процесс исполнения модуля (чисто дергаем "makeTick" у каждой dlib'ы)
|
||||
# - реализация перехватчиков доступа (пример с sbi, диаграмма с тестовым перехватчиком)
|
||||
# - реализация проецирования памяти (диаграммы с колбасами)
|
||||
# - тестовая прошивка (селфтест сложения-вычитания)
|
||||
# - пример исполнения (?)
|
||||
Submodule hmmmm_scripts updated: 9cc7b36cbb...6a52cfbb3c
@@ -2,6 +2,7 @@
|
||||
#define __BASE_DEVICE_H__
|
||||
|
||||
#include "hmmmm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
void* specs;
|
||||
@@ -9,6 +10,7 @@ typedef struct {
|
||||
device_public_context_t* ctx;
|
||||
uint64_t clockCycleLimit;
|
||||
uint64_t clockCycleCounter;
|
||||
uint64_t clockDivider;
|
||||
} device_handle_t;
|
||||
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "wsServer/include/ws.h"
|
||||
#include "ptQueue/inc/ptQueue.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
|
||||
typedef struct {
|
||||
ws_cli_conn_t clientId;
|
||||
uint8_t isAuthed;
|
||||
ptQueue* incomeQ;
|
||||
ptQueue* outcomeQ;
|
||||
queue_spsc_t* incomeQ;
|
||||
queue_spsc_t* outcomeQ;
|
||||
uint64_t connectedAt;
|
||||
uint32_t streamRegIterator;
|
||||
} ClientContext;
|
||||
|
||||
@@ -35,7 +35,6 @@ typedef struct {
|
||||
device_handle_t** devHandlers;
|
||||
} compose_dev_handle_t;
|
||||
|
||||
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf);
|
||||
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf);
|
||||
|
||||
#endif // ifndef __COMPOSE_DEVICE_H__
|
||||
34
inc/config.h
34
inc/config.h
@@ -1,36 +1,12 @@
|
||||
#ifndef __HMMMM_CONFIG_H__
|
||||
#define __HMMMM_CONFIG_H__
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pub/libhmmmm/config.h"
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
size_t start;
|
||||
size_t len;
|
||||
uint8_t wordLen;
|
||||
uint8_t isExecutable;
|
||||
} conf_mem_seg_t;
|
||||
|
||||
typedef struct {
|
||||
conf_mem_seg_t** memSegConfs;
|
||||
} conf_mem_t;
|
||||
|
||||
typedef struct {
|
||||
char** id;
|
||||
char** clockId;
|
||||
uint64_t clockDivider;
|
||||
uint64_t clockMultipler;
|
||||
conf_mem_t* memConf;
|
||||
char* libPath;
|
||||
} conf_dev_t;
|
||||
|
||||
|
||||
|
||||
void freeMemSegConf(conf_mem_seg_t* memSegConf);
|
||||
void freeMemConf(conf_mem_t* memConf);
|
||||
// void freeMemSegConf(conf_mem_seg_t* memSegConf);
|
||||
// void freeMemConf(conf_mem_t* memConf);
|
||||
void freeConf(conf_dev_t* conf);
|
||||
void freeComposeId(char** id);
|
||||
uint8_t compareComposeId(char** idA, char** idB);
|
||||
// uint8_t compareComposeId(char** idA, char** idB);
|
||||
|
||||
#endif // ifndef __HMMMM_CONFIG_H__
|
||||
#endif
|
||||
@@ -2,40 +2,58 @@
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ptQueue/inc/ptQueue.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
#include "wsServer/include/ws.h"
|
||||
#include "ptQueue/inc/mpsc.h"
|
||||
|
||||
#include "linkedlist.h"
|
||||
#include "sized_ptr.h"
|
||||
|
||||
#include "streamed.h"
|
||||
#include "flatcc/flatcc_builder.h"
|
||||
|
||||
typedef struct {
|
||||
SizedPtr* bufs;
|
||||
uint8_t buffersCount;
|
||||
_Atomic (uint8_t) readRequestIdx;
|
||||
_Atomic (uint8_t) currWritingIdx;
|
||||
CACHE_ALIGN _Atomic (uint8_t) readRequestIdx;
|
||||
CACHE_ALIGN _Atomic (uint8_t) currWritingIdx;
|
||||
} OutgoingBuffers;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t* resetRequest;
|
||||
uint8_t* emulState;
|
||||
uint64_t* clockCounter;
|
||||
uint64_t* tickTarget; // 0 = run indefinitely, >0 = auto-pause at this tick
|
||||
LinkedListEntry** clientsHead;
|
||||
uint8_t* utilizedFlag;
|
||||
OutgoingBuffers* outBufs;
|
||||
DeviceSegStreamReg** deviceStreamRegs;
|
||||
uint8_t** devicesMem;
|
||||
size_t** devicesMemSegAddrs;
|
||||
size_t devicesCount;
|
||||
void** deviceHandles; // device_handle_t** — dynamically loaded devices
|
||||
void* interceptCtxs; // contiguous intercept_ctx_t array (freed as single block)
|
||||
size_t interceptCtxCount;
|
||||
flatcc_builder_t stream_builder;
|
||||
uint64_t simRateLimit;
|
||||
// Cached DeviceIdMappingNotif broadcast message (sent to newly authed clients)
|
||||
uint8_t* deviceIdMappingMsg;
|
||||
size_t deviceIdMappingMsgLen;
|
||||
queue_mpsc_t* inMsgQueue;
|
||||
queue_spsc_t* outMsgQueue;
|
||||
} EmulContext;
|
||||
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t registerMutex;
|
||||
ptQueue* regQueue;
|
||||
queue_spsc_t* regQueue;
|
||||
queue_mpsc_t* inMsgQueue;
|
||||
uint8_t* accessToken;
|
||||
EmulContext* emulContext;
|
||||
} ServerContext;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifndef __HMMMMM__
|
||||
#define __HMMMMM__
|
||||
#include "libhmmmm.h"
|
||||
#include "pub/libhmmmm/device.h"
|
||||
#include "config.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -25,6 +25,7 @@ typedef struct {
|
||||
void (*freeDevMem)(device_mem_t* mem);
|
||||
void (*fillSmartReadSpecs)(void* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount);
|
||||
void (*fillSmartWriteSpecs)(void* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount);
|
||||
void (*reset)(void* specs, device_public_context_t* devInfo);
|
||||
} device_lib_t;
|
||||
|
||||
device_lib_t* loadDeviceLib(const char *libpath, char* errbuf);
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __LIB_HMMMMM__
|
||||
#define __LIB_HMMMMM__
|
||||
|
||||
#include "libmem.h"
|
||||
#include "libdevice.h"
|
||||
|
||||
#define EXTENDED_DEVICE_TYPE_DUMMY 0
|
||||
#define EXTENDED_DEVICE_TYPE_INSTR_SIMUL 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // ifndef __LIB_HMMMMM__
|
||||
52
inc/libmem.h
52
inc/libmem.h
@@ -1,52 +0,0 @@
|
||||
#ifndef __LIBMEM_H__
|
||||
#define __LIBMEM_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define GET_BIT(n, b) ((n >> b) & 1)
|
||||
|
||||
// Internal mem handlers
|
||||
typedef void* (*mem_h_read_func)(uint64_t ident, uint64_t addr, void* rawCells);
|
||||
typedef void (*mem_h_write_func)(uint64_t ident, uint64_t addr, void* rawCells, void* data);
|
||||
|
||||
typedef struct {
|
||||
mem_h_read_func func;
|
||||
uint64_t ident;
|
||||
} mem_h_read_handler;
|
||||
typedef struct {
|
||||
mem_h_write_func func;
|
||||
uint64_t ident;
|
||||
} mem_h_write_handler;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* rawCells;
|
||||
void** cells;
|
||||
uint64_t smartAddrReadMask;
|
||||
uint64_t smartAddrWriteMask;
|
||||
mem_h_read_handler* smartAddrReadHandlers;
|
||||
mem_h_write_handler* smartAddrWriteHandlers;
|
||||
char** memsegNames;
|
||||
uint64_t* memsegShifts;
|
||||
uint64_t* memreadCellAddrs;
|
||||
uint64_t* memwriteCellAddrs;
|
||||
uint8_t memreadLen;
|
||||
uint8_t memwriteLen;
|
||||
} device_mem_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint8_t wordLen;
|
||||
char* name;
|
||||
} memseg_spec_t;
|
||||
|
||||
|
||||
// External handlers
|
||||
typedef void* (*ext_h_read_func)(uint64_t addr, void* rawCells, void* devContext);
|
||||
typedef void (*ext_h_write_func)(uint64_t addr, void* rawCells, void* data, void* devContext);
|
||||
|
||||
#endif // ifndef __LIBMEM_H__
|
||||
@@ -4,8 +4,9 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
|
||||
void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen);
|
||||
void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen);
|
||||
void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen);
|
||||
|
||||
#endif
|
||||
14
inc/proto/handlers/config.h
Normal file
14
inc/proto/handlers/config.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __PROTO_HANDLERS_CONFIG_H__
|
||||
#define __PROTO_HANDLERS_CONFIG_H__
|
||||
|
||||
#include "context.h"
|
||||
#include "proto/handlers/ws.h"
|
||||
#include "config_ctrl_reader.h"
|
||||
|
||||
void handleConfigCtrlMessage(
|
||||
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
ClientContext* ctx,
|
||||
EmulContext* emulContext);
|
||||
|
||||
#endif // __PROTO_HANDLERS_CONFIG_H__
|
||||
@@ -1,10 +1,16 @@
|
||||
#ifndef __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 "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 //ifndef __PROTO_HANDLERS_CONTROL_H__
|
||||
#endif // __PROTO_HANDLERS_CONTROL_H__
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#ifndef __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 "mem_reader.h"
|
||||
|
||||
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext);
|
||||
|
||||
#endif //ifndef __PROTO_HANDLERS_MEM_H__
|
||||
void handleIncomingMemMessage(
|
||||
hmmmm_mem_MemClientMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
ClientContext* ctx,
|
||||
EmulContext* emulContext);
|
||||
|
||||
#endif // __PROTO_HANDLERS_MEM_H__
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
#ifndef __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 "stream_reader.h"
|
||||
|
||||
|
||||
void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId);
|
||||
// static void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId);
|
||||
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__
|
||||
|
||||
@@ -1,26 +1,82 @@
|
||||
#ifndef __PROTO_MSG_H__
|
||||
#define __PROTO_MSG_H__
|
||||
|
||||
#include "client.h"
|
||||
#include "wsServer/include/ws.h"
|
||||
#include <stdint.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;
|
||||
ClientContext* ctx;
|
||||
} FbMessage;
|
||||
|
||||
typedef struct {
|
||||
ws_cli_conn_t clientIdx;
|
||||
uint8_t* msg;
|
||||
size_t msgLen;
|
||||
uint64_t dispatch_us; // microsecond timestamp when queued (for batching window)
|
||||
} OutgoingMessage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t nonce;
|
||||
uint8_t packetType;
|
||||
uint8_t payloadHeader;
|
||||
const void* payload;
|
||||
size_t payloadLen;
|
||||
} BaseMessage;
|
||||
// Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer;
|
||||
// ownership is transferred to the caller (dispatchOutgoingMessage will free it).
|
||||
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
|
||||
|
||||
BaseMessage* parseMessage(const uint8_t* bytes, size_t size);
|
||||
uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut);
|
||||
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);
|
||||
// Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame.
|
||||
uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out);
|
||||
|
||||
#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);
|
||||
|
||||
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{DeviceIdMappingNotif}}} frame.
|
||||
// paths: array of NULL-terminated string arrays; path_lens: component count per path.
|
||||
// seg_names[i]: array of seg_counts[i] segment name strings for device i.
|
||||
uint8_t* fb_build_config_device_id_mapping(
|
||||
uint64_t nonce,
|
||||
char** paths[], size_t path_lens[],
|
||||
char* seg_names[][64], size_t seg_counts[],
|
||||
size_t device_count, size_t* len_out);
|
||||
|
||||
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{ConfigLoadError}}} frame.
|
||||
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out);
|
||||
|
||||
// Build a ServerMessage{CompactedMessage{frames=[...]}} frame.
|
||||
// msgs / msg_lens: array of `count` pre-built ServerMessage byte buffers to pack.
|
||||
// The caller retains ownership of the source buffers; this function copies their bytes.
|
||||
// Nonce is set to UINT64_MAX (no-nonce / fire-and-forget).
|
||||
uint8_t* fb_build_compacted_message(
|
||||
uint8_t* const* msgs, const size_t* msg_lens, size_t count, size_t* len_out);
|
||||
|
||||
#endif // __PROTO_MSG_H__
|
||||
|
||||
29
inc/pub/libhmmmm/config.h
Normal file
29
inc/pub/libhmmmm/config.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __HMMMM_PUB_LIB_CONFIG_H__
|
||||
#define __HMMMM_PUB_LIB_CONFIG_H__
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
size_t start;
|
||||
size_t len;
|
||||
uint8_t wordLen;
|
||||
uint8_t isExecutable;
|
||||
} conf_mem_seg_t;
|
||||
|
||||
typedef struct {
|
||||
conf_mem_seg_t** memSegConfs;
|
||||
} conf_mem_t;
|
||||
|
||||
typedef struct {
|
||||
char** id;
|
||||
char** clockId;
|
||||
uint64_t clockDivider;
|
||||
uint64_t clockMultipler;
|
||||
conf_mem_t* memConf;
|
||||
char* libPath;
|
||||
} conf_dev_t;
|
||||
|
||||
|
||||
#endif // ifndef __HMMMM_PUB_LIB_CONFIG_H__
|
||||
@@ -2,10 +2,15 @@
|
||||
#define __LIBDEVICE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "libmem.h"
|
||||
#include "mem.h"
|
||||
|
||||
#define SMART_ADDR_TYPE_GLOBAL 1
|
||||
#define SMART_ADDR_TYPE_SEGMENTED 2
|
||||
|
||||
#define EXTENDED_DEVICE_TYPE_DUMMY 0
|
||||
#define EXTENDED_DEVICE_TYPE_INSTR_SIMUL 1
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t addr;
|
||||
95
inc/pub/libhmmmm/mem.h
Normal file
95
inc/pub/libhmmmm/mem.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef __HMMMM_PUB_MEM_H__
|
||||
#define __HMMMM_PUB_MEM_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define GET_BIT(n, b) ((uint8_t)((n >> b) & 1))
|
||||
|
||||
#define MEM_ACCESS_INTERCEPT_BUF_SIZE 64
|
||||
|
||||
// Internal mem handlers
|
||||
typedef void* (*mem_h_read_func)(uint64_t ident, uint64_t addr, void* rawCells);
|
||||
typedef void (*mem_h_write_func)(uint64_t ident, uint64_t addr, void* rawCells, void* data);
|
||||
|
||||
typedef struct {
|
||||
mem_h_read_func func;
|
||||
uint64_t ident;
|
||||
} mem_h_read_handler;
|
||||
typedef struct {
|
||||
mem_h_write_func func;
|
||||
uint64_t ident;
|
||||
} mem_h_write_handler;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* rawCells;
|
||||
void** cells;
|
||||
uint64_t smartAddrReadMask;
|
||||
uint64_t smartAddrWriteMask;
|
||||
mem_h_read_handler* smartAddrReadHandlers;
|
||||
mem_h_write_handler* smartAddrWriteHandlers;
|
||||
char** memsegNames;
|
||||
uint64_t* memsegShifts;
|
||||
uint64_t* memsegSizes;
|
||||
uint8_t* memsegCellSizes;
|
||||
uint64_t* memreadCellAddrs;
|
||||
uint8_t* memwriteCellSegments;
|
||||
uint64_t* memwriteCellAddrs;
|
||||
uint64_t* memwriteValues;
|
||||
uint8_t memreadLen;
|
||||
uint8_t memwriteLen;
|
||||
} device_mem_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint8_t wordLen;
|
||||
char* name;
|
||||
} memseg_spec_t;
|
||||
|
||||
|
||||
// External handlers
|
||||
typedef void* (*ext_h_read_func)(uint64_t addr, void* rawCells, void* devContext);
|
||||
typedef void (*ext_h_write_func)(uint64_t addr, void* rawCells, void* data, void* devContext);
|
||||
|
||||
|
||||
|
||||
#define READ_MEM(__tgt, __mem, __segno, __addr, __cell_t) \
|
||||
{\
|
||||
uint64_t __globalAddr = __mem->memsegShifts[__segno] + __addr; \
|
||||
if (__mem->smartAddrReadHandlers[__globalAddr].func != NULL) \
|
||||
{ \
|
||||
/* __tgt = *(__cell_t*)__mem->smartAddrReadHandlers[__globalAddr].func(__mem->smartAddrReadHandlers[__globalAddr].ident, __addr, __mem->rawCells + __mem->memsegShifts[__segno]);*/ \
|
||||
__tgt = 0;\
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
__tgt = ((__cell_t*)__mem->cells[__segno])[__addr]; \
|
||||
} \
|
||||
__mem->memreadCellAddrs[__mem->memreadLen] = __globalAddr; \
|
||||
__mem->memreadLen += 1; \
|
||||
}
|
||||
|
||||
#define WRITE_MEM(__mem, __segno, __addr, __cell_t, __val) \
|
||||
{ \
|
||||
uint64_t __globalAddr = ((uint64_t)(__mem->memsegShifts[__segno]) + (uint64_t)(__addr)); \
|
||||
if (0 && __mem->smartAddrWriteHandlers[__globalAddr].func != NULL) \
|
||||
{ \
|
||||
/*__cell_t __dat = (__cell_t)(__val);*/ \
|
||||
/*__mem->smartAddrWriteHandlers[__globalAddr].func(__mem->smartAddrWriteHandlers[__globalAddr].ident, __addr, __mem->rawCells + __mem->memsegShifts[__segno], (void*)&__dat);*/ \
|
||||
} \
|
||||
else if(0) \
|
||||
{ \
|
||||
((__cell_t*)__mem->cells[__segno])[__addr] = (__cell_t)(__val); \
|
||||
} \
|
||||
__mem->memwriteCellAddrs[__mem->memwriteLen] = __addr; \
|
||||
__mem->memwriteCellSegments[__mem->memwriteLen] = __segno; \
|
||||
/*__mem->memwriteWordLengths[__mem->memwriteLen] = sizeof(__cell_t);*/ \
|
||||
(__mem->memwriteValues[__mem->memwriteLen]) = (__cell_t)(__val); \
|
||||
__mem->memwriteLen += 1; \
|
||||
}
|
||||
|
||||
#endif // ifndef __HMMMM_PUB_MEM_H__
|
||||
@@ -12,7 +12,9 @@
|
||||
typedef struct {
|
||||
ClientContext* clientContext;
|
||||
uint32_t regId;
|
||||
uint32_t segId;
|
||||
uint64_t startAddr;
|
||||
uint64_t startGlobalAddr;
|
||||
uint64_t segLen;
|
||||
uint8_t mode;
|
||||
} StreamReg;
|
||||
|
||||
BIN
src/.main.c.swp
Normal file
BIN
src/.main.c.swp
Normal file
Binary file not shown.
@@ -2,49 +2,29 @@
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "tomlc99/toml.h"
|
||||
|
||||
#include "base_device.h"
|
||||
|
||||
|
||||
|
||||
|
||||
device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
char intErrbuf[1024];
|
||||
|
||||
conf_dev_t *devConf = openBaseDeviceConfig(configPath, intErrbuf);
|
||||
if (devConf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to read device config: %s", intErrbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_handle_t* ret = openBaseDevice(devConf, intErrbuf);
|
||||
|
||||
if(ret == NULL)
|
||||
{
|
||||
freeConf(devConf);
|
||||
free(devConf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void closeBaseDevice(device_handle_t* devHandle)
|
||||
{
|
||||
if(devHandle == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(devHandle->ctx != NULL)
|
||||
{
|
||||
if(devHandle->specs != NULL)
|
||||
if(devHandle->specs != NULL && devHandle->lib != NULL)
|
||||
{
|
||||
devHandle->lib->freeSpecs(devHandle->specs);
|
||||
}
|
||||
if(devHandle->ctx->deviceMem != NULL)
|
||||
if(devHandle->ctx->deviceMem != NULL && devHandle->lib != NULL)
|
||||
{
|
||||
devHandle->lib->freeDevMem(devHandle->ctx->deviceMem);
|
||||
}
|
||||
free(devHandle->ctx);
|
||||
}
|
||||
if(devHandle->lib != NULL)
|
||||
{
|
||||
if(devHandle->lib->extendedHandlers != NULL)
|
||||
{
|
||||
free(devHandle->lib->extendedHandlers);
|
||||
@@ -52,13 +32,14 @@ void closeBaseDevice(device_handle_t* devHandle)
|
||||
dlclose(devHandle->lib->_dlhandl);
|
||||
free(devHandle->lib);
|
||||
}
|
||||
}
|
||||
|
||||
device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
{
|
||||
device_handle_t* ret = malloc(sizeof(device_handle_t));
|
||||
if (ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device handle struct");
|
||||
snprintf(errbuf, 1024, "unable to allocate device handle struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -67,7 +48,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
device_lib_t* devLib = loadDeviceLib(devConf->libPath, intErrbuf);
|
||||
if (devLib == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device library %s: %s", devConf->libPath, intErrbuf);
|
||||
snprintf(errbuf, 1024, "unable to load device library %s: %s", devConf->libPath, intErrbuf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
@@ -76,7 +57,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
|
||||
if (devSpecs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "device config parse error: %s", intErrbuf);
|
||||
snprintf(errbuf, 1024, "device config parse error: %s", intErrbuf);
|
||||
dlclose(devLib->_dlhandl);
|
||||
free(ret);
|
||||
free(devLib);
|
||||
@@ -88,189 +69,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
ret->specs = devSpecs;
|
||||
ret->clockCycleCounter = 0;
|
||||
ret->clockCycleLimit = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
conf_mem_seg_t** parseMemToml(const toml_table_t* memTable)
|
||||
{
|
||||
uint16_t memSegIdx = 0;
|
||||
const char* memSegKey = toml_key_in(memTable, 0);
|
||||
while (memSegKey && memSegIdx < 0xFFFF)
|
||||
{
|
||||
memSegIdx++;
|
||||
memSegKey = toml_key_in(memTable, memSegIdx);
|
||||
}
|
||||
|
||||
conf_mem_seg_t** segConfigs = malloc(sizeof(conf_mem_seg_t*) * ((size_t)memSegIdx + 1));
|
||||
if (segConfigs == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
segConfigs[memSegIdx] = NULL;
|
||||
|
||||
for (size_t i = 0; i < memSegIdx; i++)
|
||||
{
|
||||
segConfigs[i] = (conf_mem_seg_t*)malloc(sizeof(conf_mem_seg_t));
|
||||
memSegKey = toml_key_in(memTable, (int)i);
|
||||
|
||||
if (segConfigs[i] == NULL || memSegKey == NULL)
|
||||
{
|
||||
if (segConfigs[i] != NULL)
|
||||
{
|
||||
free(segConfigs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
segConfigs[i]->name = (char*)malloc(strlen(memSegKey) + 1);
|
||||
if (segConfigs[i]->name == NULL)
|
||||
{
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
if (j < i)
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(segConfigs[i]->name, memSegKey);
|
||||
|
||||
toml_table_t* segTable = toml_table_in(memTable, memSegKey);
|
||||
if (segTable == NULL)
|
||||
{
|
||||
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t startDatum = toml_int_in(segTable, "start");
|
||||
toml_datum_t lenDatum = toml_int_in(segTable, "len");
|
||||
toml_datum_t wordLenDatum = toml_int_in(segTable, "wordLen");
|
||||
toml_datum_t executableDatum = toml_bool_in(segTable, "executable");
|
||||
|
||||
if (!startDatum.ok || !lenDatum.ok || !wordLenDatum.ok)
|
||||
{
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
segConfigs[i]->start = (size_t)startDatum.u.i;
|
||||
segConfigs[i]->len = (size_t)lenDatum.u.i;
|
||||
segConfigs[i]->wordLen = (uint8_t)wordLenDatum.u.i;
|
||||
segConfigs[i]->isExecutable = 0;
|
||||
if (executableDatum.ok)
|
||||
{
|
||||
segConfigs[i]->isExecutable = executableDatum.u.b != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return segConfigs;
|
||||
}
|
||||
|
||||
|
||||
conf_dev_t* openBaseDeviceConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
conf_dev_t* ret = malloc(sizeof(conf_dev_t));
|
||||
if (ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device conf struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->id = NULL;
|
||||
ret->clockDivider = 1;
|
||||
ret->clockMultipler = 1;
|
||||
ret->clockId = NULL;
|
||||
|
||||
|
||||
ret->memConf = malloc(sizeof(conf_mem_t));
|
||||
if (ret->memConf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device memory conf struct");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char intErrbuf[1024];
|
||||
|
||||
|
||||
|
||||
FILE *fp = fopen (configPath, "rb");
|
||||
if (!fp) {
|
||||
sprintf(errbuf, "unable to open config file %s", configPath);
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
|
||||
fclose(fp);
|
||||
|
||||
if (!conf) {
|
||||
sprintf(errbuf, "cannot parse - %s", intErrbuf);
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* devBlock = toml_table_in(conf, "dev");
|
||||
if (!devBlock) {
|
||||
sprintf(errbuf, "missing [dev]");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* memTable = toml_table_in(conf, "mem");
|
||||
if (!memTable) {
|
||||
sprintf(errbuf, "missing [mem]");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t libpathBlock = toml_string_in(devBlock, "libpath");
|
||||
if (!libpathBlock.ok) {
|
||||
sprintf(errbuf, "unable to read dev.libpath");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf_mem_seg_t** memSegConfs = parseMemToml(memTable);
|
||||
if (memSegConfs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to parse mem segments");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->memConf->memSegConfs = memSegConfs;
|
||||
ret->libPath = libpathBlock.u.s;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2,488 +2,85 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tomlc99/toml.h"
|
||||
|
||||
#include "base_device.h"
|
||||
#include "compose_device.h"
|
||||
|
||||
|
||||
// void fillDevClockConfig(const toml_table_t* clockTable, conf_dev_t** devConfs)
|
||||
// char** appendId(char** prev, const char* cur, char* errbuf)
|
||||
// {
|
||||
// uint16_t clockSegIdx = 0;
|
||||
// uint16_t clockSegSkips = 0;
|
||||
// const char* clockSegKey = toml_key_in(clockTable, 0);
|
||||
// while (clockSegKey && (clockSegIdx + clockSegSkips) < 0xFFFF)
|
||||
// if(prev == NULL)
|
||||
// {
|
||||
// clockSegKey = toml_key_in(clockTable, clockSegIdx + clockSegSkips + 1);
|
||||
// if(strcmp(clockSegKey, "limiter") != 0)
|
||||
// prev = malloc(sizeof(char*) * 2);
|
||||
// if(prev == NULL)
|
||||
// {
|
||||
// clockSegIdx++;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// clockSegSkips++;
|
||||
// }
|
||||
// snprintf(errbuf, 1024, "unable to allocate id");
|
||||
// return NULL;
|
||||
// }
|
||||
// prev[0] = NULL;
|
||||
// prev[1] = NULL;
|
||||
// }
|
||||
|
||||
char** appendId(char** prev, const char* cur, char* errbuf)
|
||||
{
|
||||
if(prev == NULL)
|
||||
{
|
||||
prev = malloc(sizeof(char*) * 2);
|
||||
if(prev == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate id");
|
||||
return NULL;
|
||||
}
|
||||
prev[0] = NULL;
|
||||
prev[1] = NULL;
|
||||
}
|
||||
// size_t clockIdLen = 0;
|
||||
// while (prev[clockIdLen] != NULL){clockIdLen++;}
|
||||
|
||||
size_t clockIdLen = 0;
|
||||
while (prev[clockIdLen] != NULL){clockIdLen++;}
|
||||
// clockIdLen++;
|
||||
|
||||
clockIdLen++;
|
||||
// char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1));
|
||||
|
||||
char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1));
|
||||
// if(new == NULL)
|
||||
// {
|
||||
// snprintf(errbuf, 1024, "unable to reallocate id");
|
||||
// freeComposeId(prev);
|
||||
// return NULL;
|
||||
// }
|
||||
// prev = new;
|
||||
|
||||
if(new == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to reallocate id");
|
||||
freeComposeId(prev);
|
||||
return NULL;
|
||||
}
|
||||
prev = new;
|
||||
// prev[clockIdLen] = NULL;
|
||||
// size_t idLen = strlen(cur);
|
||||
// prev[clockIdLen - 1] = malloc(sizeof(char) * (idLen + 1));
|
||||
// if(prev[clockIdLen - 1] == NULL)
|
||||
// {
|
||||
// snprintf(errbuf, 1024, "unable to allocate new id entry");
|
||||
// freeComposeId(prev);
|
||||
// return NULL;
|
||||
// }
|
||||
// strcpy(prev[clockIdLen - 1], cur);
|
||||
// prev[clockIdLen - 1][idLen] = '\0';
|
||||
// return prev;
|
||||
// }
|
||||
|
||||
prev[clockIdLen] = NULL;
|
||||
size_t idLen = strlen(cur);
|
||||
prev[clockIdLen - 1] = malloc(sizeof(char) * (idLen + 1));
|
||||
if(prev[clockIdLen - 1] == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate new id entry");
|
||||
freeComposeId(prev);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(prev[clockIdLen - 1], cur);
|
||||
prev[clockIdLen - 1][idLen] = '\0';
|
||||
return prev;
|
||||
}
|
||||
// static void freeProjectionConfig(projection_conf_t* conf)
|
||||
// {
|
||||
// if(conf == NULL)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// freeComposeId(conf->target);
|
||||
// freeComposeId(conf->baseAt);
|
||||
// if(conf->baseSeg != NULL)
|
||||
// {
|
||||
// free(conf->baseSeg);
|
||||
// }
|
||||
// conf->target = NULL;
|
||||
// conf->baseAt = NULL;
|
||||
// conf->baseSeg = NULL;
|
||||
// free(conf);
|
||||
// }
|
||||
|
||||
conf_dev_t** parseDevToml(const toml_table_t* devTable, const toml_table_t* clockTable, char* errbuf)
|
||||
{
|
||||
uint16_t devSegIdx = 0;
|
||||
const char* devSegKey = toml_key_in(devTable, 0);
|
||||
while (devSegKey && devSegIdx < 0xFFFF)
|
||||
{
|
||||
devSegIdx++;
|
||||
devSegKey = toml_key_in(devTable, devSegIdx);
|
||||
}
|
||||
|
||||
conf_dev_t** deviceConfigs = malloc(sizeof(conf_dev_t*) * (devSegIdx + 1));
|
||||
if(deviceConfigs == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
deviceConfigs[devSegIdx] = NULL;
|
||||
|
||||
for(size_t i = 0; i < devSegIdx; i++)
|
||||
{
|
||||
devSegKey = toml_key_in(devTable, (int)i);
|
||||
if(devSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device key %lu", i);
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
toml_datum_t devpathDatum = toml_string_in(devTable, devSegKey);
|
||||
if(!devpathDatum.ok || !devpathDatum.u.s)
|
||||
{
|
||||
sprintf(errbuf, "unable to get device path for %s", devSegKey);
|
||||
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
char intErrbuf[1024];
|
||||
|
||||
conf_dev_t* conf = openBaseDeviceConfig(devpathDatum.u.s, intErrbuf);
|
||||
if(conf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load config for %s: %s", devSegKey, intErrbuf);
|
||||
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->id = NULL;
|
||||
conf->id = appendId(conf->id, devSegKey, intErrbuf);
|
||||
|
||||
|
||||
if(conf->id == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device %s name field", devSegKey);
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
deviceConfigs[i] = conf;
|
||||
}
|
||||
|
||||
if(clockTable != NULL)
|
||||
{
|
||||
for(size_t i = 0; i < devSegIdx; i++)
|
||||
{
|
||||
conf_dev_t* conf = deviceConfigs[i];
|
||||
toml_table_t* devClockTable = toml_table_in(clockTable, conf->id[0]);
|
||||
if(devClockTable == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
toml_datum_t dividerDatum = toml_int_in(devClockTable, "divider");
|
||||
if(dividerDatum.ok)
|
||||
{
|
||||
conf->clockDivider = (uint64_t)dividerDatum.u.i;
|
||||
}
|
||||
toml_datum_t multiplerDatum = toml_int_in(devClockTable, "multipler");
|
||||
if(multiplerDatum.ok)
|
||||
{
|
||||
conf->clockMultipler = (uint64_t)multiplerDatum.u.i;
|
||||
}
|
||||
|
||||
toml_datum_t srcDatum = toml_string_in(devClockTable, "src");
|
||||
if(srcDatum.ok && srcDatum.u.s != NULL)
|
||||
{
|
||||
char intErrbuf[1024] = {0};
|
||||
conf->clockId = appendId(conf->clockId, srcDatum.u.s, intErrbuf);
|
||||
if(conf->clockId == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append clock id for %s: %s", conf->id[0], intErrbuf);
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return deviceConfigs;
|
||||
}
|
||||
void freeProjectionConfig(projection_conf_t* conf)
|
||||
{
|
||||
if(conf == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
freeComposeId(conf->target);
|
||||
freeComposeId(conf->baseAt);
|
||||
if(conf->baseSeg != NULL)
|
||||
{
|
||||
free(conf->baseSeg);
|
||||
}
|
||||
conf->target = NULL;
|
||||
conf->baseAt = NULL;
|
||||
conf->baseSeg = NULL;
|
||||
free(conf);
|
||||
}
|
||||
void freeProjectionConfigs(projection_conf_t** confs)
|
||||
{
|
||||
if(confs == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
size_t i = 0;
|
||||
while(confs[i] != NULL)
|
||||
{
|
||||
freeProjectionConfig(confs[i]);
|
||||
confs[i] = NULL;
|
||||
}
|
||||
free(confs);
|
||||
}
|
||||
|
||||
|
||||
projection_conf_t* parseDeviceProjectionConf(const toml_table_t* deviceProjectionTable, char* errbuf)
|
||||
{
|
||||
toml_datum_t baseAtDatum = toml_string_in(deviceProjectionTable, "base_at");
|
||||
if(!baseAtDatum.ok || baseAtDatum.u.s == NULL)
|
||||
{
|
||||
sprintf(errbuf, "missing 'base_at'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t baseSegDatum = toml_string_in(deviceProjectionTable, "base_seg");
|
||||
if(!baseSegDatum.ok || baseSegDatum.u.s == NULL)
|
||||
{
|
||||
sprintf(errbuf, "missing 'base_seg'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t projectionShiftDatum = toml_int_in(deviceProjectionTable, "projection_shift");
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
projection_conf_t* ret = malloc(sizeof(projection_conf_t));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate projection conf");
|
||||
return NULL;
|
||||
}
|
||||
ret->baseAt = NULL;
|
||||
ret->baseSeg = NULL;
|
||||
ret->target = NULL;
|
||||
ret->projectionShift = 0;
|
||||
|
||||
if(projectionShiftDatum.ok)
|
||||
{
|
||||
ret->projectionShift = (uint64_t)projectionShiftDatum.u.i;
|
||||
}
|
||||
|
||||
ret->baseAt = appendId(ret->baseAt, baseAtDatum.u.s, intErrbuf);
|
||||
if(ret->baseAt == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append base_at id");
|
||||
freeProjectionConfig(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->baseSeg = malloc(strlen(baseSegDatum.u.s));
|
||||
if(ret->baseSeg == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to fill base_seg");
|
||||
freeProjectionConfig(ret);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(ret->baseSeg, baseSegDatum.u.s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
projection_conf_t** parseDeviceProjectionConfs(const toml_table_t* deviceProjectionsTable, char* errbuf)
|
||||
{
|
||||
uint16_t projSegIdx = 0;
|
||||
const char* projSegKey = toml_key_in(deviceProjectionsTable, 0);
|
||||
while (projSegKey && projSegIdx < 0xFFFF)
|
||||
{
|
||||
projSegIdx++;
|
||||
projSegKey = toml_key_in(deviceProjectionsTable, projSegIdx);
|
||||
}
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
projection_conf_t** ret = malloc(sizeof(projection_conf_t*) * (projSegIdx + 1));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device projections confs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i <= projSegIdx; i++){ret[i] = NULL;}
|
||||
|
||||
for(size_t i = 0; i < projSegIdx; i++)
|
||||
{
|
||||
projSegKey = toml_key_in(deviceProjectionsTable, (int)i);
|
||||
if(projSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device projection key %lu", i);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* projTable = toml_table_in(deviceProjectionsTable, projSegKey);
|
||||
if(projTable == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to open device %s projection table", projSegKey);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret[i] = parseDeviceProjectionConf(projTable, intErrbuf);
|
||||
if(ret[i] == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device %s projection configs: %s", projSegKey, intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret[i]->target = appendId(ret[i]->target, projSegKey, intErrbuf);
|
||||
if(ret[i]->target == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append projection target id: %s", intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
projection_conf_t** parseProjectionConfs(const toml_table_t* projectionTable, char* errbuf)
|
||||
{
|
||||
uint16_t devicesProjSegIdx = 0;
|
||||
const char* devicesProjSegKey = toml_key_in(projectionTable, 0);
|
||||
while (devicesProjSegKey && devicesProjSegIdx < 0xFFFF)
|
||||
{
|
||||
devicesProjSegIdx++;
|
||||
devicesProjSegKey = toml_key_in(projectionTable, devicesProjSegIdx);
|
||||
}
|
||||
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
size_t totalRetSize = 0;
|
||||
projection_conf_t** ret = malloc(sizeof(projection_conf_t*));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate base projection conf array");
|
||||
return NULL;
|
||||
}
|
||||
ret[0] = NULL;
|
||||
|
||||
for(size_t i = 0; i < devicesProjSegIdx; i++)
|
||||
{
|
||||
devicesProjSegKey = toml_key_in(projectionTable, (int)i);
|
||||
if(devicesProjSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load proj key %lu", i);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* deviceProjTable = toml_table_in(projectionTable, devicesProjSegKey);
|
||||
if(deviceProjTable == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to open device projection table");
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
projection_conf_t** devProjections = parseDeviceProjectionConfs(deviceProjTable, intErrbuf);
|
||||
|
||||
if(devProjections == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to parse device %s projection rules: %s", devicesProjSegKey, intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
size_t dlen = 0;
|
||||
while(devProjections[dlen] != NULL)
|
||||
{
|
||||
devProjections[dlen]->target = appendId(devProjections[dlen]->target, devicesProjSegKey, intErrbuf);
|
||||
if(devProjections[dlen]->target == NULL)
|
||||
{
|
||||
freeProjectionConfigs(ret);
|
||||
freeProjectionConfigs(devProjections);
|
||||
return NULL;
|
||||
}
|
||||
dlen++;
|
||||
}
|
||||
ret = realloc(ret, sizeof(projection_conf_t*) * (dlen + totalRetSize + 1));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to reallocate full projection array");
|
||||
freeProjectionConfigs(devProjections);
|
||||
return NULL;
|
||||
}
|
||||
for(size_t i = 0; i <= dlen; i++)
|
||||
{
|
||||
ret[totalRetSize + i] = devProjections[i];
|
||||
}
|
||||
totalRetSize += dlen;
|
||||
ret[totalRetSize] = NULL;
|
||||
free(devProjections);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
compose_dev_conf_t* ret = malloc(sizeof(compose_dev_conf_t));
|
||||
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
FILE *fp = fopen (configPath, "rb");
|
||||
if (!fp) {
|
||||
sprintf(errbuf, "unable to open config file %s", configPath);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
|
||||
fclose(fp);
|
||||
|
||||
|
||||
toml_table_t* devTable = toml_table_in(conf, "dev");
|
||||
if (!devTable) {
|
||||
sprintf(errbuf, "missing [dev]");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->projections = NULL;
|
||||
|
||||
toml_table_t* memTable = toml_table_in(conf, "mem");
|
||||
if(memTable)
|
||||
{
|
||||
toml_table_t* projectionTable = toml_table_in(memTable, "projection");
|
||||
if(projectionTable)
|
||||
{
|
||||
ret->projections = parseProjectionConfs(projectionTable, intErrbuf);
|
||||
if(ret->projections == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load projections: %s", intErrbuf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toml_table_t* clockTable = toml_table_in(conf, "clock");
|
||||
|
||||
|
||||
conf_dev_t** devHandlers = parseDevToml(devTable, clockTable, intErrbuf);
|
||||
|
||||
if(devHandlers == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load devices configs: %s", intErrbuf);
|
||||
freeProjectionConfigs(ret->projections);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->baseConfigs = devHandlers;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
// void freeProjectionConfigs(projection_conf_t** confs)
|
||||
// {
|
||||
// if(confs == NULL)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// size_t i = 0;
|
||||
// while(confs[i] != NULL)
|
||||
// {
|
||||
// freeProjectionConfig(confs[i]);
|
||||
// confs[i] = NULL;
|
||||
// }
|
||||
// free(confs);
|
||||
// }
|
||||
|
||||
|
||||
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
@@ -495,7 +92,7 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
|
||||
if(devHandlers == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate dev handlers");
|
||||
snprintf(errbuf, 1024, "unable to allocate dev handlers");
|
||||
return NULL;
|
||||
}
|
||||
devHandlers[devIdx] = NULL;
|
||||
@@ -504,11 +101,11 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
for(size_t i = 0; i < devIdx; i++)
|
||||
{
|
||||
conf_dev_t* devConf = conf->baseConfigs[i];
|
||||
char intErrbuf[1024] = {0};
|
||||
char intErrbuf[255] = {0};
|
||||
device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf);
|
||||
if(devHandle == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to open base device %s: %s", devConf->id[0], intErrbuf);
|
||||
snprintf(errbuf, 1024, "unable to open base device %s: %s", devConf->id[0], intErrbuf);
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
closeBaseDevice(devHandlers[j]);
|
||||
@@ -523,7 +120,3 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
|
||||
return devHandlers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
32
src/config.c
32
src/config.c
@@ -4,7 +4,7 @@
|
||||
#include "config.h"
|
||||
|
||||
|
||||
void xfree(void* p)
|
||||
static void xfree(void* p)
|
||||
{
|
||||
if(p != NULL)
|
||||
{
|
||||
@@ -28,21 +28,21 @@ void freeComposeId(char** id)
|
||||
}
|
||||
|
||||
|
||||
uint8_t compareComposeId(char** idA, char** idB)
|
||||
{
|
||||
size_t i = 0;
|
||||
while(idA[i] != NULL && idB[i] != NULL)
|
||||
{
|
||||
if(strcmp(idA[i], idB[i]) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return idA[i] == NULL && idB[i] == NULL;
|
||||
}
|
||||
// static uint8_t compareComposeId(char** idA, char** idB)
|
||||
// {
|
||||
// size_t i = 0;
|
||||
// while(idA[i] != NULL && idB[i] != NULL)
|
||||
// {
|
||||
// if(strcmp(idA[i], idB[i]) != 0)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
// i++;
|
||||
// }
|
||||
// return idA[i] == NULL && idB[i] == NULL;
|
||||
// }
|
||||
|
||||
void freeMemSegConf(conf_mem_seg_t* memSegConf)
|
||||
static void freeMemSegConf(conf_mem_seg_t* memSegConf)
|
||||
{
|
||||
if(memSegConf == NULL)
|
||||
{
|
||||
@@ -50,7 +50,7 @@ void freeMemSegConf(conf_mem_seg_t* memSegConf)
|
||||
}
|
||||
xfree(memSegConf->name);
|
||||
}
|
||||
void freeMemConf(conf_mem_t* memConf)
|
||||
static void freeMemConf(conf_mem_t* memConf)
|
||||
{
|
||||
if(memConf == NULL)
|
||||
{
|
||||
|
||||
66
src/hmmmm.c
66
src/hmmmm.c
@@ -10,6 +10,7 @@ typedef size_t (*_dlib_pubExtractOpcode_t)(device_mem_t* devMem, size_t _program
|
||||
typedef uint8_t(*_dlib_pubExtractPcounterSizeWords_t)();
|
||||
|
||||
typedef device_public_context_t* (*_dlib_dev_init_t)(void* specs, char* errbuf);
|
||||
typedef void (*_dlib_dev_reset_t)(void* specs, device_public_context_t* devInfo);
|
||||
|
||||
typedef uint8_t (*_dlib_makeDeviceTick_t)(device_public_context_t* devInfo);
|
||||
|
||||
@@ -25,7 +26,7 @@ typedef void (*_dlib_freeDevMem_t)(device_mem_t* mem);
|
||||
typedef void (*_dlib_fillSmartReadSpecs_t)(void* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount);
|
||||
typedef void (*_dlib_fillSmartWriteSpecs_t)(void* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount);
|
||||
|
||||
instruction_simul_handlers_t* _fillInstructionSimul(void* handle)
|
||||
static instruction_simul_handlers_t* _fillInstructionSimul(void* handle)
|
||||
{
|
||||
instruction_simul_handlers_t* ret = malloc(sizeof(instruction_simul_handlers_t));
|
||||
|
||||
@@ -92,14 +93,16 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
|
||||
if (dev == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device lib struct");
|
||||
snprintf(errbuf, 1024, "unable to allocate device lib struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *handle = dlopen(libpath, RTLD_NOW);
|
||||
|
||||
if (!handle) {
|
||||
sprintf(errbuf, "unable to open dl handle");
|
||||
if (!handle)
|
||||
{
|
||||
const char *dlerr = dlerror();
|
||||
snprintf(errbuf, 1024, "unable to open dl handle: %s", dlerr);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
@@ -109,8 +112,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_dev_init_t _dlib_dev_init = (_dlib_dev_init_t)(uintptr_t)dlsym(handle, "init");
|
||||
|
||||
const char *dlsym_init_error = dlerror();
|
||||
if (dlsym_init_error) {
|
||||
sprintf(errbuf, "unable to find init symbol: %s", dlsym_init_error);
|
||||
if (dlsym_init_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find init symbol: %s", dlsym_init_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -120,8 +124,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
|
||||
|
||||
const char *dlsym_maketick_error = dlerror();
|
||||
if (dlsym_maketick_error) {
|
||||
sprintf(errbuf, "unable to find makeDeviceTick symbol: %s", dlsym_maketick_error);
|
||||
if (dlsym_maketick_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find makeDeviceTick symbol: %s", dlsym_maketick_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -132,8 +137,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_parseSpecsFromConfig_t _dlib_parseSpecsFromConfig = (_dlib_parseSpecsFromConfig_t)(uintptr_t)dlsym(handle, "parseSpecsFromConfig");
|
||||
|
||||
const char *_dlib_parseSpecsFromConfig_error = dlerror();
|
||||
if (_dlib_parseSpecsFromConfig_error) {
|
||||
sprintf(errbuf, "unable to find parseSpecsFromConfig symbol: %s", _dlib_parseSpecsFromConfig_error);
|
||||
if (_dlib_parseSpecsFromConfig_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find parseSpecsFromConfig symbol: %s", _dlib_parseSpecsFromConfig_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -144,8 +150,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_fillSmartReadSpecs_t _dlib_fillSmartReadSpecs = (_dlib_fillSmartReadSpecs_t)(uintptr_t)dlsym(handle, "fillSmartReadSpecs");
|
||||
|
||||
const char *_dlib_fillSmartReadSpecs_error = dlerror();
|
||||
if (_dlib_fillSmartReadSpecs_error) {
|
||||
sprintf(errbuf, "unable to find fillSmartReadSpecs symbol: %s", _dlib_fillSmartReadSpecs_error);
|
||||
if (_dlib_fillSmartReadSpecs_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find fillSmartReadSpecs symbol: %s", _dlib_fillSmartReadSpecs_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -156,8 +163,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_fillSmartWriteSpecs_t _dlib_fillSmartWriteSpecs = (_dlib_fillSmartWriteSpecs_t)(uintptr_t)dlsym(handle, "fillSmartWriteSpecs");
|
||||
|
||||
const char *_dlib_fillSmartWriteSpecs_error = dlerror();
|
||||
if (_dlib_fillSmartWriteSpecs_error) {
|
||||
sprintf(errbuf, "unable to find fillSmartWriteSpecs symbol: %s", _dlib_fillSmartWriteSpecs_error);
|
||||
if (_dlib_fillSmartWriteSpecs_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find fillSmartWriteSpecs symbol: %s", _dlib_fillSmartWriteSpecs_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -167,8 +175,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_deviceType_t _dlib_deviceType = (_dlib_deviceType_t)(uintptr_t)dlsym(handle, "pubDeviceType");
|
||||
|
||||
const char *dlib_deviceType_error = dlerror();
|
||||
if (dlib_deviceType_error) {
|
||||
sprintf(errbuf, "unable to find pubDeviceType symbol: %s", dlib_deviceType_error);
|
||||
if (dlib_deviceType_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find pubDeviceType symbol: %s", dlib_deviceType_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -180,19 +189,33 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
_dlib_freeSpecs_t _dlib_freeSpecs = (_dlib_freeSpecs_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
|
||||
|
||||
const char *dlib_freeSpecs_error = dlerror();
|
||||
if (dlib_freeSpecs_error) {
|
||||
sprintf(errbuf, "unable to find freeSpecs symbol: %s", dlib_freeSpecs_error);
|
||||
if (dlib_freeSpecs_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find freeSpecs symbol: %s", dlib_freeSpecs_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
|
||||
_dlib_dev_reset_t _dlib_reset = (_dlib_dev_reset_t)(uintptr_t)dlsym(handle, "reset");
|
||||
|
||||
const char *dlib_dev_reset_t_error = dlerror();
|
||||
if (dlib_dev_reset_t_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find reset symbol: %s", dlib_dev_reset_t_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevMem");
|
||||
|
||||
const char *dlib_freeDevMem_error = dlerror();
|
||||
if (dlib_freeDevMem_error) {
|
||||
sprintf(errbuf, "unable to find freeDevMem symbol: %s", dlib_freeDevMem_error);
|
||||
if (dlib_freeDevMem_error)
|
||||
{
|
||||
snprintf(errbuf, 1024, "unable to find freeDevMem symbol: %s", dlib_freeDevMem_error);
|
||||
dlclose(handle);
|
||||
free(dev);
|
||||
return NULL;
|
||||
@@ -206,6 +229,7 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
dev->fillSmartWriteSpecs = _dlib_fillSmartWriteSpecs;
|
||||
dev->freeSpecs = _dlib_freeSpecs;
|
||||
dev->freeDevMem = _dlib_freeDevMem;
|
||||
dev->reset = _dlib_reset;
|
||||
|
||||
uint8_t devType = _dlib_deviceType();
|
||||
|
||||
|
||||
935
src/main.c
935
src/main.c
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,20 @@
|
||||
#include "proto/dial.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "panic.h"
|
||||
#include "proto/msg.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
|
||||
static uint64_t dial_now_us(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
|
||||
}
|
||||
|
||||
|
||||
void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
|
||||
@@ -17,7 +27,7 @@ void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
|
||||
memcpy(newMsg, msg, msgLen);
|
||||
|
||||
ClientContext* ctx = clientsHead->payload;
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, newMsg, msgLen);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, newMsg, msgLen);
|
||||
clientsHead = clientsHead->nextEntry;
|
||||
}
|
||||
free(msg);
|
||||
@@ -27,20 +37,25 @@ void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
|
||||
|
||||
|
||||
|
||||
void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen)
|
||||
void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen)
|
||||
{
|
||||
SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
|
||||
if(p->size + 1 >= p->allocatedSize)
|
||||
{
|
||||
printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
|
||||
OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2);
|
||||
NULL_GUARD(newPtr);
|
||||
p->ptr = newPtr;
|
||||
p->allocatedSize = p->allocatedSize * 2;
|
||||
}
|
||||
OutgoingMessage* outmsg = &((OutgoingMessage*)p->ptr)[p->size];
|
||||
// OutgoingBuffers* outBufs = emulContext->outBufs;
|
||||
// SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
|
||||
// if(p->size + 1 >= p->allocatedSize)
|
||||
// {
|
||||
// // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
|
||||
// OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 5);
|
||||
// NULL_GUARD(newPtr);
|
||||
// p->ptr = newPtr;
|
||||
// p->allocatedSize = p->allocatedSize * 5;
|
||||
// }
|
||||
OutgoingMessage* outmsg = malloc(sizeof(OutgoingMessage));
|
||||
// OutgoingMessage* outmsg = &((OutgoingMessage*)p->ptr)[p->size];
|
||||
NULL_GUARD(outmsg);
|
||||
outmsg->msg = msg;
|
||||
outmsg->msgLen = msgLen;
|
||||
outmsg->clientIdx = clientIdx;
|
||||
p->size++;
|
||||
outmsg->dispatch_us = dial_now_us();
|
||||
queue_spsc_push(outMsgQueue, outmsg);
|
||||
// p->size++;
|
||||
}
|
||||
@@ -1,21 +1,29 @@
|
||||
#include "proto/handlers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "proto/enums.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "proto/enums.h"
|
||||
#include "proto/handlers/auth.h"
|
||||
#include "proto/handlers/stream.h"
|
||||
#include "proto/handlers/control.h"
|
||||
#include "proto/handlers/mem.h"
|
||||
#include "proto/msg.h"
|
||||
|
||||
#include "proto_reader.h"
|
||||
#include "proto_verifier.h"
|
||||
#include "control_reader.h"
|
||||
#include "ptQueue/inc/mpsc.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
#include "stream_reader.h"
|
||||
#include "mem_reader.h"
|
||||
|
||||
|
||||
void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
|
||||
{
|
||||
if(ctx->streamRegIterator > 0)
|
||||
static void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
|
||||
{
|
||||
// if (ctx->streamRegIterator > 0) {
|
||||
unregisterClientStreams(emulContext, ctx);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@@ -24,24 +32,21 @@ void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
|
||||
if (ev->regType == REG_EVTYPE_CONNECT)
|
||||
{
|
||||
printf("open client %lu\n", ev->ctx->clientId);
|
||||
LinkedListEntry* newClientsLinkedListHead = malloc(sizeof(LinkedListEntry));
|
||||
NULL_GUARD(newClientsLinkedListHead);
|
||||
LinkedListEntry* entry = malloc(sizeof(LinkedListEntry));
|
||||
NULL_GUARD(entry);
|
||||
|
||||
newClientsLinkedListHead->payload = ev->ctx;
|
||||
if(*emulContext->clientsHead != NULL)
|
||||
{
|
||||
(*emulContext->clientsHead)->prevEntry = newClientsLinkedListHead;
|
||||
entry->payload = ev->ctx;
|
||||
if (*emulContext->clientsHead != NULL) {
|
||||
(*emulContext->clientsHead)->prevEntry = entry;
|
||||
}
|
||||
newClientsLinkedListHead->prevEntry = NULL;
|
||||
newClientsLinkedListHead->nextEntry = *emulContext->clientsHead;
|
||||
*emulContext->clientsHead = newClientsLinkedListHead;
|
||||
entry->prevEntry = NULL;
|
||||
entry->nextEntry = *emulContext->clientsHead;
|
||||
*emulContext->clientsHead = entry;
|
||||
}
|
||||
else if (ev->regType == REG_EVTYPE_CLOSE)
|
||||
{
|
||||
LinkedListEntry* clientEntry = *emulContext->clientsHead;
|
||||
|
||||
while(clientEntry != NULL)
|
||||
{
|
||||
while (clientEntry != NULL) {
|
||||
if (clientEntry->payload == ev->ctx)
|
||||
{
|
||||
printf("close client %lu\n", ev->ctx->clientId);
|
||||
@@ -61,33 +66,47 @@ void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
|
||||
}
|
||||
|
||||
|
||||
static void handleIncomingMessage(
|
||||
hmmmm_ClientMessage_table_t cm, ClientContext* ctx, EmulContext* emulContext)
|
||||
{
|
||||
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));
|
||||
|
||||
void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
|
||||
switch (ptype)
|
||||
{
|
||||
switch (msg->packetType)
|
||||
case hmmmm_ClientPayload_CtrlClientMessage:
|
||||
{
|
||||
case PACKET_TYPE_CTRL:
|
||||
{
|
||||
printf("CTRL packet\n");
|
||||
handleIncomingControlMessage(msg, emulContext);
|
||||
hmmmm_ctrl_CtrlClientMessage_table_t ctrl =
|
||||
(hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
|
||||
handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_STREAM:
|
||||
case hmmmm_ClientPayload_StreamClientMessage:
|
||||
{
|
||||
printf("STREAM packet\n");
|
||||
handleIncomingStreamMessage(msg, ctx, emulContext);
|
||||
hmmmm_stream_StreamClientMessage_table_t stream =
|
||||
(hmmmm_stream_StreamClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
|
||||
handleIncomingStreamMessage(stream, nonce, ctx, emulContext);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_MEM:
|
||||
case hmmmm_ClientPayload_MemClientMessage:
|
||||
{
|
||||
printf("MEM packet\n");
|
||||
handleIncomingMemMessage(msg, ctx, emulContext);
|
||||
hmmmm_mem_MemClientMessage_table_t mem =
|
||||
(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;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Unsupported packet type: %u\n", msg->packetType);
|
||||
printf("client %lu: unknown payload type %u\n", ctx->clientId, ptype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -96,47 +115,77 @@ void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* em
|
||||
|
||||
void handleAllClients(EmulContext* emulContext)
|
||||
{
|
||||
LinkedListEntry* clientEntry = *emulContext->clientsHead;
|
||||
|
||||
size_t handleLimit = 128;
|
||||
queue_mpsc_t* inMsgQueue = emulContext->inMsgQueue;
|
||||
|
||||
while(clientEntry != NULL && handleLimit > 0)
|
||||
FbMessage* fbmsg = NULL;
|
||||
|
||||
for(size_t handleLimit = 0; handleLimit < 32; handleLimit++)
|
||||
{
|
||||
handleLimit--;
|
||||
ClientContext* ctx = clientEntry->payload;
|
||||
if(!ctx->isAuthed)
|
||||
fbmsg = NULL;
|
||||
if(queue_mpsc_pop(inMsgQueue, (void**)&fbmsg))
|
||||
{
|
||||
clientEntry = disconnectDueTimeout(emulContext, clientEntry);
|
||||
if(clientEntry == NULL)
|
||||
if(fbmsg != NULL)
|
||||
{
|
||||
*emulContext->utilizedFlag = 1;
|
||||
|
||||
// if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
|
||||
hmmmm_ClientMessage_table_t cm =
|
||||
hmmmm_ClientMessage_as_root(fbmsg->data);
|
||||
// printf("read message len %lu\n", fbmsg->size);
|
||||
handleIncomingMessage(cm, fbmsg->ctx, emulContext);
|
||||
// } else {
|
||||
// printf("client %lu: dropped malformed FlatBuffer\n", fbmsg->ctx->clientId);
|
||||
// }
|
||||
|
||||
free(fbmsg->data);
|
||||
free(fbmsg);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(*emulContext->utilizedFlag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void* payload = ctx->incomeQ->head->payload;
|
||||
if(payload != NULL)
|
||||
{
|
||||
*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);
|
||||
|
||||
|
||||
free(payload);
|
||||
ctx->incomeQ->head = ctx->incomeQ->head->nextEl;
|
||||
}
|
||||
clientEntry = clientEntry->nextEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// LinkedListEntry* clientEntry = *emulContext->clientsHead;
|
||||
// size_t handleLimit = 128;
|
||||
|
||||
// while (clientEntry != NULL && handleLimit > 0) {
|
||||
// handleLimit--;
|
||||
// ClientContext* ctx = clientEntry->payload;
|
||||
|
||||
// if (!ctx->isAuthed) {
|
||||
// clientEntry = disconnectDueTimeout(emulContext, clientEntry);
|
||||
// if (clientEntry == NULL) break;
|
||||
// if (*emulContext->utilizedFlag) continue;
|
||||
// } else {
|
||||
// FbMessage* fbmsg = NULL;
|
||||
// queue_spsc_pop(ctx->incomeQ, (void**)&fbmsg);
|
||||
// // FbMessage* fbmsg = ctx->incomeQ->head->payload;
|
||||
// if (fbmsg != NULL) {
|
||||
// *emulContext->utilizedFlag = 1;
|
||||
|
||||
// // if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
|
||||
// printf("read message len %lu\n", fbmsg->size);
|
||||
// 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(fbmsg);
|
||||
// // ctx->incomeQ->head = ctx->incomeQ->head->nextEl;
|
||||
// }
|
||||
// clientEntry = clientEntry->nextEntry;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,140 +1,159 @@
|
||||
#include "proto/handlers/auth.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "my_mutex.h"
|
||||
|
||||
#include "proto/enums.h"
|
||||
#include "events.h"
|
||||
#include "panic.h"
|
||||
|
||||
|
||||
#include "proto/pack.h"
|
||||
#include "proto/enums.h"
|
||||
#include "proto/msg.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"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
|
||||
|
||||
static uint8_t validateAccessTokenDeterministic(
|
||||
const uint8_t* data, const uint8_t* token, uint64_t timestamp)
|
||||
{
|
||||
char buf[1024];
|
||||
sprintf(buf, "%s%lu", token, timestamp);
|
||||
(void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp);
|
||||
|
||||
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;
|
||||
|
||||
for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++)
|
||||
{
|
||||
if (data[i] != hash[i])
|
||||
{
|
||||
for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
|
||||
if (data[i] != hash[i]) {
|
||||
valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
uint8_t valid1 = validateAccessTokenDeterministic(data, access_token, t);
|
||||
uint8_t valid2 = validateAccessTokenDeterministic(data, access_token, t - 1);
|
||||
|
||||
return valid1 || valid2;
|
||||
return validateAccessTokenDeterministic(data, access_token, t)
|
||||
|| validateAccessTokenDeterministic(data, access_token, t - 1);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if(msgSize != SHA512_DIGEST_LENGTH)
|
||||
{
|
||||
int ret = ws_close_client(client);
|
||||
if(ret == -1)
|
||||
{
|
||||
printf("Unable to close client %lu\n", client);
|
||||
}
|
||||
|
||||
// Verify it's a valid ClientMessage FlatBuffer
|
||||
if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) {
|
||||
printf("Auth: invalid FlatBuffer from client %lu\n", client);
|
||||
ws_close_client(client);
|
||||
return 0;
|
||||
}
|
||||
uint8_t isValid = validateAccessToken(msg, (const uint8_t*)ctx->accessToken);
|
||||
if(!isValid)
|
||||
{
|
||||
printf("Auth token invalid\n");
|
||||
int ret = ws_close_client(client);
|
||||
if(ret == -1)
|
||||
{
|
||||
printf("Unable to close client %lu\n", client);
|
||||
}
|
||||
|
||||
hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg);
|
||||
|
||||
if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) {
|
||||
printf("Auth: expected AuthRequest, got type %u from client %lu\n",
|
||||
hmmmm_ClientMessage_payload_type(cm), client);
|
||||
ws_close_client(client);
|
||||
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];
|
||||
|
||||
printf("Auth token is valid\n");
|
||||
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
|
||||
if(ev == NULL)
|
||||
{
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
|
||||
if (ev == NULL) {
|
||||
panic("Unable to allocate register event");
|
||||
}
|
||||
ev->regType = REG_EVTYPE_AUTH;
|
||||
ev->ctx = cctx;
|
||||
|
||||
ws_set_connection_context(client, cctx);
|
||||
|
||||
|
||||
with_lock(&ctx->registerMutex)
|
||||
{
|
||||
printf("Writing auth event\n");
|
||||
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
if(exitCode)
|
||||
{
|
||||
panic("Unable to push to reg queue: %s\n", errbuf);
|
||||
}
|
||||
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
|
||||
queue_spsc_push(ctx->regQueue, ev);
|
||||
// if (!queue_spsc_push(ctx->regQueue, ev)) {
|
||||
// panic("Unable to push to reg queue: %s\n", errbuf);
|
||||
// }
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if(now - ctx->connectedAt <= 30)
|
||||
{
|
||||
if (now - cctx->connectedAt <= 30) {
|
||||
return clientEntry->nextEntry;
|
||||
}
|
||||
|
||||
printf("Timeout on connection %lu\n", ctx->clientId);
|
||||
int ret = ws_close_client(ctx->clientId);
|
||||
if(ret == -1)
|
||||
{
|
||||
printf("Timeout on connection %lu\n", cctx->clientId);
|
||||
int ret = ws_close_client(cctx->clientId);
|
||||
if (ret == -1) {
|
||||
printf("Unable to close client\n");
|
||||
}
|
||||
LinkedListEntry* nextEntry = clientEntry->nextEntry;
|
||||
|
||||
removeLinkedListEntry(emulContext->clientsHead, clientEntry);
|
||||
clientEntry = nextEntry;
|
||||
*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);
|
||||
NULL_GUARD(framedata);
|
||||
|
||||
encodeUintToBytes(ctx->clientId, framedata);
|
||||
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, framedata, 8);
|
||||
|
||||
|
||||
// Send AuthResponse with assigned seat_id
|
||||
size_t len = 0;
|
||||
uint8_t* msg = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, *emulContext->emulState, &len);
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, msg, len);
|
||||
uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, 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->outMsgQueue, cctx->clientId, stateMsg, len);
|
||||
|
||||
// Send cached DeviceIdMappingNotif if a config has been loaded
|
||||
if (emulContext->deviceIdMappingMsg && emulContext->deviceIdMappingMsgLen > 0) {
|
||||
uint8_t* mappingCopy = malloc(emulContext->deviceIdMappingMsgLen);
|
||||
if (mappingCopy) {
|
||||
memcpy(mappingCopy, emulContext->deviceIdMappingMsg, emulContext->deviceIdMappingMsgLen);
|
||||
dispatchOutgoingMessage(
|
||||
emulContext->outMsgQueue, cctx->clientId,
|
||||
mappingCopy, emulContext->deviceIdMappingMsgLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
758
src/proto/handlers/config.c
Normal file
758
src/proto/handlers/config.c
Normal file
@@ -0,0 +1,758 @@
|
||||
#include "proto/handlers/config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "state.h"
|
||||
#include "base_device.h"
|
||||
#include "proto/msg.h"
|
||||
#include "proto/dial.h"
|
||||
|
||||
#include "config_ctrl_reader.h"
|
||||
#include "config_reader.h"
|
||||
#include "device_reader.h"
|
||||
#include "mem_config_reader.h"
|
||||
|
||||
#define MAX_DEVICES 64
|
||||
#define MAX_DEPTH 16
|
||||
#define MAX_SEGMENTS 64
|
||||
|
||||
|
||||
// Converts a FlatBuffer BaseDeviceConfig into a heap-allocated conf_dev_t
|
||||
// suitable for openBaseDevice(). Caller must call freeConf() when done.
|
||||
static conf_dev_t* conf_from_fb(
|
||||
hmmmm_config_BaseDeviceConfig_table_t base,
|
||||
char* errbuf)
|
||||
{
|
||||
conf_dev_t* dc = calloc(1, sizeof(conf_dev_t));
|
||||
if (!dc) { snprintf(errbuf, 1024, "alloc conf_dev_t"); return NULL; }
|
||||
|
||||
dc->clockDivider = 1;
|
||||
dc->clockMultipler = 1;
|
||||
|
||||
flatbuffers_string_t lp = hmmmm_config_BaseDeviceConfig_libpath(base);
|
||||
if (!lp) { snprintf(errbuf, 1024, "missing libpath"); free(dc); return NULL; }
|
||||
dc->libPath = strdup(lp);
|
||||
|
||||
hmmmm_config_MemSegment_vec_t segs = hmmmm_config_BaseDeviceConfig_mem_segments(base);
|
||||
size_t seg_n = segs ? hmmmm_config_MemSegment_vec_len(segs) : 0;
|
||||
|
||||
dc->memConf = calloc(1, sizeof(conf_mem_t));
|
||||
if (!dc->memConf) { free(dc->libPath); free(dc); snprintf(errbuf, 1024, "alloc memConf"); return NULL; }
|
||||
|
||||
dc->memConf->memSegConfs = calloc(seg_n + 1, sizeof(conf_mem_seg_t*));
|
||||
if (!dc->memConf->memSegConfs) {
|
||||
free(dc->memConf); free(dc->libPath); free(dc);
|
||||
snprintf(errbuf, 1024, "alloc memSegConfs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < seg_n; i++) {
|
||||
hmmmm_config_MemSegment_table_t seg = hmmmm_config_MemSegment_vec_at(segs, i);
|
||||
conf_mem_seg_t* s = calloc(1, sizeof(conf_mem_seg_t));
|
||||
if (!s) { snprintf(errbuf, 1024, "alloc seg %zu", i); goto fail; }
|
||||
|
||||
flatbuffers_string_t sname = hmmmm_config_MemSegment_name(seg);
|
||||
s->name = strdup(sname ? sname : "");
|
||||
s->start = hmmmm_config_MemSegment_addr(seg);
|
||||
s->len = hmmmm_config_MemSegment_len(seg);
|
||||
s->wordLen = hmmmm_config_MemSegment_word_len(seg);
|
||||
s->isExecutable = hmmmm_config_MemSegment_executable(seg);
|
||||
dc->memConf->memSegConfs[i] = s;
|
||||
}
|
||||
dc->memConf->memSegConfs[seg_n] = NULL;
|
||||
return dc;
|
||||
|
||||
fail:
|
||||
freeConf(dc); // freeConf() already calls free(dc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
device_handle_t* handles[MAX_DEVICES];
|
||||
const char* path_stack[MAX_DEPTH]; // current path components
|
||||
size_t depth;
|
||||
// For building the DeviceIdMappingNotif response:
|
||||
char** paths[MAX_DEVICES]; // hierarchical path per device (heap-alloc'd)
|
||||
size_t path_lens[MAX_DEVICES]; // number of path components per device
|
||||
size_t seg_counts[MAX_DEVICES]; // number of memory segments per device
|
||||
char* seg_names[MAX_DEVICES][MAX_SEGMENTS]; // segment names (heap-alloc'd)
|
||||
size_t count;
|
||||
} LoadState;
|
||||
|
||||
|
||||
static int load_devices_recursive(
|
||||
hmmmm_config_ComposeDeviceConfig_table_t compose,
|
||||
LoadState* st,
|
||||
char* errbuf)
|
||||
{
|
||||
hmmmm_config_DeviceEntry_vec_t entries =
|
||||
hmmmm_config_ComposeDeviceConfig_devices(compose);
|
||||
size_t n = entries ? hmmmm_config_DeviceEntry_vec_len(entries) : 0;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
hmmmm_config_DeviceEntry_table_t entry =
|
||||
hmmmm_config_DeviceEntry_vec_at(entries, i);
|
||||
|
||||
flatbuffers_string_t id = hmmmm_config_DeviceEntry_id(entry);
|
||||
if (!id) { snprintf(errbuf, 1024, "device entry missing id"); return -1; }
|
||||
|
||||
if (st->depth >= MAX_DEPTH) {
|
||||
snprintf(errbuf, 1024, "device tree too deep");
|
||||
return -1;
|
||||
}
|
||||
st->path_stack[st->depth] = id;
|
||||
st->depth++;
|
||||
|
||||
hmmmm_config_DeviceConfig_union_type_t ctype =
|
||||
hmmmm_config_DeviceEntry_config_type(entry);
|
||||
|
||||
if (ctype == hmmmm_config_DeviceConfig_BaseDeviceConfig) {
|
||||
if (st->count >= MAX_DEVICES) {
|
||||
snprintf(errbuf, 1024, "too many devices");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hmmmm_config_BaseDeviceConfig_table_t base =
|
||||
(hmmmm_config_BaseDeviceConfig_table_t)
|
||||
hmmmm_config_DeviceEntry_config(entry);
|
||||
|
||||
conf_dev_t* dc = conf_from_fb(base, errbuf);
|
||||
if (!dc) return -1;
|
||||
|
||||
device_handle_t* dev = openBaseDevice(dc, errbuf);
|
||||
if (!dev) {
|
||||
freeConf(dc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint64_t clockDivider = hmmmm_config_DeviceEntry_clock_divider(entry);
|
||||
if(clockDivider > 0)
|
||||
{
|
||||
dev->clockDivider = clockDivider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dev->lib->fillSmartReadSpecs(dev->specs, NULL, 0);
|
||||
dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0);
|
||||
|
||||
char initErrBuf[256];
|
||||
|
||||
dev->ctx = dev->lib->init(dev->specs, initErrBuf);
|
||||
if (!dev->ctx)
|
||||
{
|
||||
snprintf(errbuf, 1024, "error while loading device %lu: %s", st->count, initErrBuf);
|
||||
closeBaseDevice(dev);
|
||||
free(dev);
|
||||
freeConf(dc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->lib->reset(dev->specs, dev->ctx);
|
||||
|
||||
freeConf(dc);
|
||||
|
||||
size_t idx = st->count;
|
||||
st->handles[idx] = dev;
|
||||
|
||||
// Store segment count and names from FlatBuffer config
|
||||
hmmmm_config_MemSegment_vec_t dev_segs =
|
||||
hmmmm_config_BaseDeviceConfig_mem_segments(base);
|
||||
size_t nseg = dev_segs ? hmmmm_config_MemSegment_vec_len(dev_segs) : 0;
|
||||
// if (nseg > MAX_SEGMENTS) nseg = MAX_SEGMENTS;
|
||||
|
||||
st->seg_counts[idx] = nseg;
|
||||
for (size_t si = 0; si < nseg; si++)
|
||||
{
|
||||
hmmmm_config_MemSegment_table_t seg =
|
||||
hmmmm_config_MemSegment_vec_at(dev_segs, si);
|
||||
flatbuffers_string_t sname = hmmmm_config_MemSegment_name(seg);
|
||||
st->seg_names[idx][si] = strdup(sname ? sname : "");
|
||||
}
|
||||
|
||||
// Copy path
|
||||
st->path_lens[idx] = st->depth;
|
||||
st->paths[idx] = calloc(st->depth + 1, sizeof(char*));
|
||||
if (!st->paths[idx])
|
||||
{
|
||||
snprintf(errbuf, 1024, "alloc path array");
|
||||
return -1;
|
||||
}
|
||||
for (size_t p = 0; p < st->depth; p++)
|
||||
{
|
||||
st->paths[idx][p] = strdup(st->path_stack[p]);
|
||||
}
|
||||
st->paths[idx][st->depth] = NULL;
|
||||
|
||||
st->count++;
|
||||
|
||||
}
|
||||
else if (ctype == hmmmm_config_DeviceConfig_ComposeDeviceConfig)
|
||||
{
|
||||
hmmmm_config_ComposeDeviceConfig_table_t child =
|
||||
(hmmmm_config_ComposeDeviceConfig_table_t)
|
||||
hmmmm_config_DeviceEntry_config(entry);
|
||||
|
||||
if (load_devices_recursive(child, st, errbuf) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errbuf, 1024, "unknown device config type %u", ctype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
st->depth--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void free_load_state_paths(LoadState* st)
|
||||
{
|
||||
for (size_t i = 0; i < st->count; i++) {
|
||||
if (st->paths[i]) {
|
||||
for (size_t j = 0; j < st->path_lens[i]; j++) {
|
||||
free(st->paths[i][j]);
|
||||
}
|
||||
free(st->paths[i]);
|
||||
}
|
||||
for (size_t si = 0; si < st->seg_counts[i]; si++) {
|
||||
free(st->seg_names[i][si]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find a device index by its leaf ID (last path component)
|
||||
static size_t find_device_by_id(LoadState* st, const char* id)
|
||||
{
|
||||
for (size_t i = 0; i < st->count; i++) {
|
||||
if (st->path_lens[i] == 0) continue;
|
||||
// Match by last path component (the device's own ID)
|
||||
const char* leaf = st->paths[i][st->path_lens[i] - 1];
|
||||
if (strcmp(leaf, id) == 0) return i;
|
||||
}
|
||||
return (size_t)~0;
|
||||
}
|
||||
|
||||
|
||||
// Find a segment index by name in a device
|
||||
static size_t find_seg_by_name(device_handle_t* dev, size_t seg_count, const char* name)
|
||||
{
|
||||
const device_mem_t* mem = dev->ctx->deviceMem;
|
||||
for (size_t i = 0; i < seg_count; i++) {
|
||||
if (strcmp(mem->memsegNames[i], name) == 0) return i;
|
||||
}
|
||||
return (size_t)~0;
|
||||
}
|
||||
|
||||
|
||||
static int apply_projections(
|
||||
hmmmm_config_ComposeDeviceConfig_table_t compose,
|
||||
LoadState* st,
|
||||
char* errbuf)
|
||||
{
|
||||
hmmmm_config_Projection_vec_t proj_vec =
|
||||
hmmmm_config_ComposeDeviceConfig_projections(compose);
|
||||
size_t proj_n = proj_vec ? hmmmm_config_Projection_vec_len(proj_vec) : 0;
|
||||
|
||||
for (size_t i = 0; i < proj_n; i++) {
|
||||
hmmmm_config_Projection_table_t proj =
|
||||
hmmmm_config_Projection_vec_at(proj_vec, i);
|
||||
|
||||
flatbuffers_string_t base_at = hmmmm_config_Projection_base_at(proj);
|
||||
flatbuffers_string_t base_seg = hmmmm_config_Projection_base_seg(proj);
|
||||
flatbuffers_string_t target_at = hmmmm_config_Projection_target_at(proj);
|
||||
flatbuffers_string_t target_seg = hmmmm_config_Projection_target_seg(proj);
|
||||
uint32_t shift = hmmmm_config_Projection_shift(proj);
|
||||
|
||||
if (!base_at || !base_seg || !target_at || !target_seg) {
|
||||
snprintf(errbuf, 1024, "projection %zu: missing field", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t base_idx = find_device_by_id(st, base_at);
|
||||
if (base_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "projection %zu: base device '%s' not found", i, base_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t target_idx = find_device_by_id(st, target_at);
|
||||
if (target_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "projection %zu: target device '%s' not found", i, target_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t base_seg_idx = find_seg_by_name(
|
||||
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
|
||||
if (base_seg_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "projection %zu: base segment '%s' not found in '%s'",
|
||||
i, base_seg, base_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t target_seg_idx = find_seg_by_name(
|
||||
st->handles[target_idx], st->seg_counts[target_idx], target_seg);
|
||||
if (target_seg_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "projection %zu: target segment '%s' not found in '%s'",
|
||||
i, target_seg, target_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Remap: target device's segment now points to base device's segment memory + shift
|
||||
void* base_ptr = st->handles[base_idx]->ctx->deviceMem->cells[base_seg_idx];
|
||||
st->handles[target_idx]->ctx->deviceMem->cells[target_seg_idx] =
|
||||
(uint8_t*)base_ptr + shift;
|
||||
|
||||
printf("[CTRL/CONFIG] projection: %s.%s -> %s.%s (shift=%u)\n",
|
||||
base_at, base_seg, target_at, target_seg, shift);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ── Intercept handler context and functions ─────────────────────────────────
|
||||
|
||||
typedef struct {
|
||||
uint8_t* point_cell; // direct pointer to point device's cell
|
||||
uint8_t* base_cell; // direct pointer to base device's cell (shadow_copy)
|
||||
uint8_t word_len;
|
||||
} intercept_ctx_t;
|
||||
|
||||
// shadow_replace: read from point device instead of base
|
||||
static void* intercept_replace_read(uint64_t ident, uint64_t addr, void* rawCells)
|
||||
{
|
||||
const intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
return ctx->point_cell;
|
||||
}
|
||||
|
||||
// shadow_replace: write to point device instead of base
|
||||
static void intercept_replace_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
|
||||
{
|
||||
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
memcpy(ctx->point_cell, data, ctx->word_len);
|
||||
}
|
||||
|
||||
// shadow_copy: write to both base and point
|
||||
static void intercept_copy_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
|
||||
{
|
||||
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
memcpy(ctx->base_cell, data, ctx->word_len);
|
||||
memcpy(ctx->point_cell, data, ctx->word_len);
|
||||
}
|
||||
|
||||
#define MAX_INTERCEPTS 256
|
||||
|
||||
static int apply_intercepts(
|
||||
hmmmm_config_ComposeDeviceConfig_table_t compose,
|
||||
LoadState* st,
|
||||
intercept_ctx_t** out_ctxs,
|
||||
size_t* out_ctx_count,
|
||||
char* errbuf)
|
||||
{
|
||||
hmmmm_config_Intercept_vec_t icpt_vec =
|
||||
hmmmm_config_ComposeDeviceConfig_intercepts(compose);
|
||||
size_t icpt_n = icpt_vec ? hmmmm_config_Intercept_vec_len(icpt_vec) : 0;
|
||||
|
||||
*out_ctx_count = 0;
|
||||
if (icpt_n == 0) { *out_ctxs = NULL; return 0; }
|
||||
if (icpt_n > MAX_INTERCEPTS) {
|
||||
snprintf(errbuf, 1024, "too many intercepts (%zu)", icpt_n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate array of intercept contexts (one per intercept)
|
||||
intercept_ctx_t* ctxs = calloc(icpt_n, sizeof(intercept_ctx_t));
|
||||
if (!ctxs) { snprintf(errbuf, 1024, "alloc intercept contexts"); return -1; }
|
||||
|
||||
for (size_t i = 0; i < icpt_n; i++) {
|
||||
hmmmm_config_Intercept_table_t icpt =
|
||||
hmmmm_config_Intercept_vec_at(icpt_vec, i);
|
||||
|
||||
flatbuffers_string_t name = hmmmm_config_Intercept_name(icpt);
|
||||
int8_t op = hmmmm_config_Intercept_op(icpt);
|
||||
int8_t mode = hmmmm_config_Intercept_mode(icpt);
|
||||
|
||||
flatbuffers_string_t base_at = hmmmm_config_Intercept_base_at(icpt);
|
||||
flatbuffers_string_t base_seg = hmmmm_config_Intercept_base_seg(icpt);
|
||||
uint32_t base_addr = hmmmm_config_Intercept_base_addr(icpt);
|
||||
|
||||
flatbuffers_string_t point_at = hmmmm_config_Intercept_point_at(icpt);
|
||||
flatbuffers_string_t point_seg = hmmmm_config_Intercept_point_seg(icpt);
|
||||
uint32_t point_addr = hmmmm_config_Intercept_point_addr(icpt);
|
||||
|
||||
if (!base_at || !base_seg || !point_at || !point_seg) {
|
||||
snprintf(errbuf, 1024, "intercept %zu: missing field", i);
|
||||
free(ctxs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Resolve base device + segment
|
||||
size_t base_idx = find_device_by_id(st, base_at);
|
||||
if (base_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "intercept %zu: base device '%s' not found", i, base_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
size_t base_seg_idx = find_seg_by_name(
|
||||
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
|
||||
if (base_seg_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "intercept %zu: base segment '%s' not found in '%s'",
|
||||
i, base_seg, base_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
// Resolve point device + segment
|
||||
size_t point_idx = find_device_by_id(st, point_at);
|
||||
if (point_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "intercept %zu: point device '%s' not found", i, point_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
size_t point_seg_idx = find_seg_by_name(
|
||||
st->handles[point_idx], st->seg_counts[point_idx], point_seg);
|
||||
if (point_seg_idx == (size_t)~0) {
|
||||
snprintf(errbuf, 1024, "intercept %zu: point segment '%s' not found in '%s'",
|
||||
i, point_seg, point_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
device_mem_t* base_mem = st->handles[base_idx]->ctx->deviceMem;
|
||||
device_mem_t* point_mem = st->handles[point_idx]->ctx->deviceMem;
|
||||
|
||||
// Compute global address on base device (for handler array index)
|
||||
uint64_t base_global = base_mem->memsegShifts[base_seg_idx] + base_addr;
|
||||
|
||||
// Get word length from base segment
|
||||
uint8_t wl = 1; // default
|
||||
// memsegShifts difference gives segment size; use rawCells layout
|
||||
// We can get wordLen from the point segment shift or use 1 for byte-addressable
|
||||
|
||||
// Fill intercept context
|
||||
intercept_ctx_t* ctx = &ctxs[i];
|
||||
ctx->word_len = wl;
|
||||
ctx->base_cell = (uint8_t*)base_mem->rawCells + base_global;
|
||||
ctx->point_cell = (uint8_t*)point_mem->rawCells
|
||||
+ point_mem->memsegShifts[point_seg_idx] + point_addr;
|
||||
|
||||
uint64_t ident = (uint64_t)(uintptr_t)ctx;
|
||||
|
||||
if (mode == hmmmm_config_InterceptMode_shadow_replace) {
|
||||
if (op == hmmmm_config_InterceptOp_op_read ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrReadHandlers[base_global].func = intercept_replace_read;
|
||||
base_mem->smartAddrReadHandlers[base_global].ident = ident;
|
||||
}
|
||||
if (op == hmmmm_config_InterceptOp_op_write ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrWriteHandlers[base_global].func = intercept_replace_write;
|
||||
base_mem->smartAddrWriteHandlers[base_global].ident = ident;
|
||||
}
|
||||
} else if (mode == hmmmm_config_InterceptMode_shadow_copy) {
|
||||
if (op == hmmmm_config_InterceptOp_op_write ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrWriteHandlers[base_global].func = intercept_copy_write;
|
||||
base_mem->smartAddrWriteHandlers[base_global].ident = ident;
|
||||
}
|
||||
} else if (mode == hmmmm_config_InterceptMode_callback) {
|
||||
// Callback mode: not implemented yet — skip with warning
|
||||
printf("[CTRL/CONFIG] intercept %zu '%s': callback mode not yet implemented\n",
|
||||
i, name ? name : "(unnamed)");
|
||||
} else {
|
||||
snprintf(errbuf, 1024, "intercept %zu: unknown mode %d", i, mode);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
printf("[CTRL/CONFIG] intercept: %s.%s[%u] -> %s.%s[%u] mode=%s op=%s\n",
|
||||
base_at, base_seg, base_addr,
|
||||
point_at, point_seg, point_addr,
|
||||
hmmmm_config_InterceptMode_name(mode),
|
||||
hmmmm_config_InterceptOp_name(op));
|
||||
}
|
||||
|
||||
*out_ctxs = ctxs;
|
||||
*out_ctx_count = icpt_n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void free_old_config(EmulContext* emulContext)
|
||||
{
|
||||
if (emulContext->devicesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Free intercept context array (single contiguous allocation)
|
||||
if (emulContext->interceptCtxs)
|
||||
{
|
||||
free(emulContext->interceptCtxs);
|
||||
emulContext->interceptCtxs = NULL;
|
||||
emulContext->interceptCtxCount = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < emulContext->devicesCount; i++)
|
||||
{
|
||||
device_handle_t* dev = (device_handle_t*)emulContext->deviceHandles[i];
|
||||
closeBaseDevice(dev);
|
||||
free(dev);
|
||||
|
||||
if (emulContext->deviceStreamRegs[i])
|
||||
{
|
||||
free(emulContext->deviceStreamRegs[i]->regs);
|
||||
free(emulContext->deviceStreamRegs[i]);
|
||||
}
|
||||
}
|
||||
free(emulContext->deviceHandles);
|
||||
free(emulContext->devicesMem);
|
||||
free(emulContext->deviceStreamRegs);
|
||||
|
||||
emulContext->deviceHandles = NULL;
|
||||
emulContext->devicesMem = NULL;
|
||||
emulContext->deviceStreamRegs = NULL;
|
||||
emulContext->devicesCount = 0;
|
||||
emulContext->simRateLimit = 0;
|
||||
|
||||
if (emulContext->deviceIdMappingMsg)
|
||||
{
|
||||
free(emulContext->deviceIdMappingMsg);
|
||||
emulContext->deviceIdMappingMsg = NULL;
|
||||
emulContext->deviceIdMappingMsgLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleConfigCtrlMessage(
|
||||
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
ClientContext* ctx,
|
||||
EmulContext* emulContext)
|
||||
{
|
||||
printf("[CTRL/CONFIG] received config load request\n");
|
||||
|
||||
char errbuf[1024];
|
||||
|
||||
// Block config load only while actively executing (ticking)
|
||||
if (*emulContext->emulState != EMUL_STATE_STILL) {
|
||||
snprintf(errbuf, 1024, "reset emulator before loading config, current state is %d", *emulContext->emulState);
|
||||
printf("[CTRL/CONFIG] error: %s\n", errbuf);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
hmmmm_config_EmulationConfig_table_t econf =
|
||||
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_config(msg);
|
||||
|
||||
if (!econf) {
|
||||
snprintf(errbuf, 1024, "missing EmulationConfig");
|
||||
printf("[CTRL/CONFIG] error: %s\n", errbuf);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
flatbuffers_string_t root_id = hmmmm_config_EmulationConfig_root_id(econf);
|
||||
hmmmm_config_ComposeDeviceConfig_table_t root =
|
||||
hmmmm_config_EmulationConfig_root(econf);
|
||||
|
||||
if (!root) {
|
||||
snprintf(errbuf, 1024, "missing root ComposeDeviceConfig");
|
||||
printf("[CTRL/CONFIG] error: %s\n", errbuf);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t simRateLimit = hmmmm_config_EmulationConfig_sim_rate_limit(econf);
|
||||
|
||||
// Free old config
|
||||
free_old_config(emulContext);
|
||||
|
||||
// Load new config
|
||||
LoadState st = {0};
|
||||
if (root_id) {
|
||||
st.path_stack[0] = root_id;
|
||||
st.depth = 1;
|
||||
}
|
||||
|
||||
if (load_devices_recursive(root, &st, errbuf) != 0) {
|
||||
printf("[CTRL/CONFIG] load error: %s\n", errbuf);
|
||||
|
||||
// Close any devices that were opened before the failure
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
printf("[CTRL/CONFIG] loaded %zu devices\n", st.count);
|
||||
|
||||
// Apply projections
|
||||
if (apply_projections(root, &st, errbuf) != 0) {
|
||||
printf("[CTRL/CONFIG] projection error: %s\n", errbuf);
|
||||
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply intercepts
|
||||
intercept_ctx_t* icpt_ctxs = NULL;
|
||||
size_t icpt_count = 0;
|
||||
if (apply_intercepts(root, &st, &icpt_ctxs, &icpt_count, errbuf) != 0) {
|
||||
printf("[CTRL/CONFIG] intercept error: %s\n", errbuf);
|
||||
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store intercept context array for later cleanup (single contiguous allocation)
|
||||
emulContext->interceptCtxs = icpt_ctxs;
|
||||
emulContext->interceptCtxCount = icpt_count;
|
||||
|
||||
// Allocate new arrays for EmulContext
|
||||
size_t dc = st.count;
|
||||
emulContext->devicesCount = dc;
|
||||
emulContext->simRateLimit = simRateLimit;
|
||||
|
||||
emulContext->deviceHandles = calloc(dc, sizeof(void*));
|
||||
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
|
||||
emulContext->devicesMemSegAddrs = calloc(dc, sizeof(size_t*));
|
||||
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
|
||||
|
||||
if (!emulContext->deviceHandles || !emulContext->devicesMem ||
|
||||
!emulContext->deviceStreamRegs) {
|
||||
free(emulContext->deviceHandles);
|
||||
free(emulContext->devicesMem);
|
||||
free(emulContext->deviceStreamRegs);
|
||||
emulContext->deviceHandles = NULL;
|
||||
emulContext->devicesMem = NULL;
|
||||
emulContext->deviceStreamRegs = NULL;
|
||||
emulContext->devicesCount = 0;
|
||||
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
snprintf(errbuf, sizeof(errbuf), "failed to allocate device arrays");
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dc; i++)
|
||||
{
|
||||
emulContext->deviceHandles[i] = st.handles[i];
|
||||
emulContext->devicesMem[i] = st.handles[i]->ctx->deviceMem->rawCells;
|
||||
|
||||
emulContext->devicesMemSegAddrs[i] = calloc(st.seg_counts[i], sizeof(size_t));
|
||||
|
||||
for(size_t seg_id = 0; seg_id < st.seg_counts[i]; seg_id++)
|
||||
{
|
||||
|
||||
// size_t segShift = (size_t)(st.handles[i]->ctx->deviceMem->cells[seg_id]) - (size_t)(st.handles[i]->ctx->deviceMem->cells[0]);
|
||||
size_t segShift = st.handles[i]->ctx->deviceMem->memsegShifts[seg_id];
|
||||
emulContext->devicesMemSegAddrs[i][seg_id] = segShift;
|
||||
}
|
||||
|
||||
DeviceSegStreamReg* dsr = calloc(1, sizeof(DeviceSegStreamReg));
|
||||
if (!dsr)
|
||||
{
|
||||
emulContext->deviceStreamRegs[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
dsr->allocatedSize = 4;
|
||||
dsr->regCount = 0;
|
||||
dsr->regs = calloc(4, sizeof(StreamReg));
|
||||
if (!dsr->regs)
|
||||
{
|
||||
free(dsr);
|
||||
emulContext->deviceStreamRegs[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
emulContext->deviceStreamRegs[i] = dsr;
|
||||
}
|
||||
|
||||
// Reset emulation state to STILL and notify all clients
|
||||
if (*emulContext->emulState != EMUL_STATE_STILL)
|
||||
{
|
||||
*emulContext->emulState = EMUL_STATE_STILL;
|
||||
size_t notify_len;
|
||||
uint8_t* notify = fb_build_exec_notify(0, *emulContext->clockCounter, EMUL_STATE_STILL, ¬ify_len);
|
||||
broadcastClients(emulContext, notify, notify_len);
|
||||
}
|
||||
|
||||
// Build DeviceIdMappingNotif and broadcast to all connected clients
|
||||
size_t msg_len;
|
||||
printf("fuck!\n");
|
||||
|
||||
|
||||
for(size_t i = 0; i < emulContext->devicesCount; i++)
|
||||
{
|
||||
const device_handle_t* handl = emulContext->deviceHandles[i];
|
||||
for(size_t j = 0; j < st.seg_counts[i]; j++)
|
||||
{
|
||||
if(st.seg_names[i][j])
|
||||
{
|
||||
free(st.seg_names[i][j]);
|
||||
}
|
||||
printf("setting device %lu segment %lu name: \"%s\"\n", i, j, handl->ctx->deviceMem->memsegNames[j]);
|
||||
st.seg_names[i][j] = strdup(handl->ctx->deviceMem->memsegNames[j]);
|
||||
}
|
||||
}
|
||||
//*emulContext->resetRequest = 1;
|
||||
uint8_t* out = fb_build_config_device_id_mapping(
|
||||
nonce, st.paths, st.path_lens, st.seg_names, st.seg_counts, dc, &msg_len);
|
||||
|
||||
free_load_state_paths(&st);
|
||||
|
||||
// Cache a copy for newly connecting clients
|
||||
if (emulContext->deviceIdMappingMsg) {
|
||||
free(emulContext->deviceIdMappingMsg);
|
||||
}
|
||||
emulContext->deviceIdMappingMsg = malloc(msg_len);
|
||||
if (emulContext->deviceIdMappingMsg) {
|
||||
memcpy(emulContext->deviceIdMappingMsg, out, msg_len);
|
||||
emulContext->deviceIdMappingMsgLen = msg_len;
|
||||
}
|
||||
|
||||
// Broadcast to all clients (broadcastClients takes ownership of out)
|
||||
broadcastClients(emulContext, out, msg_len);
|
||||
}
|
||||
@@ -1,33 +1,135 @@
|
||||
#include "proto/handlers/control.h"
|
||||
#include "proto/handlers/config.h"
|
||||
|
||||
#include "proto/pack.h"
|
||||
#include "proto/enums.h"
|
||||
#include "panic.h"
|
||||
#include <stdio.h>
|
||||
#include "state.h"
|
||||
#include "proto/msg.h"
|
||||
#include "proto/dial.h"
|
||||
|
||||
#include "control_reader.h"
|
||||
#include "exec_ctrl_reader.h"
|
||||
#include "config_ctrl_reader.h"
|
||||
#include "setup_buf_reader.h"
|
||||
#include "orphaned_reader.h"
|
||||
#include "lost_reader.h"
|
||||
|
||||
|
||||
|
||||
|
||||
void handleIncomingControlMessage(BaseMessage* msg, EmulContext* emulContext)
|
||||
// Maps FlatBuffers ExecPrompt values to EMUL_STATE_OP_* constants.
|
||||
// ExecPrompt: start=1, pause=2, resume=3, stop=4, reset=5
|
||||
// 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)
|
||||
{
|
||||
printf("ctrl exec\n");
|
||||
uint8_t stateOp = ((const uint8_t*)(msg->payload))[0];
|
||||
printf("state operation: %u\n", stateOp);
|
||||
uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, stateOp);
|
||||
if(newEmulState != *emulContext->emulState)
|
||||
{
|
||||
printf("Switch state %u -> %u\n", *emulContext->emulState, newEmulState);
|
||||
*emulContext->emulState = newEmulState;
|
||||
|
||||
size_t len = 0;
|
||||
uint8_t* notify = createControlNotifyMessage(msg->nonce, *emulContext->clockCounter, newEmulState, &len);
|
||||
broadcastClients(emulContext, notify, len);
|
||||
switch (prompt) {
|
||||
case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START;
|
||||
case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE;
|
||||
case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME;
|
||||
case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP;
|
||||
case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleIncomingCtrlMessage(
|
||||
hmmmm_ctrl_CtrlClientMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
ClientContext* ctx,
|
||||
EmulContext* emulContext)
|
||||
{
|
||||
hmmmm_ctrl_CtrlClientPayload_union_type_t ptype =
|
||||
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);
|
||||
uint64_t tick_count =
|
||||
hmmmm_ctrl_exec_ExecCtrlMessage_tick_count(exec);
|
||||
|
||||
printf("[CTRL/EXEC] prompt=%s tick_count=%lu\n",
|
||||
hmmmm_ctrl_exec_ExecPrompt_name(prompt), tick_count);
|
||||
|
||||
uint8_t state_op = prompt_to_state_op(prompt);
|
||||
if (state_op == 0) {
|
||||
printf("[CTRL/EXEC] unknown prompt, ignoring\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(state_op == EMUL_STATE_OP_RESET && emulContext->emulState != EMUL_STATE_STILL)
|
||||
{
|
||||
*emulContext->resetRequest = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("invalid payload header: %u\n", msg->payloadHeader);
|
||||
uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op);
|
||||
*emulContext->emulState = new_state;
|
||||
printf("[CTRL/EXEC] state -> %u\n", new_state);
|
||||
|
||||
if (new_state == EMUL_STATE_EXEC && tick_count > 0) {
|
||||
*emulContext->tickTarget = *emulContext->clockCounter + tick_count;
|
||||
|
||||
printf("set ticket target at %lu\n", *emulContext->tickTarget);
|
||||
} else if (new_state == EMUL_STATE_EXEC) {
|
||||
*emulContext->tickTarget = 0;
|
||||
printf("reset tick target\n");
|
||||
}
|
||||
|
||||
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->outMsgQueue, 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->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
}
|
||||
else if (ptype == hmmmm_ctrl_CtrlClientPayload_ConfigCtrlMessage)
|
||||
{
|
||||
handleConfigCtrlMessage(
|
||||
(hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t)
|
||||
hmmmm_ctrl_CtrlClientMessage_payload(msg),
|
||||
nonce, ctx, emulContext);
|
||||
}
|
||||
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->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +1,171 @@
|
||||
#include "proto/handlers/mem.h"
|
||||
#include "proto/enums.h"
|
||||
#include "proto/pack.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "base_device.h"
|
||||
#include "proto/msg.h"
|
||||
#include "proto/dial.h"
|
||||
#include "mem_reader.h"
|
||||
#include "pub/libhmmmm/mem.h"
|
||||
|
||||
|
||||
uint8_t* createMemReadResponseMessage(uint64_t nonce, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut)
|
||||
// #define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
|
||||
static void printMemory(void* cells, uint64_t cellsCount)
|
||||
{
|
||||
*lenOut = 9 + 8 + payloadLen;
|
||||
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
|
||||
NULL_GUARD(outmsg);
|
||||
|
||||
encodeUintToBytes(nonce, outmsg);
|
||||
for(uint64_t i = 0; i < cellsCount; i++)
|
||||
{
|
||||
printf("%lu: 0x%04X", i, ((uint8_t*)cells)[i]);
|
||||
|
||||
outmsg[8] = PACKET_TYPE_MEM << 4;
|
||||
outmsg[8] |= MEM_TYPE_READ_RESP;
|
||||
// uint8_t wasRead = 0;
|
||||
// for (uint8_t j = 0; j < mem->memreadLen; j++)
|
||||
// {
|
||||
// if(mem->memreadCellAddrs[j] == i)
|
||||
// {
|
||||
// wasRead = 1;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// uint8_t wasWrite = 0;
|
||||
// for (uint8_t j = 0; j < mem->memwriteLen; j++)
|
||||
// {
|
||||
// if(mem->memwriteCellAddrs[j] == i)
|
||||
// {
|
||||
// wasWrite = 1;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
encodeUintToBytes(clockCounter, outmsg + 9);
|
||||
// if (wasRead == 1)
|
||||
// {
|
||||
// printf("\t[was read]");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// printf("\t[was not read]");
|
||||
// }
|
||||
|
||||
memcpy(outmsg + 9 + 8, payload, payloadLen);
|
||||
|
||||
return outmsg;
|
||||
// if (wasWrite == 1)
|
||||
// {
|
||||
// printf("\t[was written]");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// printf("\t[was not written]");
|
||||
// }
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingMemReadReq(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
|
||||
void handleIncomingMemMessage(
|
||||
hmmmm_mem_MemClientMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
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);
|
||||
hmmmm_mem_MemClientPayload_union_type_t ptype =
|
||||
hmmmm_mem_MemClientMessage_payload_type(msg);
|
||||
|
||||
if(devId >= emulContext->devicesCount)
|
||||
// printf("[MEM] client=%lu nonce=%lu type=%s\n",
|
||||
// ctx->clientId, nonce,
|
||||
// hmmmm_mem_MemClientPayload_type_name(ptype));
|
||||
|
||||
if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest)
|
||||
{
|
||||
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);
|
||||
volatile 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;
|
||||
}
|
||||
|
||||
// offset = (uint32_t)(((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
|
||||
|
||||
//TODO: lookup config for global addr conversion
|
||||
uint64_t globalAddr = startAddr + segId * 128;
|
||||
// if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
|
||||
// printf("[MEM/READ] out of bounds\n");
|
||||
// return;
|
||||
// }
|
||||
// printf("[MEM/READ] from %d/%d+%d:%d\n", dev_id, seg_id, offset, length);
|
||||
|
||||
uint8_t* readPtr = emulContext->devicesMem[devId] + globalAddr;
|
||||
device_handle_t* handl = emulContext->deviceHandles[dev_id];
|
||||
|
||||
size_t outLen = 0;
|
||||
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, readPtr, readLen, &outLen);
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, outMsg, outLen);
|
||||
|
||||
const uint8_t* base = handl->ctx->deviceMem->cells[seg_id]; //emulContext->devicesMem[dev_id] + handl->ctx->deviceMem->memsegShifts[seg_id];
|
||||
|
||||
// for(size_t i = 0; i < length; i++)
|
||||
// {
|
||||
// printf("%02X ", (base + offset)[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
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->outMsgQueue, ctx->clientId, out, out_len);
|
||||
}
|
||||
|
||||
|
||||
void handleIncomingMemWrite(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
|
||||
else if (ptype == hmmmm_mem_MemClientPayload_MemWriteRequest)
|
||||
{
|
||||
uint64_t devId = decodeBytesToU64(msg->payload);
|
||||
uint64_t segId = decodeBytesToU64(msg->payload + 8);
|
||||
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
|
||||
hmmmm_mem_MemWriteRequest_table_t req =
|
||||
(hmmmm_mem_MemWriteRequest_table_t)
|
||||
hmmmm_mem_MemClientMessage_payload(msg);
|
||||
|
||||
if(devId >= emulContext->devicesCount)
|
||||
{
|
||||
uint32_t dev_id = hmmmm_mem_MemWriteRequest_device_id(req);
|
||||
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);
|
||||
|
||||
// printf("[MEM/WRITE] device=%u seg=%u offset=%u len=%zu\n",
|
||||
// dev_id, seg_id, offset, data_len);
|
||||
|
||||
|
||||
if (dev_id >= (uint32_t)emulContext->devicesCount) {
|
||||
printf("[MEM/WRITE] invalid device %u\n", dev_id);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: lookup config for global addr conversion
|
||||
uint64_t globalAddr = startAddr + segId * 128;
|
||||
|
||||
uint8_t* writePtr = emulContext->devicesMem[devId] + globalAddr;
|
||||
|
||||
memcpy(writePtr, msg->payload + 24, msg->payloadLen - 24);
|
||||
if(!data) {
|
||||
printf("[MEM/WRITE] invalid data\n");
|
||||
return;
|
||||
}
|
||||
// if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
|
||||
// printf("[MEM/WRITE] out of bounds\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// device_mem_t* devMem = emulContext->devicesMem[dev_id];
|
||||
|
||||
device_handle_t* handl = emulContext->deviceHandles[dev_id];
|
||||
|
||||
// printf("[MEM/WRITE] from %d/%d+%d:%lu\n", dev_id, seg_id, offset, data_len);
|
||||
uint8_t* base = handl->ctx->deviceMem->cells[seg_id]; // emulContext->devicesMem[dev_id] + handl->ctx->deviceMem->memsegShifts[seg_id];
|
||||
memcpy(base + offset, data, data_len);
|
||||
|
||||
|
||||
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
|
||||
{
|
||||
if(msg->payloadHeader == MEM_TYPE_READ_REQ)
|
||||
{
|
||||
handleIncomingMemReadReq(msg, ctx, emulContext);
|
||||
}
|
||||
else if(msg->payloadHeader == MEM_TYPE_WRITE_PUSH)
|
||||
{
|
||||
handleIncomingMemWrite(msg, ctx, emulContext);
|
||||
|
||||
// for(size_t i = 0; i < data_len; i++)
|
||||
// {
|
||||
// printf("%02X ", (base + offset)[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
size_t out_len;
|
||||
uint8_t* out = fb_build_mem_read_response(
|
||||
nonce, *emulContext->clockCounter,
|
||||
dev_id, seg_id, offset,
|
||||
base + offset, data_len,
|
||||
&out_len);
|
||||
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, out_len);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,32 @@
|
||||
#include "proto/handlers/stream.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "streamed.h"
|
||||
#include "proto/pack.h"
|
||||
#include "proto/enums.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)
|
||||
{
|
||||
for(size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++)
|
||||
static void unregisterClientStream(
|
||||
EmulContext* emulContext, ClientContext* ctx, uint32_t regId)
|
||||
{
|
||||
for (size_t deviceId = 0; deviceId < emulContext->devicesCount; 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];
|
||||
if(iReg->clientContext->clientId == ctx->clientId && iReg->regId == regId)
|
||||
{
|
||||
printf("Discard stream %u register for client %lu\n", regId, ctx->clientId);
|
||||
deviceRegs->regCount -= 1;
|
||||
for(size_t j = i; j < deviceRegs->regCount; j++)
|
||||
if (iReg->clientContext->clientId == ctx->clientId
|
||||
&& iReg->regId == regId)
|
||||
{
|
||||
printf("Discard stream %u for client %lu\n", regId, ctx->clientId);
|
||||
deviceRegs->regCount--;
|
||||
for (size_t j = i; j < deviceRegs->regCount; j++) {
|
||||
deviceRegs->regs[j] = deviceRegs->regs[j + 1];
|
||||
}
|
||||
break;
|
||||
@@ -31,94 +37,121 @@ void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32
|
||||
|
||||
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];
|
||||
|
||||
StreamReg* newStreamRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount);
|
||||
NULL_GUARD(newStreamRegs);
|
||||
size_t newStreamRegsId = 0;
|
||||
StreamReg* newRegs = calloc(deviceRegs->allocatedSize, sizeof(StreamReg));
|
||||
NULL_GUARD(newRegs);
|
||||
size_t newCount = 0;
|
||||
|
||||
for (size_t i = 0; i < deviceRegs->regCount; i++)
|
||||
{
|
||||
StreamReg* reg = &deviceRegs->regs[i];
|
||||
if (reg->clientContext->clientId != ctx->clientId)
|
||||
{
|
||||
newStreamRegs[newStreamRegsId] = *reg;
|
||||
newStreamRegsId++;
|
||||
newRegs[newCount++] = *reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Removing reg %d mode stream for %lu/%lu+%lu: [%u]\n", reg->mode, deviceId, reg->startAddr, reg->segLen, reg->regId);
|
||||
printf("Removing stream reg [%u] for client %lu\n",
|
||||
reg->regId, ctx->clientId);
|
||||
}
|
||||
}
|
||||
free(deviceRegs->regs);
|
||||
deviceRegs->regCount = newStreamRegsId;
|
||||
deviceRegs->regs = newStreamRegs;
|
||||
StreamReg* oldRegs = deviceRegs->regs;
|
||||
deviceRegs->regCount = newCount;
|
||||
deviceRegs->regs = newRegs;
|
||||
free(oldRegs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleStreamRegMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext, uint8_t X)
|
||||
// ── Incoming stream messages ──────────────────────────────────────────────────
|
||||
|
||||
void handleIncomingStreamMessage(
|
||||
hmmmm_stream_StreamClientMessage_table_t msg,
|
||||
uint64_t nonce,
|
||||
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 segLen = decodeBytesToU64(msg->payload + 24);
|
||||
hmmmm_stream_StreamClientPayload_union_type_t ptype =
|
||||
hmmmm_stream_StreamClientMessage_payload_type(msg);
|
||||
|
||||
if(devId >= emulContext->devicesCount)
|
||||
printf("[STREAM] client=%lu nonce=%lu type=%s\n",
|
||||
ctx->clientId, nonce,
|
||||
hmmmm_stream_StreamClientPayload_type_name(ptype));
|
||||
|
||||
if (ptype == hmmmm_stream_StreamClientPayload_StreamRegRequest)
|
||||
{
|
||||
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[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;
|
||||
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;
|
||||
}
|
||||
|
||||
//TODO: lookup config for global segment addr conversion
|
||||
uint64_t globalStartAddr = startAddr + segId * 128;
|
||||
|
||||
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount];
|
||||
uint64_t globalOffset = (((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
|
||||
|
||||
uint32_t reg_id = ctx->streamRegIterator++;
|
||||
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
|
||||
reg->clientContext = ctx;
|
||||
reg->segLen = segLen;
|
||||
reg->startAddr = globalStartAddr;
|
||||
reg->regId = ctx->streamRegIterator;
|
||||
reg->mode = X;
|
||||
reg->regId = reg_id;
|
||||
reg->segId = seg_id;
|
||||
reg->startAddr = (uint64_t)offset;
|
||||
reg->startGlobalAddr = globalOffset;
|
||||
reg->segLen = (uint64_t)length;
|
||||
reg->mode = (uint8_t)mode;
|
||||
|
||||
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);
|
||||
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->outMsgQueue, ctx->clientId, out, msg_len);
|
||||
}
|
||||
|
||||
void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
|
||||
else if (ptype == hmmmm_stream_StreamClientPayload_StreamDeregRequest)
|
||||
{
|
||||
uint8_t X = msg->payloadHeader >> 3;
|
||||
uint8_t streamType = msg->payloadHeader & 0b111;
|
||||
hmmmm_stream_StreamDeregRequest_table_t req =
|
||||
(hmmmm_stream_StreamDeregRequest_table_t)
|
||||
hmmmm_stream_StreamClientMessage_payload(msg);
|
||||
|
||||
if(streamType == STREAM_TYPE_REG_REQUEST)
|
||||
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)
|
||||
{
|
||||
handleStreamRegMessage(msg, ctx, emulContext, X);
|
||||
}
|
||||
else if(streamType == STREAM_TYPE_REG_DISCARD)
|
||||
{
|
||||
uint32_t regId = decodeBytesToU32(msg->payload);
|
||||
unregisterClientStream(emulContext, ctx, regId);
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,49 @@
|
||||
#include "proto/handlers/ws.h"
|
||||
#include "context.h"
|
||||
#include "proto/handlers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "panic.h"
|
||||
#include "proto/enums.h"
|
||||
#include "my_mutex.h"
|
||||
#include "proto/enums.h"
|
||||
#include "proto/handlers/auth.h"
|
||||
#include "proto/msg.h"
|
||||
|
||||
#include "proto_verifier.h"
|
||||
#include "proto_reader.h"
|
||||
#include "ptQueue/inc/spsc.h"
|
||||
#include "wsServer/include/ws.h"
|
||||
|
||||
|
||||
void onWsOpen(ws_cli_conn_t client)
|
||||
{
|
||||
char errbuf[1024];
|
||||
|
||||
ptQueue* incomeQueue = ptQueueCreate(errbuf);
|
||||
NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf);
|
||||
queue_spsc_t *incomeQueue = malloc(sizeof(queue_spsc_t));
|
||||
// queue_spsc_t incomeQueue, outcomeQueue;
|
||||
if(incomeQueue == NULL || !queue_spsc_init(incomeQueue, 65536))
|
||||
{
|
||||
panic("Unable to create income queue");
|
||||
}
|
||||
queue_spsc_t *outcomeQueue = malloc(sizeof(queue_spsc_t));
|
||||
if(outcomeQueue == NULL || !queue_spsc_init(outcomeQueue, 65536))
|
||||
{
|
||||
panic("Unable to create outcome queue");
|
||||
}
|
||||
|
||||
ptQueue* outcomeQueue = ptQueueCreate(errbuf);
|
||||
// = ptQueueCreate(errbuf);
|
||||
// NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf);
|
||||
|
||||
NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf);
|
||||
// queue_spsc_t* outcomeQueue = ptQueueCreate(errbuf);
|
||||
// NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf);
|
||||
|
||||
ClientContext* cctx = malloc(sizeof(ClientContext));
|
||||
if(cctx == NULL)
|
||||
{
|
||||
ptQueueFree(incomeQueue);
|
||||
ptQueueFree(outcomeQueue);
|
||||
if (cctx == NULL) {
|
||||
queue_spsc_destroy(incomeQueue);
|
||||
queue_spsc_destroy(outcomeQueue);
|
||||
// ptQueueFree(incomeQueue);
|
||||
// ptQueueFree(outcomeQueue);
|
||||
panic("Unable to allocate client context\n");
|
||||
}
|
||||
cctx->clientId = client;
|
||||
@@ -32,13 +53,13 @@ void onWsOpen(ws_cli_conn_t client)
|
||||
cctx->outcomeQ = outcomeQueue;
|
||||
cctx->connectedAt = (uint64_t)time(NULL);
|
||||
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
|
||||
if(ev == NULL)
|
||||
{
|
||||
ptQueueFree(incomeQueue);
|
||||
ptQueueFree(outcomeQueue);
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
|
||||
if (ev == NULL) {
|
||||
queue_spsc_destroy(incomeQueue);
|
||||
queue_spsc_destroy(outcomeQueue);
|
||||
// ptQueueFree(incomeQueue);
|
||||
// ptQueueFree(outcomeQueue);
|
||||
free(cctx);
|
||||
|
||||
panic("Unable to allocate register event");
|
||||
}
|
||||
ev->regType = REG_EVTYPE_CONNECT;
|
||||
@@ -50,91 +71,112 @@ void onWsOpen(ws_cli_conn_t client)
|
||||
|
||||
with_lock(&ctx->registerMutex)
|
||||
{
|
||||
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
if(exitCode)
|
||||
{
|
||||
ptQueueFree(incomeQueue);
|
||||
ptQueueFree(outcomeQueue);
|
||||
free(cctx);
|
||||
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
|
||||
panic("Unable to push to reg queue: %s\n", errbuf);
|
||||
}
|
||||
queue_spsc_push(ctx->regQueue, ev);
|
||||
// if (!queue_spsc_push(ctx->regQueue, ev)) {
|
||||
// queue_spsc_destroy(incomeQueue);
|
||||
// queue_spsc_destroy(outcomeQueue);
|
||||
// // ptQueueFree(incomeQueue);
|
||||
// // ptQueueFree(outcomeQueue);
|
||||
// free(cctx);
|
||||
// panic("Unable to push to reg queue: %s\n", errbuf);
|
||||
// }
|
||||
}
|
||||
|
||||
char *cli;
|
||||
cli = ws_getaddress(client);
|
||||
const char* cli = ws_getaddress(client);
|
||||
printf("Connection %lu opened, addr: %s\n", client, cli);
|
||||
}
|
||||
|
||||
|
||||
void onWsClose(ws_cli_conn_t client)
|
||||
{
|
||||
pthread_t tid = pthread_self();
|
||||
char *cli;
|
||||
cli = ws_getaddress(client);
|
||||
printf("Connection %lu closed, addr: %s, thread id: %lu\n", client, cli, tid);
|
||||
|
||||
const char* cli = ws_getaddress(client);
|
||||
printf("Connection %lu closed, addr: %s\n", client, cli);
|
||||
|
||||
char errbuf[1024];
|
||||
|
||||
ClientContext* cctx = ws_get_connection_context(client);
|
||||
if(cctx == NULL)
|
||||
{
|
||||
cctx = NULL;
|
||||
printf("Unable to get client context\n");
|
||||
if (cctx == NULL) {
|
||||
printf("Unable to get client context for %lu\n", client);
|
||||
}
|
||||
|
||||
|
||||
ServerContext* ctx = ws_get_server_context(client);
|
||||
if(ctx == NULL)
|
||||
{
|
||||
printf("Unable to get server context\n");
|
||||
if (ctx == NULL) {
|
||||
printf("Unable to get server context for %lu\n", client);
|
||||
return;
|
||||
}
|
||||
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
|
||||
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
|
||||
NULL_GUARD(ev);
|
||||
ev->regType = REG_EVTYPE_CLOSE;
|
||||
ev->ctx = cctx;
|
||||
|
||||
|
||||
with_lock(&ctx->registerMutex)
|
||||
{
|
||||
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
if(exitCode)
|
||||
{
|
||||
panic("Unable to push to reg queue: %s\n", errbuf);
|
||||
}
|
||||
queue_spsc_push(ctx->regQueue, ev);
|
||||
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
|
||||
// if (!queue_spsc_push(ctx->regQueue, ev))
|
||||
// {
|
||||
// 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);
|
||||
if(cctx == NULL)
|
||||
{
|
||||
if (cctx == NULL) {
|
||||
panic("Unable to get client context\n");
|
||||
}
|
||||
|
||||
if (cctx->isAuthed == 0)
|
||||
{
|
||||
|
||||
if (!cctx->isAuthed) {
|
||||
handle_auth(cctx, client, msg, size, type);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Verify the FlatBuffer before queuing
|
||||
// if (hmmmm_ClientMessage_verify_as_root(msg, size)) {
|
||||
// printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
|
||||
|
||||
// FILE *write_ptr;
|
||||
|
||||
// write_ptr = fopen("/tmp/last_failed_packet.bin","wb"); // w for write, b for binary
|
||||
// NULL_GUARD(write_ptr);
|
||||
|
||||
// fwrite(msg,size,1,write_ptr); // write 10 bytes from our buffer
|
||||
// // free(write_ptr);
|
||||
// fclose(write_ptr);
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Copy bytes — the WS buffer is only valid for this callback's duration
|
||||
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;
|
||||
fbmsg->ctx = cctx;
|
||||
memcpy(fbmsg->data, msg, (size_t)size);
|
||||
|
||||
char errbuf[1024];
|
||||
BaseMessage* baseMsg = parseMessage(msg, size);
|
||||
printf("msg\n");
|
||||
int isErr = ptQueuePush(cctx->incomeQ, baseMsg, errbuf);
|
||||
if(isErr)
|
||||
{
|
||||
panic("Unable to dispatch client message: %s\n", errbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// int isErr = ptQueuePush(cctx->incomeQ, fbmsg, errbuf);
|
||||
ServerContext* sctx = ws_get_server_context(client);
|
||||
|
||||
// printf("got message with len %lu\n", size);
|
||||
|
||||
queue_mpsc_push(sctx->inMsgQueue, fbmsg);
|
||||
// if (!queue_spsc_push(cctx->incomeQ, fbmsg)) {
|
||||
// if (!queue_mpsc_push(sctx->inMsgQueue, fbmsg)) {
|
||||
// free(fbmsg->data);
|
||||
// free(fbmsg);
|
||||
// panic("Unable to queue client message: %s\n", errbuf);
|
||||
// }
|
||||
}
|
||||
|
||||
435
src/proto/msg.c
435
src/proto/msg.c
@@ -1,85 +1,404 @@
|
||||
#include "proto/msg.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "proto/enums.h"
|
||||
#include "proto/pack.h"
|
||||
#include "flatcc/flatcc_builder.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 "compacted_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;
|
||||
BaseMessage* msg = malloc(sizeof(BaseMessage));
|
||||
NULL_GUARD(msg);
|
||||
uint64_t nonce = decodeBytesToU64(bytes);
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
}
|
||||
|
||||
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;
|
||||
msg->payloadHeader = bytes[8] & 0b1111;
|
||||
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
|
||||
|
||||
msg->payloadLen = size - headerSize;
|
||||
|
||||
uint8_t* payload = malloc(sizeof(uint8_t) * (msg->payloadLen));
|
||||
NULL_GUARD(payload);
|
||||
|
||||
memcpy(payload, bytes + headerSize, msg->payloadLen);
|
||||
msg->payload = payload;
|
||||
|
||||
return msg;
|
||||
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
||||
flatcc_builder_clear(&B);
|
||||
NULL_GUARD(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
|
||||
NULL_GUARD(outmsg, "Unable to allocate message");
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
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;
|
||||
outmsg[8] |= CTRL_TYPE_NOTIF_STATE;
|
||||
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
||||
hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
|
||||
|
||||
outmsg[9] = NOTIF_TYPE_EXEC;
|
||||
encodeUintToBytes(clockCounter, outmsg + 10);
|
||||
outmsg[18] = newEmulState;
|
||||
return outmsg;
|
||||
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
||||
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
||||
|
||||
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;
|
||||
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
|
||||
NULL_GUARD(outmsg);
|
||||
flatcc_builder_reset(B);
|
||||
|
||||
encodeUintToBytes(nonce, outmsg);
|
||||
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (X << 3) | STREAM_TYPE_REG_CONFIRM);
|
||||
encodeUintToBytes(devId, outmsg + 9);
|
||||
encodeUintToBytes(segId, outmsg + 9 + 8);
|
||||
encodeUintToBytes(startAddr, outmsg + 9 + 8 + 8);
|
||||
encodeUintToBytes(segLength, outmsg + 9 + 8 + 8 + 8);
|
||||
encodeUintToBytes(regId, outmsg + 9 + 8 + 8 + 8 + 8);
|
||||
return outmsg;
|
||||
flatbuffers_uint8_vec_ref_t data_vec =
|
||||
flatbuffers_uint8_vec_create(B, data, data_len);
|
||||
|
||||
hmmmm_stream_StreamDataPush_ref_t push =
|
||||
hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
|
||||
|
||||
hmmmm_stream_StreamServerPayload_union_ref_t stream_payload =
|
||||
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;
|
||||
|
||||
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
|
||||
NULL_GUARD(outmsg);
|
||||
|
||||
uint64_t nonce = (uint64_t)~0;
|
||||
encodeUintToBytes(nonce, outmsg);
|
||||
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (mode << 3) | STREAM_TYPE_SEND);
|
||||
|
||||
encodeUintToBytes(regId, outmsg + 9);
|
||||
encodeUintToBytes(clockCounter, outmsg + 9 + 4);
|
||||
memcpy(outmsg + 9 + 4 + 8, payload, payloadLen);
|
||||
|
||||
return outmsg;
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
}
|
||||
|
||||
hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb =
|
||||
hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks);
|
||||
|
||||
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
||||
hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb);
|
||||
|
||||
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
||||
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
uint8_t* fb_build_config_device_id_mapping(
|
||||
uint64_t nonce,
|
||||
char** paths[], size_t path_lens[],
|
||||
char* seg_names[][64], size_t seg_counts[],
|
||||
size_t device_count, size_t* len_out)
|
||||
{
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
}
|
||||
|
||||
// Pre-create DeviceIdEntry refs (bottom-up: strings first, then tables)
|
||||
hmmmm_ctrl_config_notif_DeviceIdEntry_ref_t entry_refs[device_count];
|
||||
|
||||
for (size_t i = 0; i < device_count; i++) {
|
||||
// Build string vector for path
|
||||
flatbuffers_string_vec_start(&B);
|
||||
for (size_t j = 0; j < path_lens[i]; j++) {
|
||||
flatbuffers_string_ref_t sref = flatbuffers_string_create_str(&B, paths[i][j]);
|
||||
flatbuffers_string_vec_push(&B, sref);
|
||||
}
|
||||
flatbuffers_string_vec_ref_t path_vec = flatbuffers_string_vec_end(&B);
|
||||
|
||||
// Build SegIdEntry refs for this device's segments
|
||||
size_t nseg = seg_counts[i];
|
||||
hmmmm_ctrl_config_notif_SegIdEntry_ref_t seg_refs[nseg];
|
||||
for (size_t si = 0; si < nseg; si++) {
|
||||
flatbuffers_string_ref_t name_ref =
|
||||
flatbuffers_string_create_str(&B, seg_names[i][si] ? seg_names[i][si] : "");
|
||||
seg_refs[si] = hmmmm_ctrl_config_notif_SegIdEntry_create(
|
||||
&B, name_ref, (uint32_t)si);
|
||||
}
|
||||
|
||||
hmmmm_ctrl_config_notif_SegIdEntry_vec_start(&B);
|
||||
for (size_t si = 0; si < nseg; si++) {
|
||||
hmmmm_ctrl_config_notif_SegIdEntry_vec_push(&B, seg_refs[si]);
|
||||
}
|
||||
hmmmm_ctrl_config_notif_SegIdEntry_vec_ref_t seg_ids_vec =
|
||||
hmmmm_ctrl_config_notif_SegIdEntry_vec_end(&B);
|
||||
|
||||
entry_refs[i] = hmmmm_ctrl_config_notif_DeviceIdEntry_create(
|
||||
&B, path_vec, (uint32_t)i, seg_ids_vec);
|
||||
}
|
||||
|
||||
// Build entries vector
|
||||
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_start(&B);
|
||||
for (size_t i = 0; i < device_count; i++) {
|
||||
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_push(&B, entry_refs[i]);
|
||||
}
|
||||
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_ref_t entries_vec =
|
||||
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_end(&B);
|
||||
|
||||
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_ref_t mapping =
|
||||
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_create(&B, entries_vec);
|
||||
|
||||
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
|
||||
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_DeviceIdMappingNotif(mapping);
|
||||
|
||||
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
|
||||
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
|
||||
|
||||
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
||||
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
|
||||
|
||||
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
||||
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
||||
|
||||
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* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out)
|
||||
{
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
}
|
||||
|
||||
flatbuffers_string_ref_t msg_str = flatbuffers_string_create_str(&B, message);
|
||||
|
||||
hmmmm_ctrl_config_notif_ConfigLoadError_ref_t err =
|
||||
hmmmm_ctrl_config_notif_ConfigLoadError_create(&B, msg_str);
|
||||
|
||||
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
|
||||
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_ConfigLoadError(err);
|
||||
|
||||
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
|
||||
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
|
||||
|
||||
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
||||
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
|
||||
|
||||
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
||||
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
||||
|
||||
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* fb_build_compacted_message(
|
||||
uint8_t* const* msgs, const size_t* msg_lens, size_t count, size_t* len_out)
|
||||
{
|
||||
flatcc_builder_t B;
|
||||
if (flatcc_builder_init(&B)) {
|
||||
panic("flatcc_builder_init failed\n");
|
||||
}
|
||||
|
||||
// Build CompactedFrame refs bottom-up (FlatBuffers requires nested objects
|
||||
// to be created before the table that references them).
|
||||
hmmmm_CompactedFrame_ref_t frame_refs[count];
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
flatbuffers_uint8_vec_ref_t data_vec =
|
||||
flatbuffers_uint8_vec_create(&B, msgs[i], msg_lens[i]);
|
||||
frame_refs[i] = hmmmm_CompactedFrame_create(&B, data_vec);
|
||||
}
|
||||
|
||||
hmmmm_CompactedFrame_vec_start(&B);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
hmmmm_CompactedFrame_vec_push(&B, frame_refs[i]);
|
||||
}
|
||||
hmmmm_CompactedFrame_vec_ref_t frames_vec = hmmmm_CompactedFrame_vec_end(&B);
|
||||
|
||||
hmmmm_CompactedMessage_ref_t cm = hmmmm_CompactedMessage_create(&B, frames_vec);
|
||||
|
||||
hmmmm_ServerPayload_union_ref_t payload =
|
||||
hmmmm_ServerPayload_as_CompactedMessage(cm);
|
||||
|
||||
// UINT64_MAX = no-nonce (fire-and-forget batch frame)
|
||||
hmmmm_ServerMessage_create_as_root(&B, UINT64_MAX, payload);
|
||||
|
||||
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
||||
flatcc_builder_clear(&B);
|
||||
NULL_GUARD(buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user