632 lines
21 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file icom.c
* \unit icom
* \brief This is a simple internal communication module for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2026 Lamdonn.
********************************************************************************************************/
#include "icom.h"
/**
* \brief: ICOM header field
* \note: This size is used to store the flag, transmit channel count, receive channel count, and data length of the channel
*/
#define ICOM_FLAG_SIZE (4) /**< ICOM flag size */
#define ICOM_TCNT_SIZE (4) /**< ICOM transmit channel count size */
#define ICOM_RCNT_SIZE (4) /**< ICOM receive channel count size */
#define ICOM_DLEN_SIZE (4) /**< ICOM data length size */
/**
* \brief: ICOM header field index
* \note: This index is used to access the flag, transmit channel count, receive channel count, and data length of the channel
*/
#define ICOM_FLAG_INDEX (0)
#define ICOM_TCNT_INDEX (ICOM_FLAG_INDEX + ICOM_FLAG_SIZE)
#define ICOM_RCNT_INDEX (ICOM_TCNT_INDEX + ICOM_TCNT_SIZE)
#define ICOM_DLEN_INDEX (ICOM_RCNT_INDEX + ICOM_RCNT_SIZE)
#define ICOM_DATA_INDEX (ICOM_DLEN_INDEX + ICOM_DLEN_SIZE)
/**
* \brief: ICOM header size
* \note:
* | Offset | Field | Size | Description |
* | :----: | :---: | :--: | :--------- |
* | 0 | Flag | 4 | Channel flag |
* | 4 | Tcnt | 4 | Transmit channel count |
* | 8 | Rcnt | 4 | Receive channel count |
* | 12 | Dlen | 4 | Data length |
*/
#define ICOM_HEAD_SIZE (ICOM_FLAG_SIZE + ICOM_TCNT_SIZE + ICOM_RCNT_SIZE + ICOM_DLEN_SIZE)
/**
* \brief: ICOM channel flag
* \note: This flag is used to indicate the status of the channel
*/
#define ICOM_F_INIT ((uint8_t)0x12) /**< ICOM channel flag: initialized */
#define ICOM_F_BUSY ((uint8_t)0x34) /**< ICOM channel flag: busy */
#define ICOM_F_IDLE ((uint8_t)0x56) /**< ICOM channel flag: idle */
#define ICOM_F_ERROR ((uint8_t)0x78) /**< ICOM channel flag: error */
#define getTxBase(i) (icom->txconfig[i].base) /**< ICOM transmit channel base address */
#define getTxSize(i) (icom->txconfig[i].size) /**< ICOM transmit channel size */
#define getTxFlag(i) (getTxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM transmit channel flag */
#define getTxDlen(i) (getTxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM transmit channel data length */
#define getTxData(i) (getTxBase(i)[ICOM_DATA_INDEX]) /**< ICOM transmit channel data */
#define getTxTcnt(i) (getTxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM transmit channel count */
#define getTxRcnt(i) (getTxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM transmit channel receive count */
#define getRxBase(i) (icom->rxconfig[i].base) /**< ICOM receive channel base address */
#define getRxSize(i) (icom->rxconfig[i].size) /**< ICOM receive channel size */
#define getRxFlag(i) (getRxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM receive channel flag */
#define getRxDlen(i) (getRxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM receive channel data length */
#define getRxData(i) (getRxBase(i)[ICOM_DATA_INDEX]) /**< ICOM receive channel data */
#define getRxTcnt(i) (getRxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM receive channel count */
#define getRxRcnt(i) (getRxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM receive channel receive count */
#define getTxPrivate(i) (icom->txconfig[i].private) /**< ICOM transmit channel private data */
#define getRxPrivate(i) (icom->rxconfig[i].private) /**< ICOM receive channel private data */
#define getPrivateOnline(p) ((p)[0]) /**< ICOM private data: online */
#define getPrivateOffCnt(p) ((p)[1]) /**< ICOM private data: offline count */
#define getPrivatePCount(p) ((p)[2]) /**< ICOM private data: process count */
#define getPrivateCCount(p) ((p)[3]) /**< ICOM private data: complete count */
/**
* \brief: ICOM cache invalidate function
* \param addr: Cache address
* \param size: Cache size
*/
#define Cache_Inv(addr, size) do { if (icom->inv) (icom->inv)((addr), (size)); } while (0)
/**
* \brief: ICOM cache write-back invalidate function
* \param addr: Cache address
* \param size: Cache size
*/
#define Cache_wbInv(addr, size) do { if (icom->wbinv) (icom->wbinv)((addr), (size)); } while (0)
/**
* \brief: ICOM transmitter ready function
* \param icom: ICOM handle
* \param channel: Channel number
* \return: 1 means ready, 0 means not ready
*/
static int icom_transmitter_ready(ICOM *icom, uint16_t channel)
{
int ret = 0;
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
switch (getRxFlag(channel))
{
case ICOM_F_INIT:
{
getRxFlag(channel) = ICOM_F_IDLE;
Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE);
ret = 1;
} break;
case ICOM_F_IDLE:
case ICOM_F_BUSY:
{
ret = 1;
} break;
default:
{
ret = 0;
} break;
}
return ret;
}
/**
* \brief: ICOM set data length function
* \param icom: ICOM handle
* \param channel: Channel number
* \param length: Data length
*/
static void icom_set_data_length(ICOM *icom, uint16_t channel, uint32_t length)
{
/* Data length is stored in little-endian format */
getTxBase(channel)[ICOM_DLEN_INDEX] = length & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 1] = (length >> 8) & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 2] = (length >> 16) & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 3] = (length >> 24) & 0xFF;
Cache_wbInv(&(getTxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE);
}
/**
* \brief: ICOM get data length function
* \param icom: ICOM handle
* \param channel: Channel number
* \return: Data length
*/
static uint32_t icom_get_data_length(ICOM *icom, uint16_t channel)
{
uint32_t length = 0;
Cache_Inv(&(getRxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE);
/* Data length is stored in little-endian format */
length = (uint32_t)((getRxBase(channel)[ICOM_DLEN_INDEX]) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 1] << 8) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 2] << 16) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 3] << 24));
return length;
}
/**
* \brief: ICOM control online function
* \param icom: ICOM handle
* \param private: Private data
* \param count: Process count
* \note: If process count is equal to complete count, then set online to 1.
* If offline count is less than 5, then set offline count to 1.
* Otherwise, set online to 0.
* If online is 1, then set offline count to 0.
* If offline count is greater than 5, then set online to 0.
*/
static void icom_control_online(ICOM *icom, uint32_t *private, uint32_t count)
{
getPrivateCCount(private) = count;
if (getPrivateCCount(private) == getPrivatePCount(private))
{
if (getPrivateOffCnt(private) <= 5)
{
getPrivateOffCnt(private)++;
}
/* If offline count is greater than 5, then set online to 0 */
else
{
getPrivateOnline(private) = 0;
}
}
else
{
getPrivateOnline(private) = 1;
getPrivateOffCnt(private) = 0;
}
getPrivatePCount(private) = getPrivateCCount(private);
}
/**
* \brief: ICOM online handle function
* \param icom: ICOM handle
* \note: If online is 1, then increment transmitter count.
* If offline count is greater than 5, then set online to 0.
*/
static void icom_online_handle(ICOM *icom)
{
uint16_t channel;
/* Handle tx channal */
if (icom->txconfig && icom->txcount > 0)
{
for (channel = 0; channel < icom->txcount; channel++)
{
Cache_Inv(&getTxTcnt(channel), ICOM_TCNT_SIZE);
/* Increase tx count to indicate tx channal is online */
(*((uint32_t *)(&getTxTcnt(channel))))++;
Cache_wbInv(&getTxTcnt(channel), ICOM_TCNT_SIZE);
Cache_Inv(&getTxRcnt(channel), ICOM_RCNT_SIZE);
/* Control online status */
icom_control_online(icom, getTxPrivate(channel), (*((uint32_t *)&getTxRcnt(channel))));
}
}
/* Handle rx channal */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
Cache_Inv(&getRxRcnt(channel), ICOM_RCNT_SIZE);
/* Increase rx count to indicate rx channal is online */
(*((uint32_t *)(&getRxRcnt(channel))))++;
Cache_wbInv(&getRxRcnt(channel), ICOM_RCNT_SIZE);
Cache_Inv(&getRxTcnt(channel), ICOM_TCNT_SIZE);
/* Control online status */
icom_control_online(icom, getRxPrivate(channel), (*((uint32_t *)&getRxTcnt(channel))));
}
}
}
/**
* \brief: ICOM rxdata handle function
* \param icom: ICOM handle
* \note: If rx channal is ready, then check data length.
* If data length is valid, then call callback function.
* Otherwise, set rx channal to idle.
*/
static void icom_rxdata_handle(ICOM *icom)
{
uint16_t channel;
uint32_t length = 0;
/* Handle Rx */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
/* Check rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
/* Check rx channal is busy.
* Indicate that rx channal is ready to receive data.
*/
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Get data length and check data length is valid */
length = icom_get_data_length(icom, channel);
if (length <= getRxSize(channel) - ICOM_HEAD_SIZE)
{
/* Call callback function */
if (icom->rxconfig[channel].callback)
{
Cache_Inv(&getRxData(channel), length);
icom->rxconfig[channel].callback(channel, &getRxData(channel), length);
/* Set rx channal to idle */
getRxFlag(channel) = ICOM_F_IDLE;
Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE);
}
else /* Receive manual */
{
}
}
}
}
}
}
}
/**
* \brief: ICOM initialize function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_init(ICOM *icom)
{
uint16_t channel;
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (icom->init) return ICOM_E_OK;
/* Initialize tx channal */
if (icom->txconfig && icom->txcount > 0)
{
for (channel = 0; channel < icom->txcount; channel++)
{
if (getTxBase(channel) != NULL)
{
/* Initialize tx channal flag */
getTxFlag(channel) = ICOM_F_INIT;
Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE);
}
/* Initialize tx channal private data */
memset(getTxPrivate(channel), 0, sizeof(getTxPrivate(channel)));
/* Initialize tx channal callback function, default is NULL */
icom->txconfig[channel].callback = NULL;
}
}
/* Initialize rx channal */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
if (getRxBase(channel) != NULL)
{
/* Initialize rx channal flag */
icom_transmitter_ready(icom, channel);
}
/* Initialize rx channal private data */
memset(getRxPrivate(channel), 0, sizeof(getRxPrivate(channel)));
}
}
/* ICOM controller hs been initialized. */
icom->init = 1;
return ICOM_E_OK;
}
/**
* \brief: ICOM task function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_task(ICOM *icom)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
/* Handle online status */
icom_online_handle(icom);
/* Handle rxdata */
icom_rxdata_handle(icom);
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit getbuffer function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_transmit_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getTxBase(channel)) return ICOM_E_NBASE;
if (!buffer) return ICOM_E_IBUFFER;
/* Get transmit buffer */
*buffer = &getTxData(channel);
/* Get data length */
if (length) *length = getTxSize(channel) - ICOM_HEAD_SIZE;
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit trigger function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit_trigger(ICOM *icom, uint16_t channel, uint32_t length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getTxBase(channel)) return ICOM_E_NBASE;
if (length + ICOM_HEAD_SIZE > getTxSize(channel)) return ICOM_E_LOUT;
if (getTxFlag(channel) != ICOM_F_IDLE) return ICOM_E_BUSY;
/* Set data length */
icom_set_data_length(icom, channel, length);
/* Set tx channal to busy, need rx object receive data and set to ICOM_F_IDLE */
getTxFlag(channel) = ICOM_F_BUSY;
Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE);
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param data: Pointer to data buffer
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length)
{
int ret = ICOM_E_OK;
uint8_t *buffer = NULL;
uint32_t blength = 0;
uint32_t i = 0;
/* Get transmit buffer */
ret = icom_transmit_getbuffer(icom, channel, &buffer, &blength);
if (ICOM_E_OK != ret) return ret;
if (length > blength) return ICOM_E_LOUT;
/* Copy data to transmit buffer */
for (i = 0; i < length; i++)
{
buffer[i] = data[i];
}
Cache_wbInv(buffer, length);
/* Trigger transmit */
ret = icom_transmit_trigger(icom, channel, length);
return ret;
}
/**
* \brief: ICOM receive getbuffer function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
if (!buffer) return ICOM_E_IBUFFER;
*buffer = &getRxData(channel);
if (length) *length = getRxSize(channel) - ICOM_HEAD_SIZE;
return ICOM_E_OK;
}
/**
* \brief: ICOM receive indicate function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_indicate(ICOM *icom, uint16_t channel, uint32_t *length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
/* Check if the rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Get data length */
if (length) *length = icom_get_data_length(icom, channel);
}
else
{
return ICOM_E_NRECEIVE;
}
}
else
{
return ICOM_E_NRECEIVE;
}
return ICOM_E_OK;
}
/**
* \brief: ICOM receive end function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \return: ICOM error code
*/
int icom_receive_end(ICOM *icom, uint16_t channel)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
/* Check if the rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Set rx channal to idle */
getRxFlag(channel) = ICOM_F_IDLE;
}
else
{
return ICOM_E_NRECEIVE;
}
}
else
{
return ICOM_E_NRECEIVE;
}
return ICOM_E_OK;
}
/**
* \brief: ICOM receive function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param data: Pointer to data buffer
* \param length: Pointer to store data length
* \note: Data length must be less than or equal to the receive buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length)
{
int ret = ICOM_E_OK;
uint8_t *buffer = NULL;
uint32_t rxlength = 0;
uint32_t blength = 0;
uint32_t ilength = 0;
uint32_t i = 0;
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
if (!data) return ICOM_E_IBUFFER;
if (!length) return ICOM_E_ILENGTH;
ilength = *length;
/* Determine if there is any data to receive */
ret = icom_receive_indicate(icom, channel, &rxlength);
if (ICOM_E_OK != ret) return ret;
/* Get receive buffer */
ret = icom_receive_getbuffer(icom, channel, &buffer, &blength);
if (ICOM_E_OK != ret) return ret;
if (rxlength > blength) return ICOM_E_LOUT;
/* Copy data from receive buffer */
for (i = 0; i < rxlength; i++)
{
data[i] = buffer[i];
}
/* End receive */
icom_receive_end(icom, channel);
*length = rxlength;
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit channal online check function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!online) return ICOM_E_IBUFFER;
*online = getPrivateOnline(getTxPrivate(channel));
return ICOM_E_OK;
}
/**
* \brief: ICOM receive channal online check function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!online) return ICOM_E_IBUFFER;
*online = getPrivateOnline(getRxPrivate(channel));
return ICOM_E_OK;
}