/********************************************************************************************************* * ------------------------------------------------------------------------------------------------------ * 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; }