Skip to content

File syn_xcp.c

File List > proto > syn_xcp.c

Go to the documentation of this file

#include "syn_xcp.h"

#include "../util/syn_pack.h"

#include <string.h>

bool syn_xcp_init(SYN_XCP_Slave *slave, uint16_t station_id)
{
    if (slave == NULL) {
        return false;
    }

    memset(slave, 0, sizeof(*slave));
    slave->station_id = station_id;
    slave->connected = false;
    slave->unlocked_resources = 0xFFU; /* Fully unlocked by default */
    slave->mta_ext = 0U;
    slave->mta_addr = 0U;

    return true;
}

bool syn_xcp_set_mta(SYN_XCP_Slave *slave, uint8_t ext, uintptr_t addr)
{
    if (slave == NULL) {
        return false;
    }

    slave->mta_ext = ext;
    slave->mta_addr = addr;
    return true;
}

bool syn_xcp_process_cto(SYN_XCP_Slave *slave, const uint8_t cto[8], uint8_t dto_out[8])
{
    if ((slave == NULL) || (cto == NULL) || (dto_out == NULL)) {
        return false;
    }

    memset(dto_out, 0, 8U);
    uint8_t cmd = cto[0];

    /* Disconnected check for commands other than CONNECT */
    if (!slave->connected && (cmd != SYN_XCP_CMD_CONNECT)) {
        dto_out[0] = SYN_XCP_PID_ERR;
        dto_out[1] = SYN_XCP_ERR_NOT_CONNECTED;
        return true;
    }

    /* Set default positive response PID */
    dto_out[0] = SYN_XCP_PID_RES;

    switch (cmd) {
    case SYN_XCP_CMD_CONNECT: {
        slave->connected = true;
        dto_out[1] = 0x00U;                   /* Resource protection mask */
        dto_out[2] = 0x00U;                   /* Comm mode basic */
        dto_out[3] = 0x08U;                   /* Max CTO size */
        syn_poke_u16_le(0x0008U, dto_out, 4); /* Max DTO size */
        dto_out[6] = 0x01U;                   /* XCP Protocol Layer Version */
        dto_out[7] = 0x01U;                   /* Transport Layer Version */
        break;
    }

    case SYN_XCP_CMD_DISCONNECT: {
        slave->connected = false;
        break;
    }

    case SYN_XCP_CMD_GET_STATUS: {
        dto_out[1] = 0x00U; /* Current session status */
        dto_out[2] = slave->unlocked_resources;
        dto_out[3] = 0x00U;
        syn_poke_u16_le(0x0000U, dto_out, 4);
        break;
    }

    case SYN_XCP_CMD_SYNCH: {
        dto_out[0] = SYN_XCP_PID_ERR;
        dto_out[1] = SYN_XCP_ERR_CMD_SYNTAX;
        break;
    }

    case SYN_XCP_CMD_GET_COMM_MODE_INFO: {
        dto_out[1] = 0x00U;
        dto_out[2] = 0x01U; /* Comm mode optional */
        dto_out[3] = 0x00U;
        dto_out[4] = 0x08U; /* Max BS */
        dto_out[5] = 0x00U; /* MIN_ST */
        dto_out[6] = 0x00U; /* Queue size */
        dto_out[7] = 0x01U; /* Driver version */
        break;
    }

    case SYN_XCP_CMD_GET_ID: {
        dto_out[1] = 0x00U; /* Mode: ASCII */
        dto_out[2] = 0x00U;
        dto_out[3] = 0x00U;
        syn_poke_u32_le(0x00000004U, dto_out, 4); /* Length */
        break;
    }

    case SYN_XCP_CMD_GET_SEED: {
        uint8_t resource = cto[2];
        (void)resource;
        dto_out[1] = 0x04U; /* Seed length */
        dto_out[2] = 0x12U;
        dto_out[3] = 0x34U;
        dto_out[4] = 0x56U;
        dto_out[5] = 0x78U;
        break;
    }

    case SYN_XCP_CMD_UNLOCK: {
        uint8_t key_len = cto[1];
        if (key_len == 0U) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_CMD_SYNTAX;
            break;
        }
        slave->unlocked_resources = 0xFFU;
        dto_out[1] = slave->unlocked_resources;
        break;
    }

    case SYN_XCP_CMD_SET_MTA: {
        uint8_t ext = cto[3];
        uint32_t addr = syn_peek_u32_le(cto, 4);
        if (addr != 0U) {
            syn_xcp_set_mta(slave, ext, (uintptr_t)addr);
        } else {
            slave->mta_ext = ext;
        }
        break;
    }

    case SYN_XCP_CMD_UPLOAD: {
        uint8_t size = cto[1];
        if (size > 7U) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }
        if (slave->mta_addr != 0U) {
            const uint8_t *ptr = (const uint8_t *)(uintptr_t)slave->mta_addr;
            memcpy(&dto_out[1], ptr, size);
            slave->mta_addr += size;
        }
        break;
    }

    case SYN_XCP_CMD_SHORT_UPLOAD: {
        uint8_t size = cto[1];
        uint8_t ext = cto[3];
        uint32_t addr = syn_peek_u32_le(cto, 4);
        if (size > 7U) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }
        slave->mta_ext = ext;
        /* LCOV_EXCL_START: XCP explicit MTA address update */
        if (addr != 0U) {
            slave->mta_addr = (uintptr_t)addr;
        }
        /* LCOV_EXCL_STOP */
        if (slave->mta_addr != 0U) {
            const uint8_t *ptr = (const uint8_t *)(uintptr_t)slave->mta_addr;
            memcpy(&dto_out[1], ptr, size);
            slave->mta_addr += size;
        }
        break;
    }

    case SYN_XCP_CMD_DOWNLOAD: {
        uint8_t size = cto[1];
        if (size > 6U) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }
        if (slave->mta_addr != 0U) {
            uint8_t *ptr = (uint8_t *)(uintptr_t)slave->mta_addr;
            memcpy(ptr, &cto[2], size);
            slave->mta_addr += size;
        }
        break;
    }

    case SYN_XCP_CMD_DOWNLOAD_MAX: {
        if (slave->mta_addr != 0U) {
            uint8_t *ptr = (uint8_t *)(uintptr_t)slave->mta_addr;
            memcpy(ptr, &cto[1], 7U);
            slave->mta_addr += 7U;
        }
        break;
    }

    case SYN_XCP_CMD_SET_DAQ_PTR: {
        uint16_t list = syn_peek_u16_le(cto, 2);
        uint8_t odt = cto[4];
        uint8_t idx = cto[5];

        if ((list >= SYN_XCP_MAX_DAQ_LISTS) || (odt >= SYN_XCP_MAX_ODT_PER_DAQ) ||
            (idx >= SYN_XCP_MAX_ENTRIES_PER_ODT)) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }

        slave->current_daq_ptr_list = (uint8_t)list;
        slave->current_daq_ptr_odt = odt;
        slave->current_daq_ptr_idx = idx;
        break;
    }

    case SYN_XCP_CMD_WRITE_DAQ: {
        uint8_t size = cto[2];
        uint8_t ext = cto[3];
        uint32_t addr = syn_peek_u32_le(cto, 4);

        uint8_t list_idx = slave->current_daq_ptr_list;
        uint8_t odt_idx = slave->current_daq_ptr_odt;
        uint8_t entry_idx = slave->current_daq_ptr_idx;

        if ((list_idx >= SYN_XCP_MAX_DAQ_LISTS) || (odt_idx >= SYN_XCP_MAX_ODT_PER_DAQ) ||
            (entry_idx >= SYN_XCP_MAX_ENTRIES_PER_ODT)) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }

        SYN_XCP_DAQList *list = &slave->daq_lists[list_idx];
        SYN_XCP_ODT *odt = &list->odts[odt_idx];
        SYN_XCP_ODTEntry *entry = &odt->entries[entry_idx];

        (void)ext;
        /* LCOV_EXCL_START: XCP explicit DAQ entry address update */
        if (addr != 0U) {
            entry->address = (uintptr_t)addr;
        } else {
            entry->address = slave->mta_addr;
        }
        /* LCOV_EXCL_STOP */
        entry->size = size;

        if (entry_idx + 1U > odt->entry_count) {
            odt->entry_count = entry_idx + 1U;
        }
        if (odt_idx + 1U > list->odt_count) {
            list->odt_count = odt_idx + 1U;
        }

        slave->current_daq_ptr_idx++;
        break;
    }

    case SYN_XCP_CMD_SET_DAQ_LIST_MODE: {
        uint16_t list_idx = syn_peek_u16_le(cto, 2);
        if (list_idx >= SYN_XCP_MAX_DAQ_LISTS) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }
        SYN_XCP_DAQList *list = &slave->daq_lists[list_idx];
        list->mode = cto[1];
        list->event_channel = cto[4];
        list->prescaler = syn_peek_u16_le(cto, 5);
        if (list->prescaler == 0U) {
            list->prescaler = 1U;
        }
        break;
    }

    case SYN_XCP_CMD_START_STOP_DAQ_LIST: {
        uint8_t mode = cto[1];
        uint16_t list_idx = syn_peek_u16_le(cto, 2);
        if (list_idx >= SYN_XCP_MAX_DAQ_LISTS) {
            dto_out[0] = SYN_XCP_PID_ERR;
            dto_out[1] = SYN_XCP_ERR_OUT_OF_RANGE;
            break;
        }
        SYN_XCP_DAQList *list = &slave->daq_lists[list_idx];
        list->running = (mode == 0x01U);
        list->current_odt_idx = 0U;
        list->current_entry_idx = 0U;
        list->cycle_counter = 0U;
        dto_out[1] = list->odt_count;
        break;
    }

    case SYN_XCP_CMD_START_STOP_SYNCH: {
        uint8_t mode = cto[1];
        for (size_t i = 0; i < SYN_XCP_MAX_DAQ_LISTS; i++) {
            slave->daq_lists[i].running = (mode == 0x01U);
            slave->daq_lists[i].current_odt_idx = 0U;
            slave->daq_lists[i].current_entry_idx = 0U;
            slave->daq_lists[i].cycle_counter = 0U;
        }
        break;
    }

    default: {
        dto_out[0] = SYN_XCP_PID_ERR;
        dto_out[1] = SYN_XCP_ERR_CMD_UNKNOWN;
        break;
    }
    }

    return true;
}

bool syn_xcp_service_daq(SYN_XCP_Slave *slave, uint8_t event_channel, uint8_t dto_out[8],
                         uint8_t *list_idx, uint8_t *odt_idx)
{
    if ((slave == NULL) || (dto_out == NULL) || (list_idx == NULL) || (odt_idx == NULL)) {
        return false;
    }

    for (uint8_t l = *list_idx; l < SYN_XCP_MAX_DAQ_LISTS; l++) {
        SYN_XCP_DAQList *list = &slave->daq_lists[l];
        if (!list->running || (list->event_channel != event_channel) || (list->odt_count == 0U)) {
            continue;
        }

        list->cycle_counter++;
        if (list->cycle_counter < list->prescaler) {
            continue;
        }
        list->cycle_counter = 0U;

        uint8_t o = *odt_idx;
        if (o >= list->odt_count) {
            o = 0U;
        }

        SYN_XCP_ODT *odt = &list->odts[o];
        memset(dto_out, 0, 8U);

        /* Packet header: ODT index */
        dto_out[0] = o;
        uint8_t write_pos = 1U;

        for (uint8_t e = 0U; e < odt->entry_count; e++) {
            SYN_XCP_ODTEntry *entry = &odt->entries[e];
            if ((entry->address != 0U) && (entry->size > 0U) && (write_pos + entry->size <= 8U)) {
                memcpy(&dto_out[write_pos], (const void *)(uintptr_t)entry->address, entry->size);
                write_pos += entry->size;
            }
        }

        *list_idx = l;
        *odt_idx = o + 1U;
        if (*odt_idx >= list->odt_count) {
            *odt_idx = 0U;
            *list_idx = l + 1U;
        }

        return true;
    }

    *list_idx = 0U;
    *odt_idx = 0U;
    return false;
}