Skip to content

File syn_lintp.c

File List > proto > syn_lintp.c

Go to the documentation of this file

#include "syn_lintp.h"

#if !defined(SYN_USE_LINTP) || SYN_USE_LINTP

#include <string.h>

void syn_lintp_init(SYN_LINTP_Link *link, uint8_t nad, uint8_t *rx_buf, size_t rx_size,
                    uint8_t *tx_buf, size_t tx_size)
{
    (void)tx_buf;
    (void)tx_size;

    if (link == NULL) {
        return;
    }

    memset(link, 0, sizeof(*link));
    link->nad = nad;
    link->padding_byte = 0xFF;
    link->rx_buf = rx_buf;
    link->rx_buf_size = rx_size;
    link->timer_n_as_ms = 1000;
    link->timer_n_cr_ms = 1000;
    link->rx_state = SYN_LINTP_STATE_IDLE;
    link->tx_state = SYN_LINTP_STATE_IDLE;
}

void syn_lintp_set_timeouts(SYN_LINTP_Link *link, uint32_t n_as_ms, uint32_t n_cr_ms)
{
    if (link != NULL) {
        link->timer_n_as_ms = (n_as_ms > 0) ? n_as_ms : 1000;
        link->timer_n_cr_ms = (n_cr_ms > 0) ? n_cr_ms : 1000;
    }
}

void syn_lintp_set_padding(SYN_LINTP_Link *link, uint8_t pad_byte)
{
    if (link != NULL) {
        link->padding_byte = pad_byte;
    }
}

SYN_Status syn_lintp_send(SYN_LINTP_Link *link, uint8_t nad, const uint8_t *payload, size_t len)
{
    if (link == NULL || payload == NULL || len == 0 || len > 4095) {
        return SYN_INVALID_PARAM;
    }

    if (link->tx_state != SYN_LINTP_STATE_IDLE) {
        return SYN_BUSY;
    }

    link->tx_nad = nad;
    link->tx_buf = payload;
    link->tx_total_len = len;
    link->tx_offset = 0;
    link->tx_sn = 1;
    link->tx_timer_ms = 0;

    /* Build first frame */
    memset(link->tx_frame, link->padding_byte, 8);
    link->tx_frame[0] = nad;

    if (len <= 6) {
        /* Single Frame (SF): Byte 0 = NAD, Byte 1 = 0x00 | len, Bytes 2..7 = payload */
        link->tx_frame[1] = (uint8_t)(SYN_LINTP_PCI_SF | (len & 0x0F));
        memcpy(&link->tx_frame[2], payload, len);
        link->tx_offset = len;
        link->tx_state = SYN_LINTP_STATE_IDLE; /* SF is queued in one shot */
    } else {
        /* First Frame (FF): Byte 0 = NAD, Byte 1 = 0x10 | (len >> 8), Byte 2 = len & 0xFF */
        link->tx_frame[1] = (uint8_t)(0x10 | ((len >> 8) & 0x0F));
        link->tx_frame[2] = (uint8_t)(len & 0xFF);
        memcpy(&link->tx_frame[3], payload, 5);
        link->tx_offset = 5;
        link->tx_state = SYN_LINTP_STATE_TRANSMITTING_CF;
    }

    link->tx_frame_pending = true;
    return SYN_OK;
}

bool syn_lintp_get_tx_frame(SYN_LINTP_Link *link, uint8_t out_frame[8])
{
    if (link == NULL || out_frame == NULL) {
        return false;
    }

    if (link->tx_frame_pending) {
        memcpy(out_frame, link->tx_frame, 8);
        link->tx_frame_pending = false;
        link->tx_timer_ms = 0;

        /* If more CF frames remain, format the next CF */
        if (link->tx_state == SYN_LINTP_STATE_TRANSMITTING_CF) {
            if (link->tx_offset < link->tx_total_len) {
                memset(link->tx_frame, link->padding_byte, 8);
                link->tx_frame[0] = link->tx_nad;
                link->tx_frame[1] = (uint8_t)(0x20 | (link->tx_sn & 0x0F));

                size_t bytes_to_copy = link->tx_total_len - link->tx_offset;
                if (bytes_to_copy > 6) {
                    bytes_to_copy = 6;
                }

                memcpy(&link->tx_frame[2], &link->tx_buf[link->tx_offset], bytes_to_copy);
                link->tx_offset += bytes_to_copy;
                link->tx_sn = (uint8_t)((link->tx_sn + 1) & 0x0F);
                link->tx_frame_pending = true;
            } else {
                link->tx_state = SYN_LINTP_STATE_IDLE;
            }
        }
        return true;
    }

    return false;
}

void syn_lintp_process_rx_frame(SYN_LINTP_Link *link, const uint8_t frame[8])
{
    if (link == NULL || frame == NULL) {
        return;
    }

    uint8_t frame_nad = frame[0];

    /* NAD Filtering: Ignore frames not destined for this node or functional/broadcast */
    if (frame_nad != link->nad && frame_nad != SYN_LINTP_NAD_FUNCTIONAL &&
        frame_nad != SYN_LINTP_NAD_BROADCAST) {
        return;
    }

    uint8_t pci_type = (frame[1] >> 4) & 0x0F;

    switch (pci_type) {
    case SYN_LINTP_PCI_SF: { /* Single Frame */
        size_t sf_len = frame[1] & 0x0F;
        if (sf_len == 0 || sf_len > 6 || link->rx_buf == NULL || sf_len > link->rx_buf_size) {
            return;
        }

        memcpy(link->rx_buf, &frame[2], sf_len);
        link->rx_total_len = sf_len;
        link->rx_offset = sf_len;
        link->rx_nad = frame_nad;
        link->rx_completed = true;
        link->rx_state = SYN_LINTP_STATE_IDLE;
        break;
    }

    case SYN_LINTP_PCI_FF: { /* First Frame */
        size_t ff_len = (size_t)(((frame[1] & 0x0F) << 8) | frame[2]);
        if (ff_len <= 6 || link->rx_buf == NULL || ff_len > link->rx_buf_size) {
            link->rx_state = SYN_LINTP_STATE_IDLE;
            return;
        }

        memcpy(link->rx_buf, &frame[3], 5);
        link->rx_total_len = ff_len;
        link->rx_offset = 5;
        link->rx_sn = 1;
        link->rx_nad = frame_nad;
        link->rx_completed = false;
        link->rx_timer_ms = 0;
        link->rx_state = SYN_LINTP_STATE_RECEIVING_CF;
        break;
    }

    case SYN_LINTP_PCI_CF: { /* Consecutive Frame */
        if (link->rx_state != SYN_LINTP_STATE_RECEIVING_CF) {
            return;
        }

        uint8_t sn = frame[1] & 0x0F;
        if (sn != link->rx_sn) { /* Sequence Number Mismatch Error */
            link->rx_state = SYN_LINTP_STATE_IDLE;
            link->rx_offset = 0;
            return;
        }

        size_t remaining = link->rx_total_len - link->rx_offset;
        size_t copy_bytes = (remaining > 6) ? 6 : remaining;

        if ((link->rx_offset + copy_bytes) <= link->rx_buf_size) {
            memcpy(&link->rx_buf[link->rx_offset], &frame[2], copy_bytes);
            link->rx_offset += copy_bytes;
        }

        link->rx_sn = (uint8_t)((link->rx_sn + 1) & 0x0F);
        link->rx_timer_ms = 0;

        if (link->rx_offset >= link->rx_total_len) {
            link->rx_completed = true;
            link->rx_state = SYN_LINTP_STATE_IDLE;
        }
        break;
    }

    default:
        break;
    }
}

ssize_t syn_lintp_receive(SYN_LINTP_Link *link, uint8_t *out_buf, size_t max_len)
{
    if (link == NULL || out_buf == NULL || max_len == 0) {
        return SYN_INVALID_PARAM;
    }

    if (!link->rx_completed) {
        return 0;
    }

    size_t copy_len = (link->rx_total_len < max_len) ? link->rx_total_len : max_len;
    memcpy(out_buf, link->rx_buf, copy_len);

    link->rx_completed = false;
    link->rx_offset = 0;
    link->rx_total_len = 0;

    return (ssize_t)copy_len;
}

void syn_lintp_step(SYN_LINTP_Link *link, uint32_t dt_ms)
{
    if (link == NULL) {
        return;
    }

    /* RX Inter-frame timeout (N_Cr) */
    if (link->rx_state == SYN_LINTP_STATE_RECEIVING_CF) {
        link->rx_timer_ms += dt_ms;
        if (link->rx_timer_ms >= link->timer_n_cr_ms) {
            link->rx_state = SYN_LINTP_STATE_IDLE;
            link->rx_offset = 0;
            link->rx_completed = false;
        }
    }

    /* TX Confirmation timeout (N_As) */
    if (link->tx_state != SYN_LINTP_STATE_IDLE || link->tx_frame_pending) {
        link->tx_timer_ms += dt_ms;
        if (link->tx_timer_ms >= link->timer_n_as_ms) {
            link->tx_state = SYN_LINTP_STATE_IDLE;
            link->tx_frame_pending = false;
            link->tx_offset = 0;
        }
    }
}

bool syn_lintp_is_tx_idle(const SYN_LINTP_Link *link)
{
    if (link == NULL) {
        return true;
    }
    return (link->tx_state == SYN_LINTP_STATE_IDLE && !link->tx_frame_pending);
}

#endif /* SYN_USE_LINTP */