Modules (src/modules/): - common/error: App_Error struct with structured detail union, error codes, app_error_print(); designed for future upgrade to preprocessor-generated location codes - media_ctrl: media device enumeration, topology query (entities/pads/links), link enable/disable via Media Controller API (/dev/media*) - v4l2_ctrl: control enumeration (with menu item fetching), get/set via V4L2 ext controls API, device discovery (/dev/video*) All modules use -std=c11 -D_GNU_SOURCE, build artifacts go to build/ only. Kernel-version-dependent constants guarded with #ifdef + #warning. CLI drivers (dev/cli/): - media_ctrl_cli: list, info, topology, set-link subcommands - v4l2_ctrl_cli: list, controls, get, set subcommands Docs (docs/cli/): - media_ctrl_cli.md and v4l2_ctrl_cli.md with usage, examples, and context within the video routing system Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.1 KiB
Conventions and Preferences
Language
- C11 throughout (
-std=c11) _GNU_SOURCEdefined via-D_GNU_SOURCEin all Makefiles — enables the full GNU/Linux feature set (POSIX 2008, BSD, GNU and Linux-specific extensions); do not define this in source files- Target platform: Linux (V4L2, epoll, etc. are Linux-specific)
Naming
| Kind | Convention | Example |
|---|---|---|
| Structs | Title_Snek_Case |
struct App_Error, struct Frame_Header |
| Enums (type) | Title_Snek_Case |
enum Error_Code |
| Enum values | CAPITAL_SNEK_CASE |
ERR_NONE, ERR_SYSCALL |
| Functions | lower_snek_case |
v4l2_ctrl_open(), app_error_print() |
| Local variables | lower_snek_case |
frame_count, device_fd |
Constants (#define, const) |
CAPITAL_SNEK_CASE |
MAX_FRAME_SIZE |
| Module-level singletons | lower_snek_case |
(treated as functions) |
Identifiers are prefixed with their module name where disambiguation is needed (e.g. v4l2_ctrl_, media_ctrl_, transport_).
Types
No typedefs for structs
Always use the struct keyword at the call site. This makes composite types unambiguous without inspecting declarations.
/* correct */
struct App_Error err = app_error_ok();
void process(struct Frame_Header *header);
/* wrong */
typedef struct { ... } App_Error;
typedefs are acceptable for
- Enums:
typedef enum Error_Code { ... } Error_Code;— allows using the name withoutenumkeyword - Scalar aliases where the underlying type is genuinely irrelevant to the caller (e.g.
typedef uint32_t Frame_Id;)
Formatting
- Indentation: tabs (display width 4)
- Braces: always use block braces, including single-statement bodies
/* correct */
if (err.code != ERR_NONE) {
return err;
}
/* wrong */
if (err.code != ERR_NONE)
return err;
- Quotes: double quotes for string literals (C standard)
Error Handling
Errors are returned as struct App_Error values. Functions that can fail return struct App_Error directly, or take an out-parameter for the result alongside it.
Structure
/* modules/common/error.h */
typedef enum Error_Code {
ERR_NONE = 0,
ERR_SYSCALL = 1, /* errno is meaningful */
ERR_INVALID = 2,
} Error_Code;
struct Syscall_Error_Detail {
int err_no;
};
struct Invalid_Error_Detail {
/* fields added as concrete cases arise */
};
struct App_Error {
Error_Code code;
const char *file; /* __FILE__ — future: replaced by generated location code */
int line; /* __LINE__ — future: replaced by generated location code */
union {
struct Syscall_Error_Detail syscall;
struct Invalid_Error_Detail invalid;
} detail;
};
Macros
#define APP_OK \
((struct App_Error){ .code = ERR_NONE })
#define APP_IS_OK(e) \
((e).code == ERR_NONE)
#define APP_ERROR(error_code, detail_field, ...) \
((struct App_Error){ \
.code = (error_code), \
.file = __FILE__, \
.line = __LINE__, \
.detail = { .detail_field = { __VA_ARGS__ } } \
})
#define APP_SYSCALL_ERROR() \
APP_ERROR(ERR_SYSCALL, syscall, .err_no = errno)
Presentation
Each error kind is handled in app_error_print(), which writes a human-readable description to stderr. A JSON writer (app_error_write_json()) will be added later and will use the same structured fields.
Future upgrade
When the custom preprocessor is available, __FILE__ and __LINE__ will be replaced by a generated numeric location code that uniquely identifies the error site in the project. JSON consumers will see the same struct shape either way.
Directory Structure
video-setup/
src/ - all C source files
modules/ - translation units; each has a .c and a Makefile
common/ - shared types (error, base definitions); no external dependencies
<module>/
node/ - video node entry point and integration (later)
include/ - public headers (.h files)
dev/ - development aids; not part of the final deliverable
cli/ - exploratory CLI drivers, one per module
experiments/ - freeform experiments
tests/ - automated tests (later)
Makefile - top-level build
Source files live in src/. Public headers live in include/. CLI drivers and experiments live in dev/. Nothing in dev/ is a dependency of anything in src/.
Module Structure
Each module has its source under src/modules/<name>/ and its public header under include/:
src/modules/<name>/
<name>.c - implementation
Makefile - builds a static object
include/
<name>.h - public API; minimal includes, no implementation details
The corresponding CLI driver lives at dev/cli/<name>_cli.c.
Build
- GNU make with tabs for recipe indentation
- Each module builds to a static object (
.o) - CLI drivers link the module object(s) they need
- No CDN or vendored sources; dependencies are system libraries (libc, Linux kernel headers)