Channel Configuration Pattern
The framework provides configuration structures for each channel type. Users fill in these configurations with OS-specific parameters, then the framework creates channels and integrates them with vital_channel.
Key Principle:** Framework defines the interface, user provides the parameters.
Supported Channel Types
1. Shared Memory Channels
Use:** Ultra-low latency, same CPU/board
OS-Specific Implementation Areas:**
- POSIX: mmap() to shared memory region
- QNX: mmap() or QNX shared memory
- Linux: memfd_create() or /dev/shm files
RTOS: Memory mapping or inter-task memory
Configuration Structure:**
typedef struct {
const char *name;
const char *descriptor_path;
size_t message_size;
size_t queue_depth;
uint32_t timeout_ms;
} rte_ipc_config_shm_t;
User Configuration Example:**
rte_ipc_config_shm_t shm_config = {
.name = "vital_A_to_B",
.descriptor_path = "/dev/shm/rail_vital_ab",
.queue_depth = 10,
.timeout_ms = 100,
};
rte_ipc_create_shm(&shm_channel, &shm_config);
struct rte_ipc_impl_s * rte_ipc_handle_t
Opaque handle to a created IPC channel, returned by rte_ipc_create().
Implementation by OS Integrator (NOT in framework):**
const rte_ipc_config_shm_t *config)
{
int fd = shm_open(config->descriptor_path, O_CREAT | O_RDWR, 0666);
if (fd < 0) {
}
size_t total_size = config->queue_depth * config->message_size;
ftruncate(fd, total_size);
void *mapped = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&q->mutex, &attr);
}
rte_status_t
Common result/status codes.
@ RTE_STATUS_HARDWARE_FAULT
2. FIFO (Named Pipes) Channels
Use:** Process isolation on same system, fallback transport
OS-Specific Implementation Areas:**
- POSIX: mkfifo() and open()
- Linux: /proc filesystem for FIFOs
- Windows: Named pipes (CreateNamedPipe())
RTOS: Message queues (if no FIFO support)
Configuration Structure:**
typedef struct {
const char *name;
const char *fifo_path;
size_t message_size;
uint32_t timeout_ms;
bool blocking;
} rte_ipc_config_fifo_t;
User Configuration Example:**
rte_ipc_config_fifo_t fifo_config = {
.name = "vital_A_to_B_fallback",
.fifo_path = "/tmp/rail_vital_ab_fifo",
.timeout_ms = 500,
.blocking = true,
};
rte_ipc_create_fifo(&fifo_channel, &fifo_config);
Implementation by OS Integrator (example):**
const rte_ipc_config_fifo_t *config)
{
if (access(config->fifo_path, F_OK) != 0) {
if (mkfifo(config->fifo_path, 0666) < 0) {
}
}
int flags = config->blocking ? 0 : O_NONBLOCK;
int fd = open(config->fifo_path, O_RDWR | flags);
if (fd < 0) {
}
fifo_channel_t *ch = malloc(sizeof(fifo_channel_t));
ch->fd = fd;
ch->message_size = config->message_size;
ch->timeout_ms = config->timeout_ms;
}
3. TCP/IP Channels
Use:** Remote standby across network, reliable ordered delivery
OS-Specific Implementation Areas:**
- POSIX/Linux: BSD sockets (socket(), connect(), send())
- Windows: Winsock2
RTOS: Native TCP/IP stack or lwIP
Configuration Structure:**
typedef struct {
const char *name;
const char *remote_ip;
uint16_t remote_port;
size_t message_size;
uint32_t timeout_ms;
bool server_mode;
} rte_ipc_config_tcp_t;
User Configuration Example (Client/Connect):**
rte_ipc_config_tcp_t tcp_config = {
.name = "online_to_standby",
.remote_ip = "192.168.1.100",
.remote_port = 5000,
.timeout_ms = 1000,
.server_mode = false,
};
rte_ipc_create_tcp(&tcp_channel, &tcp_config);
User Configuration Example (Server/Listen):**
rte_ipc_config_tcp_t tcp_server_config = {
.name = "standby_listen",
.remote_ip = "0.0.0.0",
.remote_port = 5000,
.timeout_ms = 1000,
.server_mode = true,
};
rte_ipc_create_tcp(&tcp_server, &tcp_server_config);
Implementation by OS Integrator (example):**
const rte_ipc_config_tcp_t *config)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
}
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(config->remote_port),
};
if (inet_pton(AF_INET, config->remote_ip, &addr.sin_addr) <= 0) {
close(sock);
}
if (config->server_mode) {
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
}
listen(sock, 1);
} else {
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
}
}
ch->socket = sock;
ch->timeout_ms = config->timeout_ms;
}
@ RTE_STATUS_INVALID_PARAM
4. UDP Channels
Use:** Low-latency network communication, best-effort
Configuration Structure:**
typedef struct {
const char *name;
const char *local_ip;
uint16_t local_port;
const char *remote_ip;
uint16_t remote_port;
size_t message_size;
uint32_t timeout_ms;
} rte_ipc_config_udp_t;
User Configuration Example:**
rte_ipc_config_udp_t udp_config = {
.name = "fast_heartbeat",
.local_ip = "192.168.1.50",
.local_port = 5001,
.remote_ip = "192.168.1.100",
.remote_port = 5001,
.message_size = sizeof(heartbeat_t),
.timeout_ms = 0,
};
rte_ipc_create_udp(&udp_channel, &udp_config);
Creating Channels for Vital Communication
Example: Online/Standby via TCP/IP
Online Host Configuration:**
rte_ipc_config_tcp_t tcp_config = {
.name = "online_to_standby_tcp",
.remote_ip = "192.168.1.100",
.remote_port = 5000,
.message_size = 256,
.timeout_ms = 1000,
.server_mode = false,
};
rte_ipc_config_udp_t udp_config = {
.name = "online_to_standby_udp",
.local_ip = "192.168.1.50",
.local_port = 5001,
.remote_ip = "192.168.1.100",
.remote_port = 5001,
.message_size = 256,
.timeout_ms = 500,
};
rte_ipc_create_tcp(&tcp_ch, &tcp_config);
rte_ipc_create_udp(&udp_ch, &udp_config);
.channel_timeout_ms = 1000,
.log_disagreements = true,
.backend_send = tcp_udp_dispatch_send,
.backend_recv = tcp_udp_dispatch_recv,
};
void *channels[2] = { &tcp_ch, &udp_ch };
rte_status_t rte_channel_init(rte_channel_storage_t *storage, const rte_channel_config_t *config)
Initializes one vital channel over a caller-supplied transport.
Configuration for rte_channel_init().
Standby Host Configuration:**
rte_ipc_config_tcp_t tcp_server_config = {
.name = "standby_listen_tcp",
.remote_ip = "0.0.0.0",
.remote_port = 5000,
.message_size = 256,
.timeout_ms = 1000,
.server_mode = true,
};
rte_ipc_config_udp_t udp_server_config = {
.name = "standby_listen_udp",
.local_ip = "192.168.1.100",
.local_port = 5001,
.remote_ip = "192.168.1.50",
.remote_port = 5001,
.message_size = 256,
.timeout_ms = 500,
};
rte_ipc_create_tcp(&tcp_ch, &tcp_server_config);
rte_ipc_create_udp(&udp_ch, &udp_server_config);
Dispatcher Callback Pattern
Since users might mix different transports, they implement a dispatcher:
typedef enum {
CHANNEL_TYPE_TCP,
CHANNEL_TYPE_UDP,
CHANNEL_TYPE_SHM,
CHANNEL_TYPE_FIFO,
} channel_type_t;
typedef struct {
channel_type_t type;
union {
} handle;
switch (wrapper->type) {
case CHANNEL_TYPE_TCP:
return rte_ipc_send_tcp(wrapper->handle.tcp, data, size, 1000);
case CHANNEL_TYPE_UDP:
return rte_ipc_send_udp(wrapper->handle.udp, data, size, 500);
case CHANNEL_TYPE_SHM:
return rte_ipc_send_shm(wrapper->handle.shm, data, size, 100);
case CHANNEL_TYPE_FIFO:
return rte_ipc_send_fifo(wrapper->handle.fifo, data, size, 500);
default:
}
}
switch (wrapper->type) {
case CHANNEL_TYPE_TCP:
return rte_ipc_recv_tcp(wrapper->handle.tcp, data, size, timeout);
case CHANNEL_TYPE_UDP:
return rte_ipc_recv_udp(wrapper->handle.udp, data, size, timeout);
}
}
static rte_status_t app_backend_send(void *channel, const void *data, size_t size)
Dispatcher send callback Routes to appropriate transport based on channel type.
static rte_status_t app_backend_recv(void *channel, void *data, size_t size, uint32_t timeout_ms)
Dispatcher receive callback Routes to appropriate transport based on channel type.
Configuration Parameter Summary
Shared Memory:**
- descriptor_path: Where to create shared memory (user specifies)
- message_size: Fixed message size (user specifies)
- queue_depth: How many messages to buffer (user specifies)
timeout_ms: Wait timeout (user specifies)
FIFO:**
- fifo_path: Path to FIFO file (user specifies)
- message_size: Fixed message size (user specifies)
- timeout_ms: Wait timeout (user specifies)
blocking: true/false (user specifies)
TCP/IP:**
- remote_ip: Destination IP address (user specifies)
- remote_port: Destination port (user specifies)
- message_size: Max message size (user specifies)
- timeout_ms: Connect/send/receive timeout (user specifies)
server_mode: Listen (true) or connect (false) (user specifies)
UDP:**
- local_ip: Bind address (user specifies)
- local_port: Bind port (user specifies)
- remote_ip: Send-to address (user specifies)
- remote_port: Send-to port (user specifies)
- message_size: Max datagram size (user specifies)
- timeout_ms: Non-blocking if 0 (user specifies)
Implementation Responsibility
Framework provides:**
- Configuration structures (what to configure)
- Vital channel voting layer
- Example dispatcher patterns
Interface definitions (what to implement)
OS Integrator implements:**
- rte_ipc_create_shm() - Create shared memory channel
- rte_ipc_create_fifo() - Create FIFO channel
- rte_ipc_create_tcp() - Create TCP channel
- rte_ipc_create_udp() - Create UDP channel
- rte_ipc_send_*() and rte_ipc_recv_*() - I/O operations
OSAdapter dispatcher callbacks (optional, user can write)
User/Integrator provides:**
- Configuration values (IPs, ports, paths)
- Dispatcher implementation (which channel for which transport)
- Channel lifecycle (create, use, destroy)
This separation ensures:
- ✓ Framework stays OS-agnostic
- ✓ Framework stays transport-agnostic
- ✓ Users have full control over configuration
- ✓ Easy to adapt to new OS/transports