Skip to content

File syn_ccp.c

File List > proto > syn_ccp.c

Go to the documentation of this file

#include "syn_ccp.h"

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

#include <string.h>

void syn_ccp_init(SYN_CCP_Slave *slave, uint16_t station_addr)
{
    SYN_ASSERT(slave != NULL);

    memset(slave, 0, sizeof(*slave));
    slave->station_addr = station_addr;
    slave->connected = false;
    slave->unlocked_resources = SYN_CCP_RESOURCE_CAL | SYN_CCP_RESOURCE_DAQ | SYN_CCP_RESOURCE_PGM;
}

void syn_ccp_set_mta(SYN_CCP_Slave *slave, uint8_t mta_num, uint8_t ext, uintptr_t addr)
{
    if (slave == NULL) {
        return;
    }
    if (mta_num == 0) {
        slave->mta0_ext = ext;
        slave->mta0_addr = addr;
    } else if (mta_num == 1) {
        slave->mta1_ext = ext;
        slave->mta1_addr = addr;
    }
}

bool syn_ccp_process_cro(SYN_CCP_Slave *slave, const uint8_t cro[8], uint8_t dto_out[8])
{
    if (slave == NULL || cro == NULL || dto_out == NULL) {
        return false;
    }

    uint8_t cmd = cro[0];
    uint8_t ctr = cro[1];

    /* Prepare base CRM payload */
    memset(dto_out, 0, 8);
    dto_out[0] = SYN_CCP_PID_CRM;
    dto_out[1] = SYN_CCP_ERR_SUCCESS;
    dto_out[2] = ctr;

    /* Handle CONNECT */
    if (cmd == SYN_CCP_CMD_CONNECT) {
        uint16_t target_addr = syn_peek_u16_le(cro, 2);
        if (target_addr == slave->station_addr || target_addr == 0x0000U) {
            slave->connected = true;
            dto_out[1] = SYN_CCP_ERR_SUCCESS;
        } else {
            /* Station address mismatch */
            return false;
        }
        return true;
    }

    /* All commands after CONNECT require connected status */
    if (!slave->connected) {
        dto_out[1] = SYN_CCP_ERR_NOT_CONNECTED;
        return true;
    }

    switch (cmd) {
    case SYN_CCP_CMD_DISCONNECT: {
        uint8_t mode = cro[2];
        uint16_t target_addr = syn_peek_u16_le(cro, 3);
        if (target_addr == slave->station_addr || target_addr == 0x0000U) {
            if (mode == 0x00U || mode == 0x01U) {
                slave->connected = false;
            }
            dto_out[1] = SYN_CCP_ERR_SUCCESS;
        }
        break;
    }

    case SYN_CCP_CMD_GET_CCP_VERSION: {
        dto_out[3] = 0x02U; /* Main version 2 */
        dto_out[4] = 0x01U; /* Release version 1 */
        break;
    }

    case SYN_CCP_CMD_EXCHANGE_ID: {
        dto_out[3] = 0x08U;                     /* Slave device ID length */
        dto_out[4] = 0x00U;                     /* DataType */
        dto_out[5] = slave->unlocked_resources; /* Resource availability */
        dto_out[6] = slave->unlocked_resources; /* Protection status */
        break;
    }

    case SYN_CCP_CMD_GET_SEED: {
        uint8_t req_resource = cro[2];
        bool is_unlocked = (slave->unlocked_resources & req_resource) != 0U;
        dto_out[3] = is_unlocked ? 0x00U : 0x01U; /* 0: Unlocked, 1: Seed needed */
        uint32_t seed = 0U;
        if (slave->seed_cb != NULL) {
            seed = slave->seed_cb(req_resource, slave->seed_ctx);
        } else {
            seed = 0x5A5A5A5AU;
        }
        syn_poke_u32_le(seed, dto_out, 4);
        break;
    }

    case SYN_CCP_CMD_UNLOCK: {
        uint8_t resource = cro[2];
        uint32_t key = syn_peek_u32_le(cro, 4);
        bool valid = false;
        if (slave->unlock_cb != NULL) {
            valid = slave->unlock_cb(resource, key, slave->unlock_ctx);
        } else {
            valid = true;
        }
        if (valid) {
            slave->unlocked_resources |= resource;
        } else {
            dto_out[1] = SYN_CCP_ERR_ACCESS_DENIED;
        }
        dto_out[3] = slave->unlocked_resources;
        break;
    }

    case SYN_CCP_CMD_SET_MTA: {
        uint8_t mta_num = cro[2];
        uint8_t ext = cro[3];
        uint32_t addr = syn_peek_u32_le(cro, 4);
        if (addr != 0) {
            syn_ccp_set_mta(slave, mta_num, ext, (uintptr_t)addr);
        } else if (mta_num == 0) {
            slave->mta0_ext = ext;
        } else {
            slave->mta1_ext = ext;
        }
        break;
    }

    case SYN_CCP_CMD_DNLOAD: {
        uint8_t size = cro[2];
        if (size > 5) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        if (slave->mta0_addr != 0) {
            uint8_t *ptr = (uint8_t *)(uintptr_t)slave->mta0_addr;
            memcpy(ptr, &cro[3], size);
            slave->mta0_addr += size;
        }
        dto_out[3] = slave->mta0_ext;
        syn_poke_u32_le((uint32_t)slave->mta0_addr, dto_out, 4);
        break;
    }

    case SYN_CCP_CMD_DNLOAD_6: {
        if (slave->mta0_addr != 0) {
            uint8_t *ptr = (uint8_t *)(uintptr_t)slave->mta0_addr;
            memcpy(ptr, &cro[2], 6);
            slave->mta0_addr += 6;
        }
        dto_out[3] = slave->mta0_ext;
        syn_poke_u32_le((uint32_t)slave->mta0_addr, dto_out, 4);
        break;
    }

    case SYN_CCP_CMD_UPLOAD: {
        uint8_t size = cro[2];
        if (size > 5) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        if (slave->mta0_addr != 0) {
            const uint8_t *ptr = (const uint8_t *)(uintptr_t)slave->mta0_addr;
            memcpy(&dto_out[3], ptr, size);
            slave->mta0_addr += size;
        }
        break;
    }

    case SYN_CCP_CMD_SHORT_UP: {
        uint8_t size = cro[2];
        uint8_t ext = cro[3];
        uint32_t addr = syn_peek_u32_le(cro, 4);
        if (size > 5) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        slave->mta0_ext = ext;
        if (addr != 0) {
            slave->mta0_addr = (uintptr_t)addr;
        }
        if (slave->mta0_addr != 0) {
            const uint8_t *ptr = (const uint8_t *)(uintptr_t)slave->mta0_addr;
            memcpy(&dto_out[3], ptr, size);
            slave->mta0_addr += size;
        }
        break;
    }

    case SYN_CCP_CMD_SET_CAL_PAGE: {
        uint32_t page_addr = syn_peek_u32_le(cro, 2);
        slave->active_cal_page = (uint8_t)(page_addr & 0xFFU);
        break;
    }

    case SYN_CCP_CMD_GET_ACTIVE_CAL_PAGE: {
        dto_out[3] = 0x00U;
        syn_poke_u32_le((uint32_t)slave->active_cal_page, dto_out, 4);
        break;
    }

    case SYN_CCP_CMD_CLEAR_MEMORY: {
        uint32_t size = syn_peek_u32_le(cro, 2);
        if (slave->erase_cb != NULL) {
            if (!slave->erase_cb((uint32_t)slave->mta0_addr, size, slave->erase_ctx)) {
                dto_out[1] = SYN_CCP_ERR_ACCESS_DENIED;
            }
        }
        break;
    }

    case SYN_CCP_CMD_PROGRAM: {
        uint8_t size = cro[2];
        if (size > 5) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        if (slave->mta0_addr != 0) {
            uint8_t *ptr = (uint8_t *)(uintptr_t)slave->mta0_addr;
            memcpy(ptr, &cro[3], size);
            slave->mta0_addr += size;
        }
        dto_out[3] = slave->mta0_ext;
        syn_poke_u32_le(slave->mta0_addr, dto_out, 4);
        break;
    }

    case SYN_CCP_CMD_GET_DAQ_SIZE: {
        uint8_t daq_num = cro[2];
        if (daq_num >= SYN_CCP_MAX_DAQ_LISTS) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        slave->selected_daq = daq_num;
        dto_out[3] = SYN_CCP_MAX_ODT_PER_DAQ;           /* Number of ODTs */
        dto_out[4] = daq_num * SYN_CCP_MAX_ODT_PER_DAQ; /* First PID */
        break;
    }

    case SYN_CCP_CMD_SET_DAQ_SIZE: {
        uint8_t daq_num = cro[2];
        uint8_t count = cro[4];
        if (daq_num >= SYN_CCP_MAX_DAQ_LISTS || count > SYN_CCP_MAX_ODT_PER_DAQ) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        slave->daq_lists[daq_num].odt_count = count;
        slave->daq_lists[daq_num].running = false;
        dto_out[3] = count;
        dto_out[4] = daq_num * SYN_CCP_MAX_ODT_PER_DAQ;
        break;
    }

    case SYN_CCP_CMD_BUILD_PKT: {
        uint8_t daq_num = cro[2];
        uint8_t odt_num = cro[3];
        uint8_t elem_idx = cro[4];
        uint8_t elem_size = cro[5];
        if (daq_num >= SYN_CCP_MAX_DAQ_LISTS || odt_num >= SYN_CCP_MAX_ODT_PER_DAQ ||
            elem_idx >= SYN_CCP_MAX_ENTRIES_PER_ODT) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        SYN_CCP_ODT *odt = &slave->daq_lists[daq_num].odts[odt_num];
        odt->entries[elem_idx].address = slave->mta0_addr;
        odt->entries[elem_idx].size = elem_size;
        if (elem_idx + 1 > odt->entry_count) {
            odt->entry_count = elem_idx + 1;
        }
        break;
    }

    case SYN_CCP_CMD_START_STOP: {
        uint8_t mode = cro[2];
        uint8_t daq_num = cro[3];
        uint8_t event_ch = cro[5];
        uint16_t prescaler = syn_peek_u16_le(cro, 6);
        if (daq_num >= SYN_CCP_MAX_DAQ_LISTS) {
            dto_out[1] = SYN_CCP_ERR_PARAM_OUT_OF_RANGE;
            break;
        }
        SYN_CCP_DAQList *daq = &slave->daq_lists[daq_num];
        daq->event_channel = event_ch;
        daq->prescaler = (prescaler == 0) ? 1 : prescaler;
        daq->cycle_counter = 0;
        daq->current_odt_idx = 0;
        daq->running = (mode == 0x01U);
        break;
    }

    case SYN_CCP_CMD_START_STOP_ALL: {
        uint8_t mode = cro[2];
        for (uint8_t i = 0; i < SYN_CCP_MAX_DAQ_LISTS; i++) {
            slave->daq_lists[i].running = (mode == 0x01U);
        }
        break;
    }

    default:
        dto_out[1] = SYN_CCP_ERR_CMD_UNKNOWN;
        break;
    }

    return true;
}

bool syn_ccp_service_daq(SYN_CCP_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;
    }

    if (!slave->connected) {
        return false;
    }

    for (uint8_t i = 0; i < SYN_CCP_MAX_DAQ_LISTS; i++) {
        SYN_CCP_DAQList *daq = &slave->daq_lists[i];
        if (!daq->running || daq->event_channel != event_channel) {
            continue;
        }

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

        uint8_t current_odt = daq->current_odt_idx;
        if (current_odt >= daq->odt_count) {
            daq->current_odt_idx = 0;
            current_odt = 0;
        }

        if (daq->odt_count == 0) {
            continue;
        }

        SYN_CCP_ODT *odt = &daq->odts[current_odt];

        memset(dto_out, 0, 8);
        dto_out[0] = i * SYN_CCP_MAX_ODT_PER_DAQ + current_odt; /* Packet ID = ODT index */

        uint8_t payload_pos = 1;
        for (uint8_t e = 0; e < odt->entry_count; e++) {
            SYN_CCP_ODTEntry *entry = &odt->entries[e];
            if (entry->address != 0 && entry->size > 0) {
                const uint8_t *src = (const uint8_t *)(uintptr_t)entry->address;
                for (uint8_t b = 0; b < entry->size && payload_pos < 8; b++) {
                    dto_out[payload_pos++] = src[b];
                }
            }
        }

        *list_idx = i;
        *odt_idx = current_odt;

        daq->current_odt_idx++;
        if (daq->current_odt_idx >= daq->odt_count) {
            daq->current_odt_idx = 0;
        }

        return true;
    }

    return false;
}