First draft

This commit is contained in:
2025-11-07 15:57:11 +00:00
commit f306df9daf
13 changed files with 502 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#include "asn1_wrapper.h"
err_t incp_decode_header(BitStream *in_bs, IncpMsg *msg)
{
int asn1_err = 0;
if (!IncpMsg_Decode(msg, in_bs, &asn1_err))
return ERR_ERROR; //TODO: Proper error setting based on asn1_err
return ERR_SUCCESS;
}

View File

@@ -0,0 +1,23 @@
// asn1_wrapper.h - Wraps all ASN.1 generated files to conform with the rest of the C interface (return type to err_t
// and alike).
// Should only be used inside INCP implementation.
#pragma once
#ifndef _INCP_INTERNAL
#error "asn1_wrapper.h should not be imported outside the implementation of INCP"
#endif
#include "incp2.h"
#include "messaging.h"
#include "out/incp_static.h"
typedef IncpMsg_selection incp_msgt_t;
err_t incp_encode_header(BitStream *bs, IncpMsg *msg);
err_t incp_decode_header(BitStream *bs, IncpMsg *msg);
err_t incp_encode_datareq(BitStream *bs, U32 id);
err_t incp_decode_datareq(BitStream *bs, U32 id);
err_t incp_encode_dataret(BitStream *bs, AnyData *data);
err_t incp_decode_dataret(BitStream *bs, AnyData *data);

View File

@@ -0,0 +1,19 @@
INCP-Generated DEFINITIONS AUTOMATIC TAGS ::= BEGIN
EXPORTS ALL;
IMPORTS U32 FROM INCP-Types;
TTC-TxTotal ::= SEQUENCE {
bytes U32
}
TTC-RxTotal ::= SEQUENCE {
bytes U32
}
AnyData ::= CHOICE {
ttcTxTotal TTC-TxTotal,
ttcRxTotal TTC-RxTotal
}
END

View File

@@ -0,0 +1,51 @@
INCP-Types DEFINITIONS AUTOMATIC TAGS ::= BEGIN
EXPORTS ALL;
U8 ::= INTEGER(0..255)
U16 ::= INTEGER(0..65535)
U24 ::= INTEGER(8388608..16777215)
U32 ::= INTEGER(0..4294967295)
U64 ::= INTEGER(0..18446744073709551615)
I8 ::= INTEGER(-128..127)
I16 ::= INTEGER(-32768..32767)
I24 ::= INTEGER(-8388608..8388607)
I32 ::= INTEGER(-2147483648..2147483647)
END
INCP-Static DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS U8, U16, U32 FROM INCP-Types
AnyData FROM INCP-Generated;
PingMsg ::= SEQUENCE {
payload SEQUENCE (SIZE(32)) OF U8
}
DataReqMsg ::= SEQUENCE {
id U32 -- REVIEW: How to limit range to only accept IDs that are valid
}
DataRetMsg ::= SEQUENCE {
data AnyData
}
CommandReqMsg ::= SEQUENCE {
id U16
}
CommandRetMsg ::= SEQUENCE {
id U16
}
IncpMsg ::= CHOICE {
ping PingMsg,
pong PingMsg,
dataReq DataReqMsg,
dataRet DataRetMsg,
commandReq CommandReqMsg,
commandRet CommandRetMsg
}
END

View File

@@ -0,0 +1,11 @@
// incp2.h - Defines types common to all INCP interfaces.
#pragma once
typedef enum {
ERR_SUCCESS = 0,
ERR_ERROR,
ERR_NOT_IMPL,
ERR_NOT_MINE,
} err_t;

View File

@@ -0,0 +1,15 @@
// messaging.h - Defines structures used during (de)construction of messages.
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "incp2.h"
typedef int endpoint_t; //REVIEW: Figure this one out. Where the values come from, where they
// are defined, etc.
#define INCP_ENDPOINT_NVAL (~(endpoint_t)0)
#define INCP_ENDPOINT_TTC ((endpoint_t)1)
#define DEFAULT_MSGBUF_SIZE 64

View File

@@ -0,0 +1,177 @@
#define _INCP_INTERNAL
#include <string.h>
#include "incp2.h"
#include "service.h"
#include "messaging.h"
#include "asn1/asn1_wrapper.h"
static err_t handle_incoming(endpoint_t source, BitStream *in_bs);
void incp_receiver_task(void *args)
{
(void)args; //Stfu
while (1)
{
//LATER: Blocking wait for RTOS queue to fetch bus / radio message (go wild on that routing)
BitStream in_bs = { 0 }; // <-- Populate from transport
endpoint_t source = INCP_ENDPOINT_NVAL; // <-- Populate from transport
handle_incoming(source, &in_bs);
}
}
err_t incp_async_datareq(endpoint_t subsystem, U32 id)
{
err_t err; //Don't init on purpose
BitStream out_bs;
uint8_t buf[DEFAULT_MSGBUF_SIZE];
BitStream_Init(&out_bs, buf, sizeof(buf));
IncpMsg msg;
msg.kind = dataReq_PRESENT;
msg.u.dataReq.id = id;
err = incp_encode_header(&out_bs, &msg);
if (err != ERR_SUCCESS) goto _exit;
err = impl_transmit(subsystem, buf, BitStream_GetLength(&out_bs));
_exit:
return err;
}
err_t incp_async_ping(endpoint_t subsystem, uint8_t *bytes)
{
err_t err; //Don't init on purpose
BitStream out_bs;
uint8_t buf[DEFAULT_MSGBUF_SIZE];
BitStream_Init(&out_bs, buf, sizeof(buf));
IncpMsg msg;
msg.kind = ping_PRESENT;
memcpy(&msg.u.ping.payload, bytes != NULL ? bytes : "I am a very fancy ping payload!", 32);
err = incp_encode_header(&out_bs, &msg);
if (err != ERR_SUCCESS) goto _exit;
err = impl_transmit(subsystem, buf, BitStream_GetLength(&out_bs));
_exit:
return err;
}
static inline err_t handle_incoming_ping(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
{
(void)in_bs;
err_t err; //Don't init on purpose
BitStream out_bs;
uint8_t buf[DEFAULT_MSGBUF_SIZE];
BitStream_Init(&out_bs, buf, sizeof(buf));
IncpMsg msg = *in_msg;
msg.kind = pong_PRESENT;
err = incp_encode_header(&out_bs, &msg);
if (err != ERR_SUCCESS) goto _exit;
err = impl_transmit(source, buf, BitStream_GetLength(&out_bs)); //"Destroy a Ghast with a fireball"
_exit:
return err;
}
static inline err_t handle_incoming_pong(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
{
(void)in_bs;
err_t err; //Don't init on purpose
impl_callback_pong(source, in_msg);
err = ERR_SUCCESS;
_exit:
return err;
}
static inline err_t handle_incoming_datareq(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
{
err_t err; //Don't init on purpose
BitStream out_bs;
uint8_t buf[DEFAULT_MSGBUF_SIZE];
BitStream_Init(&out_bs, buf, sizeof(buf));
AnyData data = { 0 };
data.kind = in_msg->u.dataReq.id; //REVIEW: Dubious types
err = impl_populate_dataret(&data);
if (err != ERR_SUCCESS) goto _exit;
err = incp_encode_dataret(&out_bs, &data);
if (err != ERR_SUCCESS) goto _exit;
err = impl_transmit(source, buf, BitStream_GetLength(&out_bs));
_exit:
return err;
}
static inline err_t handle_incoming_dataret(endpoint_t source, IncpMsg *in_msg, BitStream *in_bs)
{
err_t err; //Don't init on purpose
AnyData data; //Careful with lifetime of this! Rust my beloved (never tried it)
err = incp_decode_dataret(in_bs, &data);
if (err != ERR_SUCCESS) goto _exit;
impl_callback_dataret(source, &data);
err = ERR_SUCCESS;
_exit:
return err;
}
static err_t handle_incoming(endpoint_t source, BitStream *in_bs)
{
err_t err; //Don't init on purpose
IncpMsg msg;
err = incp_decode_header(in_bs, &msg);
if (err != ERR_SUCCESS) goto _exit;
switch (msg.kind)
{
case ping_PRESENT:
err = handle_incoming_ping(source, &msg, in_bs);
break;
case pong_PRESENT:
err = handle_incoming_pong(source, &msg, in_bs);
break;
case dataReq_PRESENT:
err = handle_incoming_datareq(source, &msg, in_bs);
break;
case dataRet_PRESENT:
err = handle_incoming_dataret(source, &msg, in_bs);
break;
default:
err = ERR_NOT_IMPL;
break;
}
_exit:
return err;
}

View File

@@ -0,0 +1,75 @@
// serivce.h - Defines INCP service interface and functions to implement per subsystem.
#pragma once
#include "incp2.h"
#include "messaging.h"
#include "asn1/out/incp_generated.h"
// ==========================
// === RTOS SETUP RELATED ===
// ==========================
/// @brief [DO NOT CALL] Entry point for the receiver task of the INCP service.
/// @param args Not used
/// @remark Do not call directly, instead, pass it to the RTOS during task creation.
void incp_receiver_task(void *args);
// ========================
// === INCP SERVICE API ===
// ========================
/// @brief Sends a datareq message of a given ID to a given subsystem.
/// @param subsystem The subsystem to target.
/// @param id The ID of the variable/report to request.
/// @return ERR_SUCCESS if successfully transmitted, an adequate error otherwise.
err_t incp_async_datareq(endpoint_t subsystem, U32 id);
/// @brief Sends a ping message to a given subsystem.
/// @param subsystem The subsystem to target.
/// @param bytes The data to populate the ping with. Must NULL or contain 32 readable bytes.
/// @return ERR_SUCCESS if successfully transmitted, an adequate error otherwise.
err_t incp_async_ping(endpoint_t subsystem, uint8_t *bytes);
// ==================================
// === TO IMPLEMENT PER SUBSYSTEM ===
// ==================================
/// @brief Implement this function to send (through whatever means, that's a you problem) the given buffer to a given
/// destination.
/// @param destination Where to send the buffer.
/// @param buf The buffer to send.
/// @param size The length of the data in the buffer.
/// @return ERR_SUCCESS if successful, an adequate error otherwise.
/// @remark Do not __attribute__((weak)) to force linker error.
err_t impl_transmit(endpoint_t destination, void *buf, size_t size);
/// @brief Implement this function to populate msg_buf with the requested variables.
/// @param data The destination to write to. Includes the variable/report id, which should not be changed.
/// @return ERR_SUCCESS if successful, an adequate error otherwise.
/// @remark Do not __attribute__((weak)) to force linker error.
err_t impl_populate_dataret(AnyData *data);
// === Callbacks ===
/// @brief Implement this function as the callback handler to a dataret message. Can (but shoudln't) be empty.
/// @param source The subsystem that send the message.
/// @param data The buffer that holds the data. (See remarks)
/// @remark Do not copy this value by reference, it's validity is only ensured during the execution of this function.
/// @remark Do not __attribute__((weak)) to force linker error if undefined.
void impl_callback_dataret(endpoint_t source, AnyData *data);
/// @brief Implement this function as the callback handler to a pong message. Can (but shoudln't) be empty.
/// @param source The subsystem that send the message.
/// @param pong The received pong. (See remarks)
/// @remark Do not copy this value by reference, it's validity is only ensured during the execution of this function.
/// @remark Do not __attribute__((weak)) to force linker error if undefined.
void impl_callback_pong(endpoint_t source, PingMsg *pong);