/** ******************************************************************************* * * @file ipmblib.c * @authors fatih.bellachia@lapp.in2p3.fr * @date 25/03/2013 * @version v0r1 * @brief This file provides functions to manage the following * functionalities of the Intelligent Platform Management Bus (IPMB) * *------------------------------------------------------------------------------ * * @copyright Copyright © 2013-2017, LAPP/CNRS * * @section LICENSE * * This software is governed by the CeCILL-C license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/or redistribute the software under the terms of the CeCILL-C * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. * ******************************************************************************* */ /* Includes ------------------------------------------------------------------*/ #include #include #include #include #include #include /* * OTP */ #include /* * libopencm3 */ #include #include /* * */ #include #include #include "ipmb/ipmblib.h" /** @addtogroup Hardware_Abstract_Layer * @{ */ /** @defgroup I2C * @{ */ /* Private define ------------------------------------------------------------*/ #define IPMB_NUM_SEQ 64 #define IPMB_MAX_MSG_LENGTH 32 #define IPMB_MAX_I2C_BUFFER_SIZE 64 #define IPMB_I2C_TIMEOUT 0x0008FFFFU #define IPMB_ERROR_MSG_LENGTH 64 #define IPMB_MAX_ISOLATOR 7 // version 2.0 only (default 9 AMC) #ifndef IPMB_QUEUE_SIZE # define IPMB_QUEUE_SIZE 32 #endif // IPMB_QUEUE_SIZE /* Private macro -------------------------------------------------------------*/ # define debug(level, ...) do { \ if (s_ucDebugLevel >= level) \ printf(__VA_ARGS__); \ } while(0) /* Private typedef -----------------------------------------------------------*/ typedef struct IPMBPacket { IPMBPacketHeader_t m_stHeader; // IPMB Header uint8_t m_aucData[0]; // Place-holder for IPMB data } IPMBPacket_t; typedef enum { IPMI_REQUEST = 0, IPMI_RESPONSE } IPMIMessageType_t; typedef struct IPMBMessage { IPMBBus_t m_eBus; // IPMB bus IPMIMessageType_t m_eMsgType; // IPMI message type (e.g. request/response) uint32_t m_uiLength; // IPMB message length IPMBPacket_t *m_pstPacket; // IPMB message } IPMBMessage_t; typedef struct IPMBSeqEntry { bool m_bInUse; // In use flag uint8_t m_ucSequence; // Sequence number uint32_t m_uiTimeout; // Timeout waiting for response uint32_t m_uiRetries; // Number of request retries uint32_t m_uiTimeStamp; // Time stamp (seconds) IPMBMessage_t m_stMessage; // Used to hold the temporary data for retransmission HANDLE m_hRequestRetry; // Request retry task handle } IPMBSeqEntry_t; typedef struct IPMBMsgHandler { IPMBMsgCallback_t m_fnMsgCallback; // User message callback function void *m_pvParameter; // Parameter to be passed to the user message callback function } IPMBMsgHandler_t; typedef struct IPMBErrorHandler { IPMBErrorCallback_t m_fnErrorCallback; // User message callback function void *m_pvParameter; // Parameter to be passed to the user message callback function } IPMBErrorHandler_t; typedef struct IPMBXferStats { uint32_t m_uiNbMessagesAlloc; // # of messages allocated uint32_t m_uiNbMessagesFree; // # of messages freed uint32_t m_uiNbMessages; // # of messages processed uint32_t m_uiNbPacketsAlloc; // # of packets allocated uint32_t m_uiNbPacketsFree; // # of packets freed uint32_t m_uiNbPackets; // # of packets processed uint32_t m_uiTotalBytes; // Total bytes } IPMBXferStats_t; typedef struct IPMBStats { IPMBXferStats_t m_uiRx; // Received IPMBXferStats_t m_uiTx; // Transmitted } IPMBStats_t; typedef struct IPMBMedium { IPMBBus_t m_eBus; // IPMB bus uint32_t m_uiI2Cx; // I2C channel uint8_t m_ucAddress; // I2C slave address uint8_t m_ucIdx; // Index of medium I2CEvent_t m_eEvent; // I2C event status uint8_t m_aucRxBuffer[IPMB_MAX_I2C_BUFFER_SIZE]; // I2C receive buffer uint8_t m_aucTxBuffer[IPMB_MAX_I2C_BUFFER_SIZE]; // I2C transmit buffer Queue_t *m_pstQueue; // Waiting list queue bool m_bEnabled; // I2C bus buffer enabled HANDLE m_hGetMessage; // Get message task handle uint16_t m_usIsolators; // IPMB-L isolators [0..15] } IPMBMedium_t; typedef struct IPMBI2CStatus { uint32_t m_uiI2Cx; // I2C channel I2CError_t m_eI2CError; // Last I2C error status char m_szErrorMessage[IPMB_ERROR_MSG_LENGTH]; // Buffer to hold I2C error message } IPMBI2CStatus_t; typedef struct IPMBDescriptor { uint8_t m_ucNbMedium; // Number of medium uint8_t m_ucIdxCurMedium; // Index of current medium used IPMBMedium_t *m_pstMedium; // Pointer of medium array allocated in IPMB_Init() IPMBMsgHandler_t m_stReceiveHandler; // Receive user's callback IPMBMsgHandler_t m_stTimeoutHandler; // Message timeout user's callback IPMBErrorHandler_t m_stErrorHandler; // Error user's callback HANDLE m_hWaitingBusFree; // Waiting bus free task handle uint8_t m_ucCurrentSeq; // Current sequence number IPMBSeqEntry_t m_astSeqTable[IPMB_NUM_SEQ]; // Sequence table IPMBError_t m_eError; // Last error status IPMBI2CStatus_t m_stI2CStatus; // Last I2C error status } IPMBDescriptor_t; /* Private variables ---------------------------------------------------------*/ static IPMBDescriptor_t s_astIPMBDesc[IPMB_MAX_BUS] = { { 0 }, { 0 } }; static IPMBStats_t s_astIPMBStats[IPMB_MAX_BUS] = { { { 0 } }, { { 0 } } }; static uint8_t s_ucDebugLevel = 0; /* Public variables ----------------------------------------------------------*/ /* Public function prototypes -----------------------------------------------*/ extern void QueueDump(Queue_t *pstQueue, const char *szMsg); /* Private function prototypes -----------------------------------------------*/ static bool IPMB_MediumDisable(IPMBMedium_t *pstMedium); static bool IPMB_MediumEnable(IPMBMedium_t *pstMedium); static void IPMB_WaitingBusFreeTask(void *pvArg); static void IPMB_RequestRetryTask(void *pvArg); static bool IPMB_MediumBufferDisable(IPMBMedium_t *pstMedium, ...); static bool IPMB_MediumBufferEnable(IPMBMedium_t *pstMedium, ...); /* Private functions ---------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ static void IPMB_Delay(unsigned long ms) /*----------------------------------------------------------------------------*/ { volatile unsigned long pause = RCC_SystickCount() + ms; while(RCC_SystickCount() < pause); } /*----------------------------------------------------------------------------*/ static void IPMB_DumpMessage(IPMBMedium_t *pstMedium, IPMBMessage_t *pstMessage) /*----------------------------------------------------------------------------*/ { if (pstMessage->m_uiLength < sizeof(IPMBPacketHeader_t)) return; if (s_ucDebugLevel >= 1) { size_t uiLength = pstMessage->m_uiLength - sizeof(IPMBPacketHeader_t); bool bRequest = (pstMessage->m_pstPacket->m_stHeader.m_netFn & 0x1) == 0x0; time_t lTime = RCC_Seconds(); char acTime[64]; if (uiLength > 0) --uiLength; strftime(acTime, sizeof acTime, "%T", localtime(&lTime)); #if 0 { uint8_t *pucBuffer = (uint8_t *)pstMessage->m_pstPacket; printf("DEBUG> [RAW]"); for(size_t i = 0; i < pstMessage->m_uiLength; i++) { printf(" 0x%02x", pucBuffer[i]); } printf("\n"); } #endif // Debug message format e.g. // IPMB-A>> 20->84 Seq33 Cmd04:2D [10 6C 20 CC 2D 1A CD] // IPMB-B<< 84->20 Seq33 Cmd05:2D [14 CC 84 CC 2D 00 23 C0 C0 00 E0] if (lTime >= 86400) printf("%ld+", lTime/86400); // print # day printf("%s %s%2s %02x->%02x Seq%02x Cmd%02x:%02x [", acTime, I2C_ToString(pstMedium->m_uiI2Cx), bRequest? ">>": "<<", pstMessage->m_pstPacket->m_stHeader.m_ucSlaveAddress, pstMessage->m_pstPacket->m_stHeader.m_ucAddress, pstMessage->m_pstPacket->m_stHeader.m_seq, pstMessage->m_pstPacket->m_stHeader.m_netFn, pstMessage->m_pstPacket->m_stHeader.m_ucCommand); for(size_t idx = 0; idx < uiLength; idx++) { printf("%02x", pstMessage->m_pstPacket->m_aucData[idx]); if (idx < (uiLength - 1)) printf(" "); } printf("]"); if (s_ucDebugLevel >= 2) printf(" Cks=%02x:%02x", pstMessage->m_pstPacket->m_stHeader.m_ucChecksum, pstMessage->m_pstPacket->m_aucData[uiLength]); printf("\n"); } } /*----------------------------------------------------------------------------*/ static void IPMB_SetError(IPMBBus_t eBus, IPMBError_t eError) /*----------------------------------------------------------------------------*/ { IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; pstDesc->m_eError = eError; if (pstDesc->m_stErrorHandler.m_fnErrorCallback != NULL) pstDesc->m_stErrorHandler.m_fnErrorCallback(pstDesc->m_stErrorHandler.m_pvParameter, eError); } /*----------------------------------------------------------------------------*/ static uint8_t IPMB_Checksum(const uint8_t *pucData, uint32_t uiLen) /*----------------------------------------------------------------------------*/ { int8_t ucSum = 0; for (uint32_t i = 0U; i < uiLen; i++) ucSum += pucData[i]; return -ucSum; } /*----------------------------------------------------------------------------*/ static bool IPMB_CheckPacket(const uint8_t *pucData, uint32_t uiLen) /*----------------------------------------------------------------------------*/ { int8_t ucSum = 0; for (uint32_t i = 0; i < uiLen; i++) ucSum += (int8_t)pucData[i]; return(ucSum == 0); } /*----------------------------------------------------------------------------*/ static void IPMB_GetMessageTask(void *pvArg) /*----------------------------------------------------------------------------*/ { if (pvArg == NULL) { printf("ERROR> [%s] pointer NULL\n", __PRETTY_FUNCTION__); return; } IPMBMedium_t *pstMedium = pvArg; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[pstMedium->m_eBus]; uint16_t usSize = QueueSize(pstMedium->m_pstQueue); for (uint16_t i = 0; i < usSize; i++) { IPMBMessage_t *pstMessage = QueueRemove(pstMedium->m_pstQueue); IPMBPacket_t *pstPacket = pstMessage->m_pstPacket; if (IPMB_CheckPacket((uint8_t *)pstPacket, pstMessage->m_uiLength) == true) { IPMB_DumpMessage(pstMedium, pstMessage); if (pstDesc->m_stReceiveHandler.m_fnMsgCallback != NULL) pstDesc->m_stReceiveHandler.m_fnMsgCallback(pstDesc->m_stReceiveHandler.m_pvParameter, pstPacket, pstMessage->m_uiLength - 1); // don't count checksum if ((pstPacket->m_stHeader.m_netFn & 0x1) == 0x1) { // Receive response IPMBSeqEntry_t *pstSeqEntry = &pstDesc->m_astSeqTable[pstPacket->m_stHeader.m_seq]; /* Check sequence entry */ if ((pstSeqEntry->m_bInUse == true) && ((pstPacket->m_stHeader.m_netFn & ~0x1) == pstSeqEntry->m_stMessage.m_pstPacket->m_stHeader.m_netFn) && (pstPacket->m_stHeader.m_lun == pstSeqEntry->m_stMessage.m_pstPacket->m_stHeader.m_bridge) && (pstPacket->m_stHeader.m_seq == pstSeqEntry->m_stMessage.m_pstPacket->m_stHeader.m_seq) && (pstPacket->m_stHeader.m_ucCommand == pstSeqEntry->m_stMessage.m_pstPacket->m_stHeader.m_ucCommand)) { /* Cancel Request Retry task */ pstSeqEntry->m_hRequestRetry = TimerTaskCancel(pstSeqEntry->m_hRequestRetry); /* Clear sequence entry */ free(pstSeqEntry->m_stMessage.m_pstPacket); memset(pstSeqEntry, 0, sizeof(IPMBSeqEntry_t)); s_astIPMBStats[pstMedium->m_eBus].m_uiTx.m_uiNbPacketsFree++; } } } /* Free IPMI message */ free(pstPacket); free(pstMessage); s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsFree++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbMessagesFree++; } } /*----------------------------------------------------------------------------*/ static void IPMB_Read(IPMBMedium_t *pstMedium) /*----------------------------------------------------------------------------*/ { if (pstMedium == NULL) return; int iBytes; if ((iBytes = I2C_Read(pstMedium->m_uiI2Cx, I2C_SLAVE, NULL, 0U)) > 0) { uint32_t uiLength = iBytes + 1; // size + slave address field uint8_t *pucBuffer = malloc(uiLength); if (pucBuffer == NULL) { IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_NO_MEMORY); return; } s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsAlloc++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPackets++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiTotalBytes += uiLength; if (I2C_Read(pstMedium->m_uiI2Cx, I2C_SLAVE, &pucBuffer[1], iBytes) > 0) { IPMBMessage_t *pstMessage = malloc(sizeof(IPMBMessage_t)); if (pstMessage == NULL) { IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_NO_MEMORY); /* Free IPMI packet */ free(pucBuffer); s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsFree++; return; } s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbMessagesAlloc++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbMessages++; pucBuffer[0] = pstMedium->m_ucAddress << 1; // Put requester's slave address pstMessage->m_eBus = pstMedium->m_eBus; pstMessage->m_uiLength = uiLength; pstMessage->m_pstPacket = (IPMBPacket_t *)pucBuffer; pstMessage->m_eMsgType = pstMessage->m_pstPacket->m_stHeader.m_netFn & 0x1; bool bRtn = QueueInsert(pstMedium->m_pstQueue, pstMessage); if (bRtn == false) { IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_FIFO_FULL); // flush message queue uint16_t usQueueSize = QueueSize(pstMedium->m_pstQueue); for (uint16_t i = 0; i < usQueueSize; i++) { IPMBMessage_t *pstMsg = (IPMBMessage_t *)QueueRemove(pstMedium->m_pstQueue); if (pstMsg != NULL) { free(pstMsg->m_pstPacket); free(pstMsg); s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsFree++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbMessagesFree++; } } // Re-insert current message if (QueueInsert(pstMedium->m_pstQueue, pstMessage) == false) { free(pstMessage->m_pstPacket); free(pstMessage); s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsFree++; s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbMessagesFree++; } } } else { /* Free IPMI packet */ free(pucBuffer); s_astIPMBStats[pstMedium->m_eBus].m_uiRx.m_uiNbPacketsFree++; } } } /** * @brief Event callback * @param arg Pointer void. User argument * @param event enum. Event flag * @retval None */ /*---------------------------------------------------------------------------*/ static void IPMB_EventCallback(void *pvArg, I2CEvent_t eEvent) /*---------------------------------------------------------------------------*/ { IPMBMedium_t *pstMedium = pvArg; if (pstMedium == NULL) return; switch (eEvent) { case I2C_EVENT_MASTER_MODE_SELECT: break; case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: break; case I2C_EVENT_MASTER_BYTE_TRANSMITTING: break; case I2C_EVENT_MASTER_BYTE_TRANSMITTED: break; case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: I2C_SlavePrepareRead(pstMedium->m_uiI2Cx); break; case I2C_EVENT_SLAVE_BYTE_RECEIVED: break; case I2C_EVENT_SLAVE_ACK_FAILURE: break; case I2C_EVENT_SLAVE_STOP_DETECTED: IPMB_Read(pstMedium); break; default: break; } pstMedium->m_eEvent = eEvent; } /** * @brief Error callback * @param arg Pointer void. User argument * @param error enum. Error flag * @retval None */ /*---------------------------------------------------------------------------*/ static void IPMB_ErrorCallback(void *pvArg, I2CError_t eError) /*---------------------------------------------------------------------------*/ { IPMBMedium_t *pstMedium = pvArg; if (pstMedium == NULL) return; switch (eError) { case I2C_ERROR_OK: break; case I2C_ERROR_BUS_ERROR: if (pstMedium->m_uiI2Cx == I2C_IPMB_L) { uint16_t usIsolators = pstMedium->m_usIsolators; for (uint8_t idx = 1; idx <= IPMB_MAX_ISOLATOR; idx++) { if (((usIsolators >> idx) & 0x1) == 1) { IPMB_MediumBufferDisable(pstMedium, idx); IPMB_MediumBufferEnable(pstMedium, idx); } } } else { IPMB_MediumBufferDisable(pstMedium); IPMB_MediumBufferEnable(pstMedium); } break; case I2C_ERROR_ARBITRATION_LOST: break; case I2C_ERROR_ACK_FAILURE: break; case I2C_ERROR_OUT_OF_BAND: break; case I2C_ERROR_PEC_ERROR: break; case I2C_ERROR_TIMEOUT: break; case I2C_ERROR_SMB_ALERT: break; default: break; } /* Save I2C error status */ s_astIPMBDesc[pstMedium->m_eBus].m_stI2CStatus.m_uiI2Cx = pstMedium->m_uiI2Cx; s_astIPMBDesc[pstMedium->m_eBus].m_stI2CStatus.m_eI2CError = eError; IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_I2C_ERROR); } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumInit(IPMBMedium_t *pstMedium, uint8_t ucIdx, uint32_t uiI2Cx, uint8_t ucAddress, IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { I2CUsrXferBuffer_t stUsrXferBuffer; if (pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } /* Open I2C interface */ stUsrXferBuffer.m_astDesc[I2C_Tx].m_pucBase = pstMedium->m_aucTxBuffer; stUsrXferBuffer.m_astDesc[I2C_Tx].m_uiCapacity = sizeof pstMedium->m_aucTxBuffer; stUsrXferBuffer.m_astDesc[I2C_Rx].m_pucBase = pstMedium->m_aucRxBuffer; stUsrXferBuffer.m_astDesc[I2C_Rx].m_uiCapacity = sizeof pstMedium->m_aucRxBuffer; if (I2C_Open(uiI2Cx, ucAddress, &stUsrXferBuffer) == false) return false; /* Set Slave transfer in non-blocking mode */ if (I2C_SetConfig(uiI2Cx, I2C_CFG_SLAVE_NON_BLOCKING, true) == false) { I2C_Close(uiI2Cx); return false; } /* Initialize I2C BUS BUFFER-x */ if (I2C_BusBufferInit(uiI2Cx) == false) { I2C_Close(uiI2Cx); return false; } /* Create message queue */ pstMedium->m_pstQueue = QueueCreate(IPMB_QUEUE_SIZE); if (pstMedium->m_pstQueue == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NO_MEMORY); I2C_Close(uiI2Cx); return false; } /* Set ERROR callback */ I2C_RegisterCallback(uiI2Cx, I2C_IT_ERROR_CALLBACK, (I2CFunction_t)IPMB_ErrorCallback, (void *)pstMedium); /* Set Medium structure */ pstMedium->m_uiI2Cx = uiI2Cx; pstMedium->m_ucAddress = ucAddress; pstMedium->m_eBus = eBus; pstMedium->m_ucIdx = ucIdx; return true; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumBufferReady(IPMBMedium_t *pstMedium) /*----------------------------------------------------------------------------*/ { if (OTP_MCU_ID_IPMC == OTP_MCU_ID) pstMedium->m_bEnabled = I2C_BusBufferReady(pstMedium->m_uiI2Cx); return pstMedium->m_bEnabled; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumBufferDisable(IPMBMedium_t *pstMedium, ...) /*----------------------------------------------------------------------------*/ { uint32_t uiTimeout = IPMB_I2C_TIMEOUT; if(pstMedium == NULL) return false; /* Disable I2C BUS BUFFER-x */ if (pstMedium->m_uiI2Cx == I2C_IPMB_L) { va_list pArgs; uint32_t uiIsolator; va_start(pArgs, pstMedium); uiIsolator = va_arg(pArgs, unsigned int); va_end(pArgs); I2C_BusBufferDisable(pstMedium->m_uiI2Cx, uiIsolator); /* Clear isolator status bit */ pstMedium->m_usIsolators &= ~(1 << uiIsolator); } else { I2C_BusBufferDisable(pstMedium->m_uiI2Cx); } pstMedium->m_bEnabled = false; /* wait I2C BUS BUFFER-x not ready */ while (IPMB_MediumBufferReady(pstMedium) && (uiTimeout-- > 0U)); if (uiTimeout == 0U) { IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_READY_TIMEOUT); return false; } return true; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumBufferEnable(IPMBMedium_t *pstMedium, ...) /*----------------------------------------------------------------------------*/ { uint32_t uiTimeout = IPMB_I2C_TIMEOUT; if(pstMedium == NULL) return false; /* enable I2C BUS BUFFER-x */ if (pstMedium->m_uiI2Cx == I2C_IPMB_L) { va_list pArgs; uint32_t uiIsolator; va_start(pArgs, pstMedium); uiIsolator = va_arg(pArgs, unsigned int); va_end(pArgs); I2C_BusBufferEnable(pstMedium->m_uiI2Cx, uiIsolator); /* Set isolator status bit */ pstMedium->m_usIsolators |= (1 << uiIsolator); } else { I2C_BusBufferEnable(pstMedium->m_uiI2Cx); } pstMedium->m_bEnabled = true; /* wait I2C BUS BUFFER-x ready */ while (!IPMB_MediumBufferReady(pstMedium) && (uiTimeout-- > 0U)); if (uiTimeout == 0U) { IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_READY_TIMEOUT); return false; } return true; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumDisable(IPMBMedium_t *pstMedium) /*----------------------------------------------------------------------------*/ { if(pstMedium == NULL) return false; /* Disable I2C interface */ I2C_Disable(pstMedium->m_uiI2Cx); /* Cancel task */ pstMedium->m_hGetMessage = TaskCancel(pstMedium->m_hGetMessage); /* Reset EVENT callback */ I2C_UnregisterCallback(pstMedium->m_uiI2Cx, I2C_IT_EVENT_CALLBACK); return true; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumEnable(IPMBMedium_t *pstMedium) /*----------------------------------------------------------------------------*/ { if(pstMedium == NULL) return false; /* Set EVENT callback */ I2C_RegisterCallback(pstMedium->m_uiI2Cx, I2C_IT_EVENT_CALLBACK, (I2CFunction_t)IPMB_EventCallback, (void *)pstMedium); /* Create task */ pstMedium->m_hGetMessage = TaskCreate(IPMB_GetMessageTask, pstMedium); /* Enable I2C interface */ I2C_Enable(pstMedium->m_uiI2Cx); /* Wait some microseconds */ for(uint32_t uiTimeout = 0; uiTimeout < IPMB_I2C_TIMEOUT; uiTimeout++) { __asm volatile ("nop"); } return true; } /*----------------------------------------------------------------------------*/ static bool IPMB_MediumClean(IPMBMedium_t *pstMedium) /*----------------------------------------------------------------------------*/ { bool bRtn = true; if(pstMedium == NULL) return false; bRtn &= IPMB_MediumDisable(pstMedium); if (pstMedium->m_uiI2Cx == I2C_IPMB_L) { uint16_t usIsolators = pstMedium->m_usIsolators; for (uint8_t idx = 1; idx <= IPMB_MAX_ISOLATOR; idx++) { if (((usIsolators >> idx) & 0x1) == 1) bRtn &= IPMB_MediumBufferDisable(pstMedium, idx); } } else { bRtn &= IPMB_MediumBufferDisable(pstMedium); } bRtn &= I2C_BusBufferClean(pstMedium->m_uiI2Cx); bRtn &= I2C_Close(pstMedium->m_uiI2Cx); pstMedium->m_pstQueue = QueueDestroy(pstMedium->m_pstQueue); return bRtn; } /*----------------------------------------------------------------------------*/ static IPMBMedium_t *IPMB_SelectMedium(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return NULL; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; if (pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return NULL; } for (uint8_t i = 0; i < pstDesc->m_ucNbMedium; i++) { IPMBMedium_t *pstMedium; pstDesc->m_ucIdxCurMedium = (pstDesc->m_ucIdxCurMedium + 1) % pstDesc->m_ucNbMedium; pstMedium = &pstDesc->m_pstMedium[pstDesc->m_ucIdxCurMedium]; if (IPMB_MediumBufferReady(pstMedium) == true) { return pstMedium; } else { IPMB_MediumBufferDisable(pstMedium); IPMB_MediumBufferEnable(pstMedium); IPMB_Delay(1); // wait 1ms if (IPMB_MediumBufferReady(pstMedium) == true) { return pstMedium; } } } IPMB_SetError(eBus, IPMB_ERROR_IPMB_DISABLED); return NULL; } /*----------------------------------------------------------------------------*/ static IPMBSeqEntry_t *IPMB_GetSeqEntry(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return NULL; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; /* Clear old sequence entries */ for (size_t i = 0; i < IPMB_NUM_SEQ; i++) { if (pstDesc->m_astSeqTable[i].m_bInUse == true) { if (RCC_Seconds() > (pstDesc->m_astSeqTable[i].m_uiTimeStamp + IPMB_SEQ_VALUE_EXPIRATION_INTERVAL)) { if (pstDesc->m_astSeqTable[i].m_stMessage.m_pstPacket != NULL) { free(pstDesc->m_astSeqTable[i].m_stMessage.m_pstPacket); s_astIPMBStats[eBus].m_uiTx.m_uiNbPacketsFree++; } memset(&pstDesc->m_astSeqTable[i], 0, sizeof(IPMBSeqEntry_t)); } } } IPMBSeqEntry_t *pstSeqEntry = NULL; /* Find sequence entry */ for (size_t i = 0; i < IPMB_NUM_SEQ; i++) { pstDesc->m_ucCurrentSeq = (pstDesc->m_ucCurrentSeq + 1) % IPMB_NUM_SEQ; if (pstDesc->m_astSeqTable[pstDesc->m_ucCurrentSeq].m_bInUse == false) { uint32_t ucCurrentSeq = pstDesc->m_ucCurrentSeq; pstDesc->m_astSeqTable[ucCurrentSeq].m_bInUse = true; pstDesc->m_astSeqTable[ucCurrentSeq].m_ucSequence = ucCurrentSeq; pstDesc->m_astSeqTable[ucCurrentSeq].m_uiTimeout = IPMB_TIME_BETWEEN_REQ_RETRIES; pstDesc->m_astSeqTable[ucCurrentSeq].m_uiRetries = IPMB_NUMBER_REQ_RETRIES; pstDesc->m_astSeqTable[ucCurrentSeq].m_uiTimeStamp = RCC_Seconds(); pstDesc->m_astSeqTable[ucCurrentSeq].m_stMessage.m_eBus = eBus; pstDesc->m_astSeqTable[ucCurrentSeq].m_stMessage.m_eMsgType = IPMI_REQUEST; pstSeqEntry = &pstDesc->m_astSeqTable[ucCurrentSeq]; break; } } if (pstSeqEntry == NULL) IPMB_SetError(eBus, IPMB_ERROR_SEQ_NOT_AVAILABLE); return pstSeqEntry; } /*----------------------------------------------------------------------------*/ static bool IPMB_Send(IPMBMessage_t *pstMessage) /*----------------------------------------------------------------------------*/ { IPMBMedium_t *pstMedium; uint32_t uiNbMediumBusy = 0U; for (uint32_t i = 0; i < s_astIPMBDesc[pstMessage->m_eBus].m_ucNbMedium; i++) { if ((pstMedium = IPMB_SelectMedium(pstMessage->m_eBus)) == NULL) return false; if (I2C_MasterBusBusy(pstMedium->m_uiI2Cx) == false) { uint8_t *pucBuffer = (uint8_t *)pstMessage->m_pstPacket; int iRtn; iRtn = I2C_Write(pstMedium->m_uiI2Cx, I2C_MASTER, &pucBuffer[1], pstMessage->m_uiLength - 1, (pstMessage->m_pstPacket->m_stHeader.m_ucAddress >> 1)); if (iRtn > 0) { IPMB_DumpMessage(pstMedium, pstMessage); //printf("DEBUG> [send] (%s) iRtn = %d\n", I2C_ToString(pstMedium->m_uiI2Cx), iRtn); return true; } } else { uiNbMediumBusy++; } } if (pstMessage->m_eMsgType == IPMI_REQUEST) { IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[pstMessage->m_eBus]; if (uiNbMediumBusy == pstDesc->m_ucNbMedium) { pstDesc->m_hWaitingBusFree = TimerTaskCreateOneShot(IPMB_WaitingBusFreeTask, pstMessage, IPMB_TIMEOUT_WAITING_BUS_FREE); IPMB_SetError(pstMedium->m_eBus, IPMB_ERROR_BUS_BUSY); } } //printf("DEBUG> [send] bRtn = 0\n"); return false; } /*----------------------------------------------------------------------------*/ static void IPMB_WaitingBusFreeTask(void *pvArg) /*----------------------------------------------------------------------------*/ { IPMBMessage_t *pstMessage = pvArg; if (pstMessage == NULL) return; if (IPMB_Send(pstMessage) == true) { IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[pstMessage->m_eBus]; IPMBSeqEntry_t *pstSeqEntry = &pstDesc->m_astSeqTable[pstMessage->m_pstPacket->m_stHeader.m_seq]; pstSeqEntry->m_hRequestRetry = TimerTaskCreate(IPMB_RequestRetryTask, pstSeqEntry, pstSeqEntry->m_uiTimeout); } } /*----------------------------------------------------------------------------*/ static void IPMB_RequestRetryTask(void *pvArg) /*----------------------------------------------------------------------------*/ { IPMBSeqEntry_t *pstSeqEntry = pvArg; if (pstSeqEntry == NULL) return; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[pstSeqEntry->m_stMessage.m_eBus]; if (pstSeqEntry->m_uiRetries > 0) { pstSeqEntry->m_uiRetries--; IPMB_Send(&pstSeqEntry->m_stMessage); } else { // Request timed out if (pstDesc->m_stTimeoutHandler.m_fnMsgCallback != NULL) pstDesc->m_stTimeoutHandler.m_fnMsgCallback(pstDesc->m_stTimeoutHandler.m_pvParameter, pstSeqEntry->m_stMessage.m_pstPacket, pstSeqEntry->m_stMessage.m_uiLength - 1); // don't count checksum /* Cancel Request Retry task */ pstSeqEntry->m_hRequestRetry = TimerTaskCancel(pstSeqEntry->m_hRequestRetry); /* Clear sequence entry */ free(pstSeqEntry->m_stMessage.m_pstPacket); s_astIPMBStats[pstSeqEntry->m_stMessage.m_eBus].m_uiTx.m_uiNbPacketsFree++; memset(pstSeqEntry, 0, sizeof(IPMBSeqEntry_t)); } } /*----------------------------------------------------------------------------*/ static bool IPMB_SendRequest(IPMBBus_t eBus, void *pvBuffer, uint32_t uiLength) /*----------------------------------------------------------------------------*/ { bool bRtn; if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; if (pvBuffer == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } IPMBSeqEntry_t *pstSeqEntry; IPMBPacket_t *pstPacket; if ((pstSeqEntry = IPMB_GetSeqEntry(eBus)) == NULL) return false; // Check buffer length (Min = Packet header with no data) if (uiLength < sizeof(IPMBPacketHeader_t)) { printf("#IPMB_SendRequest (%p, %d)\n", pvBuffer, uiLength); return false; } pstSeqEntry->m_stMessage.m_uiLength = uiLength + 1; pstPacket = malloc(pstSeqEntry->m_stMessage.m_uiLength); if (pstPacket == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NO_MEMORY); memset(pstSeqEntry, 0, sizeof(IPMBSeqEntry_t)); // clear sequence entry return false; } IPMBStats_t *pstStats = &s_astIPMBStats[eBus]; pstStats->m_uiTx.m_uiNbPacketsAlloc++; pstStats->m_uiTx.m_uiNbPackets++; pstStats->m_uiTx.m_uiTotalBytes += pstSeqEntry->m_stMessage.m_uiLength; /* Copy message */ memcpy(pstPacket, pvBuffer, uiLength); size_t uiLast = uiLength - sizeof(IPMBPacketHeader_t); pstSeqEntry->m_stMessage.m_pstPacket = pstPacket; pstPacket->m_stHeader.m_seq = pstSeqEntry->m_ucSequence; pstPacket->m_stHeader.m_ucChecksum = IPMB_Checksum((uint8_t *)&pstPacket->m_stHeader, 2); pstPacket->m_aucData[uiLast] = IPMB_Checksum((uint8_t *)&pstPacket->m_stHeader.m_ucSlaveAddress, uiLength - 3); /* Copy back message */ memcpy(pvBuffer, pstPacket, uiLength); bRtn = IPMB_Send(&pstSeqEntry->m_stMessage); if (bRtn == true) pstSeqEntry->m_hRequestRetry = TimerTaskCreate(IPMB_RequestRetryTask, pstSeqEntry, pstSeqEntry->m_uiTimeout); //printf("DEBUG> [send-req] bRtn = %d\n", bRtn); return bRtn; } /*----------------------------------------------------------------------------*/ static bool IPMB_SendResponse(IPMBBus_t eBus, void *pvBuffer, uint32_t uiLength) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; if (pvBuffer == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } IPMBMessage_t *pstMessage; IPMBPacket_t *pstPacket; pstMessage = malloc(sizeof(IPMBMessage_t)); if (pstMessage == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NO_MEMORY); return false; } IPMBStats_t *pstStats = &s_astIPMBStats[eBus]; pstStats->m_uiTx.m_uiNbMessagesAlloc++; pstStats->m_uiTx.m_uiNbMessages++; pstMessage->m_eBus = eBus; pstMessage->m_eMsgType = IPMI_RESPONSE; pstMessage->m_uiLength = uiLength + 1; pstPacket = malloc(pstMessage->m_uiLength); if (pstPacket == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NO_MEMORY); free(pstMessage); s_astIPMBStats[eBus].m_uiTx.m_uiNbMessagesFree++; return false; } pstStats->m_uiTx.m_uiNbPacketsAlloc++; pstStats->m_uiTx.m_uiNbPackets++; pstStats->m_uiTx.m_uiTotalBytes += pstMessage->m_uiLength; /* Copy message */ memcpy(pstPacket, pvBuffer, uiLength); size_t uiLast = uiLength - sizeof(IPMBPacketHeader_t); pstMessage->m_pstPacket = pstPacket; pstPacket->m_stHeader.m_ucChecksum = IPMB_Checksum((uint8_t *)&pstPacket->m_stHeader, 2); pstPacket->m_aucData[uiLast] = IPMB_Checksum((uint8_t *)&pstPacket->m_stHeader.m_ucSlaveAddress, uiLength - 3); bool bRtn = IPMB_Send(pstMessage); /* Copy back message */ if (bRtn == true) memcpy(pvBuffer, pstPacket, uiLength); free(pstMessage->m_pstPacket); free(pstMessage); pstStats->m_uiTx.m_uiNbPacketsFree++; pstStats->m_uiTx.m_uiNbMessagesFree++; //printf("DEBUG> [send-resp] bRtn = %d\n", bRtn); return bRtn; } /* Public functions ----------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ bool IPMB_Init(IPMBBus_t eBus, uint8_t ucAddress) /*----------------------------------------------------------------------------*/ { uint32_t uiNbMedium; if (eBus == IPMB_0) { uiNbMedium = 2; } else if (eBus == IPMB_L) { uiNbMedium = 1; } else { return false; } IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; pstDesc->m_pstMedium = (IPMBMedium_t *)calloc(uiNbMedium, sizeof(IPMBMedium_t)); if (pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NO_MEMORY); return false; } if (eBus == IPMB_0) { if (IPMB_MediumInit(&pstDesc->m_pstMedium[0], 0, I2C_IPMB_A, ucAddress, eBus) == false) { free(pstDesc->m_pstMedium); pstDesc->m_pstMedium = NULL; return false; } if (IPMB_MediumInit(&pstDesc->m_pstMedium[1], 1, I2C_IPMB_B, ucAddress, eBus) == false) { free(pstDesc->m_pstMedium); pstDesc->m_pstMedium = NULL; return false; } } else { if (IPMB_MediumInit(&pstDesc->m_pstMedium[0], 0, I2C_IPMB_L, ucAddress, eBus) == false) { free(pstDesc->m_pstMedium); pstDesc->m_pstMedium = NULL; return false; } } pstDesc->m_ucNbMedium = uiNbMedium; return true; } /*----------------------------------------------------------------------------*/ bool IPMB_Clean(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { bool bRtn = true; if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; if (pstDesc->m_pstMedium != NULL) { for (size_t i = 0; i < pstDesc->m_ucNbMedium; i++) { bRtn &= IPMB_MediumClean(&pstDesc->m_pstMedium[i]); } free(pstDesc->m_pstMedium); } memset(pstDesc, 0, sizeof(IPMBDescriptor_t)); return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_RegisterMsgCallback(IPMBBus_t eBus, IPMBMsgCallbackType_t eMsgCallbackType, IPMBMsgCallback_t fnMsgCallback, void *pvParameter) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; if (fnMsgCallback == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; switch (eMsgCallbackType) { case IPMB_RECEIVE_CALLBACK: pstDesc->m_stReceiveHandler.m_fnMsgCallback = fnMsgCallback; pstDesc->m_stReceiveHandler.m_pvParameter = pvParameter; break; case IPMB_TIMEOUT_CALLBACK: pstDesc->m_stTimeoutHandler.m_fnMsgCallback = fnMsgCallback; pstDesc->m_stTimeoutHandler.m_pvParameter = pvParameter; break; default: IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; break; } return true; } /*----------------------------------------------------------------------------*/ bool IPMB_UnregisterMsgCallback(IPMBBus_t eBus, IPMBMsgCallbackType_t eMsgCallbackType) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; switch (eMsgCallbackType) { case IPMB_RECEIVE_CALLBACK: pstDesc->m_stReceiveHandler.m_fnMsgCallback = NULL; pstDesc->m_stReceiveHandler.m_pvParameter = NULL; break; case IPMB_TIMEOUT_CALLBACK: pstDesc->m_stTimeoutHandler.m_fnMsgCallback = NULL; pstDesc->m_stTimeoutHandler.m_pvParameter = NULL; break; default: IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; break; } return true; } /*----------------------------------------------------------------------------*/ bool IPMB_RegisterErrorCallback(IPMBBus_t eBus, IPMBErrorCallback_t fnErrorCallback, void *pvParameter) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; if (fnErrorCallback == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; pstDesc->m_stErrorHandler.m_fnErrorCallback = fnErrorCallback; pstDesc->m_stErrorHandler.m_pvParameter = pvParameter; return true; } /*----------------------------------------------------------------------------*/ bool IPMB_UnregisterErrorCallback(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; pstDesc->m_stErrorHandler.m_fnErrorCallback = NULL; pstDesc->m_stErrorHandler.m_pvParameter = NULL; return true; } /*----------------------------------------------------------------------------*/ bool IPMB_Enable(IPMBBus_t eBus, IPMBChannel_t eChannel) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = true; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumEnable(&pstDesc->m_pstMedium[0]); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bRtn &= IPMB_MediumEnable(&pstDesc->m_pstMedium[1]); } } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumEnable(&pstDesc->m_pstMedium[0]); } } return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_Disable(IPMBBus_t eBus, IPMBChannel_t eChannel) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = true; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumDisable(&pstDesc->m_pstMedium[0]); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bRtn &= IPMB_MediumDisable(&pstDesc->m_pstMedium[1]); } } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumDisable(&pstDesc->m_pstMedium[0]); } } return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_BufferEnable(IPMBBus_t eBus, IPMBChannel_t eChannel, ...) /*----------------------------------------------------------------------------*/ { va_list pArgs; if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = true; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumBufferEnable(&pstDesc->m_pstMedium[0]); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bRtn &= IPMB_MediumBufferEnable(&pstDesc->m_pstMedium[1]); } } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) { uint32_t uiIsolator; va_start(pArgs, eChannel); uiIsolator = va_arg(pArgs, unsigned int); va_end(pArgs); bRtn &= IPMB_MediumBufferEnable(&pstDesc->m_pstMedium[0], uiIsolator); } } } return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_BufferDisable(IPMBBus_t eBus, IPMBChannel_t eChannel, ...) /*----------------------------------------------------------------------------*/ { va_list pArgs; if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = true; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumBufferDisable(&pstDesc->m_pstMedium[0]); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bRtn &= IPMB_MediumBufferDisable(&pstDesc->m_pstMedium[1]); } } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) { uint32_t uiIsolator; va_start(pArgs, eChannel); uiIsolator = va_arg(pArgs, unsigned int); va_end(pArgs); bRtn &= IPMB_MediumBufferDisable(&pstDesc->m_pstMedium[0], uiIsolator); } } } return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_BufferReady(IPMBBus_t eBus, IPMBChannel_t eChannel, ...) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = true; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bRtn &= IPMB_MediumBufferReady(&pstDesc->m_pstMedium[0]); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bRtn &= IPMB_MediumBufferReady(&pstDesc->m_pstMedium[1]); } } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) bRtn &= pstDesc->m_pstMedium[0].m_bEnabled; } } return bRtn; } /*----------------------------------------------------------------------------*/ bool IPMB_Busy(IPMBBus_t eBus, IPMBChannel_t eChannel) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; bool bRtn = false; if(pstDesc->m_pstMedium == NULL) { IPMB_SetError(eBus, IPMB_ERROR_NOT_INITIALIZED); return false; } if (eBus == IPMB_0) { bool bBusyA = false; bool bBusyB = false; if ((eChannel & IPMB_CHANNEL_IPMB_A) == IPMB_CHANNEL_IPMB_A) { if(pstDesc->m_ucNbMedium > 0) bBusyA |= I2C_MasterBusBusy(pstDesc->m_pstMedium[0].m_uiI2Cx); } if ((eChannel & IPMB_CHANNEL_IPMB_B) == IPMB_CHANNEL_IPMB_B) { if(pstDesc->m_ucNbMedium > 1) bBusyB |= I2C_MasterBusBusy(pstDesc->m_pstMedium[1].m_uiI2Cx); } bRtn = bBusyA && bBusyB; } else { if ((eChannel & IPMB_CHANNEL_IPMB_L) == IPMB_CHANNEL_IPMB_L) { if(pstDesc->m_ucNbMedium > 0) bRtn |= I2C_MasterBusBusy(pstDesc->m_pstMedium[0].m_uiI2Cx); } } return bRtn; } /*----------------------------------------------------------------------------*/ IPMBError_t IPMB_GetError(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return IPMB_ERROR_BAD_BUS_IDENTIFIER; IPMBError_t eError = s_astIPMBDesc[eBus].m_eError; /* Reset IPMB error status */ s_astIPMBDesc[eBus].m_eError = IPMB_ERROR_OK; return eError; } /*----------------------------------------------------------------------------*/ I2CError_t IPMB_GetI2CError(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return I2C_ERROR_UNKOWN; I2CError_t eError = s_astIPMBDesc[eBus].m_stI2CStatus.m_eI2CError; /* Reset I2C error status */ s_astIPMBDesc[eBus].m_stI2CStatus.m_uiI2Cx = 0; s_astIPMBDesc[eBus].m_stI2CStatus.m_eI2CError = I2C_ERROR_OK; s_astIPMBDesc[eBus].m_stI2CStatus.m_szErrorMessage[0] = '\0'; return eError; } /*----------------------------------------------------------------------------*/ char *IPMB_GetI2CStrError(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return "[IPMB-?] unknown error"; uint32_t uiI2Cx = s_astIPMBDesc[eBus].m_stI2CStatus.m_uiI2Cx; char *szErrorMessage = s_astIPMBDesc[eBus].m_stI2CStatus.m_szErrorMessage; I2CError_t eError = IPMB_GetI2CError(eBus); switch (eError) { case I2C_ERROR_OK: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Success", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_BUS_ERROR: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Bus error", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_ARBITRATION_LOST: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Arbritation lost", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_ACK_FAILURE: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Acknlowledge failure", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_OUT_OF_BAND: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Overrun/underrun error", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_PEC_ERROR: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Packet Error Checking error in receiption", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_TIMEOUT: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Timeout or Tlow error", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_SMB_ALERT: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] SMBus alert", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_TX_BUF_NOT_ALLOCATED: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Transmit buffer not allocated", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_RX_BUF_NOT_ALLOCATED: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Receive buffer not allocated", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_TX_BUF_NO_SPACE: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Transmit buffer not enough space", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_RX_BUF_NO_SPACE: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Receive buffer not enough space", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_RX_BUF_OVERFLOW: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Receive buffer overflow", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_INVALID_ARGUMENT: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Invalid argument", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_BAD_MCU_IDENTIFIER: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Bad MCU identifier", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_PERMISSION_DENIED: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Permission denied", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_ALREADY_IN_USE: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Peripheral already in use", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_MESSAGE_TOO_LONG: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Message too long", I2C_ToString(uiI2Cx)); break; case I2C_ERROR_RESOURCE_BUSY: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] Resource busy", I2C_ToString(uiI2Cx)); break; default: snprintf(szErrorMessage, IPMB_ERROR_MSG_LENGTH, "[%s] unknown error: 0x%08x", I2C_ToString(uiI2Cx), eError); break; } return szErrorMessage; } /*----------------------------------------------------------------------------*/ bool IPMB_SendMessage(IPMBBus_t eBus, void *pvMessage, uint32_t uiLength) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return false; IPMBPacketHeader_t *pstHeader = pvMessage; if (pstHeader == NULL) { IPMB_SetError(eBus, IPMB_ERROR_INVALID_ARGUMENT); return false; } bool bRtn; if ((pstHeader->m_netFn & 0x1) == 0x1) bRtn = IPMB_SendResponse(eBus, pvMessage, uiLength); else bRtn = IPMB_SendRequest(eBus, pvMessage, uiLength); return bRtn; } /*----------------------------------------------------------------------------*/ void IPMB_SetDebugLevel(uint8_t ucLevel) /*----------------------------------------------------------------------------*/ { s_ucDebugLevel = ucLevel; } /*----------------------------------------------------------------------------*/ void IPMB_DumpStats(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; IPMBStats_t *pstStats = &s_astIPMBStats[eBus]; for (size_t i = 0; i < pstDesc->m_ucNbMedium; i++) { QueueDump(pstDesc->m_pstMedium[i].m_pstQueue, I2C_ToString(pstDesc->m_pstMedium[i].m_uiI2Cx)); } // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of messages allocated: %lu\n", pstStats->m_uiRx.m_uiNbPacketsAlloc); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of messages freed : %lu\n", pstStats->m_uiRx.m_uiNbPacketsFree); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of messages processed: %lu\n", pstStats->m_uiRx.m_uiNbPackets); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of packets allocated : %lu\n", pstStats->m_uiRx.m_uiNbPacketsAlloc); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of packets freed : %lu\n", pstStats->m_uiRx.m_uiNbPacketsFree); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> # of packets processed : %lu\n", pstStats->m_uiRx.m_uiNbPackets); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Rx> Total bytes : %lu\n\n", pstStats->m_uiRx.m_uiTotalBytes); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of messages allocated: %lu\n", pstStats->m_uiTx.m_uiNbMessagesAlloc); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of messages freed : %lu\n", pstStats->m_uiTx.m_uiNbMessagesFree); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of messages processed: %lu\n", pstStats->m_uiTx.m_uiNbMessages); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of packets allocated : %lu\n", pstStats->m_uiTx.m_uiNbPacketsAlloc); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of packets freed : %lu\n", pstStats->m_uiTx.m_uiNbPacketsFree); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> # of packets processed : %lu\n", pstStats->m_uiTx.m_uiNbPackets); // cppcheck-suppress invalidPrintfArgType_uint printf("STATS-Tx> Total bytes : %lu\n\n", pstStats->m_uiTx.m_uiTotalBytes); } /*----------------------------------------------------------------------------*/ void IPMB_DumpQueueInfo(IPMBBus_t eBus) /*----------------------------------------------------------------------------*/ { if ((eBus != IPMB_0) && (eBus != IPMB_L)) return; IPMBDescriptor_t *pstDesc = &s_astIPMBDesc[eBus]; for (size_t i = 0; i < pstDesc->m_ucNbMedium; i++) { QueueDump(pstDesc->m_pstMedium[i].m_pstQueue, I2C_ToString(pstDesc->m_pstMedium[i].m_uiI2Cx)); printf("%s: pointer stored in the queue\n", I2C_ToString(pstDesc->m_pstMedium[i].m_uiI2Cx)); for (uint16_t j = 0; j < QueueSize(pstDesc->m_pstMedium[i].m_pstQueue); j++) { printf(" - %p\n", QueueAt(pstDesc->m_pstMedium[i].m_pstQueue, j)); } } } /** * @} */ /** * @} */