/* k_pipe_buffer.c */ /* * Copyright (c) 1997-2015 Wind River Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3) Neither the name of Wind River Systems nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Implementation remarks: - when using a floating end pointer: do not use pChBuff->iBuffsize for (Buff->pEnd - pChBuff->pBegin) */ #include #include #include #include #include #include #define CHECK_CHBUFF_POINTER(pData) \ __ASSERT_NO_MSG(pChBuff->pBegin <= pData && pData < pChBuff->pEnd) /*******************/ /* Markers ********************/ static int MarkerFindFree(struct marker aMarkers[]) { struct marker *pM = aMarkers; int i; for (i = 0; i < MAXNBR_MARKERS; i++, pM++) { if (NULL == pM->pointer) { break; } } if (MAXNBR_MARKERS == i) { i = -1; } return i; } static void MarkerLinkToListAfter(struct marker aMarkers[], int iMarker, int iNewMarker) { int iNextMarker; /* index of next marker in original list */ /* let the original list be aware of the new marker */ if (-1 != iMarker) { iNextMarker = aMarkers[iMarker].Next; aMarkers[iMarker].Next = iNewMarker; if (-1 != iNextMarker) { aMarkers[iNextMarker].Prev = iNewMarker; } else { /* there was no next marker */ } } else { iNextMarker = -1; /* there wasn't even a marker */ } /* link the new marker with the marker and next marker */ aMarkers[iNewMarker].Prev = iMarker; aMarkers[iNewMarker].Next = iNextMarker; } static int MarkerAddLast(struct marker_list *pMarkerList, unsigned char *pointer, int iSize, BOOL bXferBusy) { int i = MarkerFindFree(pMarkerList->aMarkers); if (i == -1) { return i; } pMarkerList->aMarkers[i].pointer = pointer; pMarkerList->aMarkers[i].size = iSize; pMarkerList->aMarkers[i].bXferBusy = bXferBusy; if (-1 == pMarkerList->iFirstMarker) { __ASSERT_NO_MSG(-1 == pMarkerList->iLastMarker); pMarkerList->iFirstMarker = i; /* we still need to set Prev & Next */ } else { __ASSERT_NO_MSG(-1 != pMarkerList->iLastMarker); __ASSERT_NO_MSG(-1 == pMarkerList->aMarkers[pMarkerList->iLastMarker].Next); } MarkerLinkToListAfter(pMarkerList->aMarkers, pMarkerList->iLastMarker, i); __ASSERT_NO_MSG(-1 == pMarkerList->aMarkers[i].Next); pMarkerList->iLastMarker = i; #ifdef STORE_NBR_MARKERS pMarkerList->iNbrMarkers++; __ASSERT_NO_MSG(0 < pMarkerList->iNbrMarkers); #endif return i; } static void MarkerUnlinkFromList(struct marker aMarkers[], int iMarker, int *piPredecessor, int *piSuccessor) { int iNextMarker = aMarkers[iMarker].Next; int iPrevMarker = aMarkers[iMarker].Prev; /* remove the marker from the list */ aMarkers[iMarker].Next = -1; aMarkers[iMarker].Prev = -1; /* repair the chain */ if (-1 != iPrevMarker) { aMarkers[iPrevMarker].Next = iNextMarker; } if (-1 != iNextMarker) { aMarkers[iNextMarker].Prev = iPrevMarker; } *piPredecessor = iPrevMarker; *piSuccessor = iNextMarker; } static void MarkerDelete(struct marker_list *pMarkerList, int index) { int i; int iPredecessor; int iSuccessor; i = index; __ASSERT_NO_MSG(-1 != i); pMarkerList->aMarkers[i].pointer = NULL; MarkerUnlinkFromList(pMarkerList->aMarkers, i, &iPredecessor, &iSuccessor); /* update first/last info */ if (i == pMarkerList->iLastMarker) { pMarkerList->iLastMarker = iPredecessor; } if (i == pMarkerList->iFirstMarker) { pMarkerList->iFirstMarker = iSuccessor; } #ifdef STORE_NBR_MARKERS pMarkerList->iNbrMarkers--; __ASSERT_NO_MSG(0 <= pMarkerList->iNbrMarkers); if (0 == pMarkerList->iNbrMarkers) { __ASSERT_NO_MSG(-1 == pMarkerList->iFirstMarker); __ASSERT_NO_MSG(-1 == pMarkerList->iLastMarker); } #endif } static void MarkersClear(struct marker_list *pMarkerList) { struct marker *pM = pMarkerList->aMarkers; int i; for (i = 0; i < MAXNBR_MARKERS; i++, pM++) { k_memset(pM, 0, sizeof(struct marker)); pM->Next = -1; pM->Prev = -1; } #ifdef STORE_NBR_MARKERS pMarkerList->iNbrMarkers = 0; #endif pMarkerList->iFirstMarker = -1; pMarkerList->iLastMarker = -1; pMarkerList->iAWAMarker = -1; } /********************************************************************************/ /* note on setting/clearing markers/guards: If there is at least one marker, there is a guard and equals one of the markers; if there are no markers (*), there is no guard. Consequently, if a marker is add when there were none, the guard will equal it. If additional markers are add, the guard will not change. However, if a marker is deleted: if it equals the guard a new guard must be selected (**) if not, guard doesn't change (*) we need to housekeep how much markers there are or we can inspect the guard (**) for this, the complete markers table needs to be investigated */ /***************************************/ /* This function will see if one or more 'areas' in the buffer can be made available (either for writing xor reading). Note: such a series of areas starts from the beginning. */ static int ScanMarkers(struct marker_list *pMarkerList, int *piSizeBWA, int *piSizeAWA, int *piNbrPendingXfers) { struct marker *pM; BOOL bMarkersAreNowAWA; int index; index = pMarkerList->iFirstMarker; __ASSERT_NO_MSG(-1 != index); bMarkersAreNowAWA = FALSE; do { int index_next; __ASSERT_NO_MSG(index == pMarkerList->iFirstMarker); if (index == pMarkerList->iAWAMarker) { bMarkersAreNowAWA = TRUE; /* from now on, everything is AWA */ } pM = &(pMarkerList->aMarkers[index]); if (pM->bXferBusy == TRUE) { break; } if (!bMarkersAreNowAWA) { *piSizeBWA += pM->size; } else { *piSizeAWA += pM->size; } index_next = pM->Next; /* pMarkerList->iFirstMarker will be updated */ MarkerDelete(pMarkerList, index); /* adjust *piNbrPendingXfers */ if (piNbrPendingXfers) { __ASSERT_NO_MSG(0 <= *piNbrPendingXfers); (*piNbrPendingXfers)--; } index = index_next; } while (-1 != index); __ASSERT_NO_MSG(index == pMarkerList->iFirstMarker); if (bMarkersAreNowAWA) { pMarkerList->iAWAMarker = pMarkerList->iFirstMarker; } #ifdef STORE_NBR_MARKERS if (0 == pMarkerList->iNbrMarkers) { __ASSERT_NO_MSG(-1 == pMarkerList->iFirstMarker); __ASSERT_NO_MSG(-1 == pMarkerList->iLastMarker); __ASSERT_NO_MSG(-1 == pMarkerList->iAWAMarker); } #endif return pMarkerList->iFirstMarker; } /*******************/ /* General ********************/ void BuffInit(unsigned char *pBuffer, int *piBuffSize, struct chbuff *pChBuff) { pChBuff->pBegin = pBuffer; pChBuff->iBuffSize = *piBuffSize; /* reset all pointers */ pChBuff->pEnd = pChBuff->pBegin + OCTET_TO_SIZEOFUNIT(pChBuff->iBuffSize); pChBuff->pEndOrig = pChBuff->pEnd; /* assumed it is allowed */ pChBuff->BuffState = BUFF_EMPTY; pChBuff->pEnd = pChBuff->pEndOrig; pChBuff->pWrite = pChBuff->pBegin; pChBuff->pWriteGuard = NULL; pChBuff->bWriteWA = FALSE; pChBuff->pRead = pChBuff->pBegin; pChBuff->pReadGuard = NULL; pChBuff->bReadWA = TRUE; /* YES!! */ pChBuff->iFreeSpaceCont = pChBuff->iBuffSize; pChBuff->iFreeSpaceAWA = 0; pChBuff->iNbrPendingReads = 0; pChBuff->iAvailDataCont = 0; pChBuff->iAvailDataAWA = 0; pChBuff->iNbrPendingWrites = 0; MarkersClear(&(pChBuff->WriteMarkers)); MarkersClear(&(pChBuff->ReadMarkers)); } int CalcFreeSpace(struct chbuff *pChBuff, int *piFreeSpaceCont, int *piFreeSpaceAWA) { unsigned char *pStart = pChBuff->pWrite; unsigned char *pStop = pChBuff->pRead; if (NULL != pChBuff->pWriteGuard) { pStop = pChBuff->pWriteGuard; } else { /* * if BuffState==BUFF_EMPTY but we have a WriteGuard, * we still need to calculate it as a normal [Start,Stop] interval */ if (BUFF_EMPTY == pChBuff->BuffState) { *piFreeSpaceCont = SIZEOFUNIT_TO_OCTET(pChBuff->pEnd - pStart); *piFreeSpaceAWA = SIZEOFUNIT_TO_OCTET(pStop - pChBuff->pBegin); return (*piFreeSpaceCont + *piFreeSpaceAWA); /* this sum equals pEnd-pBegin */ } } /* * on the other hand, if BuffState is full, we do not need a special flow; * it will be correct as (pStop - pStart) equals 0 */ if (pStop >= pStart) { *piFreeSpaceCont = SIZEOFUNIT_TO_OCTET(pStop - pStart); *piFreeSpaceAWA = 0; } else { *piFreeSpaceCont = SIZEOFUNIT_TO_OCTET(pChBuff->pEnd - pStart); *piFreeSpaceAWA = SIZEOFUNIT_TO_OCTET(pStop - pChBuff->pBegin); } return (*piFreeSpaceCont + *piFreeSpaceAWA); } void BuffGetFreeSpace(struct chbuff *pChBuff, int *piFreeSpaceTotal, int *piFreeSpaceCont, int *piFreeSpaceAWA) { int iFreeSpaceCont; int iFreeSpaceAWA; int iFreeSpaceTotal; iFreeSpaceTotal = CalcFreeSpace(pChBuff, &iFreeSpaceCont, &iFreeSpaceAWA); __ASSERT_NO_MSG(iFreeSpaceCont == pChBuff->iFreeSpaceCont); __ASSERT_NO_MSG(iFreeSpaceAWA == pChBuff->iFreeSpaceAWA); *piFreeSpaceTotal = iFreeSpaceTotal; *piFreeSpaceCont = pChBuff->iFreeSpaceCont; *piFreeSpaceAWA = pChBuff->iFreeSpaceAWA; } void BuffGetFreeSpaceTotal(struct chbuff *pChBuff, int *piFreeSpaceTotal) { int dummy1, dummy2; *piFreeSpaceTotal = CalcFreeSpace(pChBuff, &dummy1, &dummy2); __ASSERT_NO_MSG(dummy1 == pChBuff->iFreeSpaceCont); __ASSERT_NO_MSG(dummy2 == pChBuff->iFreeSpaceAWA); } int BuffEmpty(struct chbuff *pChBuff) { /* 0==iAvailDataTotal is an INcorrect condition b/c of async behavior */ int iTotalFreeSpace; BuffGetFreeSpaceTotal(pChBuff, &iTotalFreeSpace); return (pChBuff->iBuffSize == iTotalFreeSpace); } int CalcAvailData(struct chbuff *pChBuff, int *piAvailDataCont, int *piAvailDataAWA) { unsigned char *pStart = pChBuff->pRead; unsigned char *pStop = pChBuff->pWrite; if (NULL != pChBuff->pReadGuard) { pStop = pChBuff->pReadGuard; } else { /* * if BuffState==BUFF_FULL but we have a ReadGuard, * we still need to calculate it as a normal [Start,Stop] interval */ if (BUFF_FULL == pChBuff->BuffState) { *piAvailDataCont = SIZEOFUNIT_TO_OCTET(pChBuff->pEnd - pStart); *piAvailDataAWA = SIZEOFUNIT_TO_OCTET(pStop - pChBuff->pBegin); return (*piAvailDataCont + *piAvailDataAWA); /* this sum equals pEnd-pBegin */ } } /* * on the other hand, if BuffState is empty, we do not need a special flow; * it will be correct as (pStop - pStart) equals 0 */ if (pStop >= pStart) { *piAvailDataCont = SIZEOFUNIT_TO_OCTET(pStop - pStart); *piAvailDataAWA = 0; } else { *piAvailDataCont = SIZEOFUNIT_TO_OCTET(pChBuff->pEnd - pStart); *piAvailDataAWA = SIZEOFUNIT_TO_OCTET(pStop - pChBuff->pBegin); } return (*piAvailDataCont + *piAvailDataAWA); } void BuffGetAvailData(struct chbuff *pChBuff, int *piAvailDataTotal, int *piAvailDataCont, int *piAvailDataAWA) { int iAvailDataCont; int iAvailDataAWA; int iAvailDataTotal; iAvailDataTotal = CalcAvailData(pChBuff, &iAvailDataCont, &iAvailDataAWA); __ASSERT_NO_MSG(iAvailDataCont == pChBuff->iAvailDataCont); __ASSERT_NO_MSG(iAvailDataAWA == pChBuff->iAvailDataAWA); *piAvailDataTotal = iAvailDataTotal; *piAvailDataCont = pChBuff->iAvailDataCont; *piAvailDataAWA = pChBuff->iAvailDataAWA; } void BuffGetAvailDataTotal(struct chbuff *pChBuff, int *piAvailDataTotal) { int dummy1, dummy2; *piAvailDataTotal = CalcAvailData(pChBuff, &dummy1, &dummy2); __ASSERT_NO_MSG(dummy1 == pChBuff->iAvailDataCont); __ASSERT_NO_MSG(dummy2 == pChBuff->iAvailDataAWA); } int BuffFull(struct chbuff *pChBuff) { /* 0==iTotalFreeSpace is an INcorrect condition b/c of async behavior */ int iAvailDataTotal; BuffGetAvailDataTotal(pChBuff, &iAvailDataTotal); return (pChBuff->iBuffSize == iAvailDataTotal); } /*******************/ /* Buffer en-queuing: ********************/ static int AsyncEnQRegstr(struct chbuff *pChBuff, int iSize) { int i; ChannelCheck4Intrusion(pChBuff, pChBuff->pWrite, iSize); i = MarkerAddLast(&(pChBuff->WriteMarkers), pChBuff->pWrite, iSize, TRUE); if (i != -1) { /* adjust iNbrPendingWrites */ __ASSERT_NO_MSG(0 <= pChBuff->iNbrPendingWrites); (pChBuff->iNbrPendingWrites)++; /* pReadGuard changes? */ if (NULL == pChBuff->pReadGuard) { pChBuff->pReadGuard = pChBuff->pWrite; } __ASSERT_NO_MSG(pChBuff->WriteMarkers.aMarkers [pChBuff->WriteMarkers.iFirstMarker].pointer == pChBuff->pReadGuard); /* iAWAMarker changes? */ if (-1 == pChBuff->WriteMarkers.iAWAMarker && pChBuff->bWriteWA) { pChBuff->WriteMarkers.iAWAMarker = i; } } return i; } static void AsyncEnQFinished(struct chbuff *pChBuff, int iTransferID) { pChBuff->WriteMarkers.aMarkers[iTransferID].bXferBusy = FALSE; if (pChBuff->WriteMarkers.iFirstMarker == iTransferID) { int iNewFirstMarker = ScanMarkers(&(pChBuff->WriteMarkers), &(pChBuff->iAvailDataCont), &(pChBuff->iAvailDataAWA), &(pChBuff->iNbrPendingWrites)); if (-1 != iNewFirstMarker) { pChBuff->pReadGuard = pChBuff->WriteMarkers.aMarkers[iNewFirstMarker].pointer; } else { pChBuff->pReadGuard = NULL; } } } int BuffEnQ(struct chbuff *pChBuff, int iSize, unsigned char **ppWrite) { int iTransferID; if (0 == BuffEnQA(pChBuff, iSize, ppWrite, &iTransferID)) { return 0; } /* check ret value */ BuffEnQA_End(pChBuff, iTransferID, iSize /* optional */); return iSize; } int BuffEnQA(struct chbuff *pChBuff, int iSize, unsigned char **ppWrite, int *piTransferID) { if (iSize > pChBuff->iFreeSpaceCont) { return 0; } *piTransferID = AsyncEnQRegstr(pChBuff, iSize); if (-1 == *piTransferID) { return 0; } *ppWrite = pChBuff->pWrite; /* adjust write pointer and free space*/ pChBuff->pWrite += OCTET_TO_SIZEOFUNIT(iSize); if (pChBuff->pEnd == pChBuff->pWrite) { pChBuff->pWrite = pChBuff->pBegin; pChBuff->iFreeSpaceCont = pChBuff->iFreeSpaceAWA; pChBuff->iFreeSpaceAWA = 0; pChBuff->bWriteWA = TRUE; pChBuff->bReadWA = FALSE; pChBuff->ReadMarkers.iAWAMarker = -1; } else { pChBuff->iFreeSpaceCont -= iSize; } if (pChBuff->pWrite == pChBuff->pRead) { pChBuff->BuffState = BUFF_FULL; } else { pChBuff->BuffState = BUFF_OTHER; } CHECK_CHBUFF_POINTER(pChBuff->pWrite); return iSize; } void BuffEnQA_End(struct chbuff *pChBuff, int iTransferID, int iSize /* optional */) { ARG_UNUSED(iSize); /* An asynchronous data transfer to the buffer has finished */ AsyncEnQFinished(pChBuff, iTransferID); } /**********************/ /* Buffer de-queuing: */ /**********************/ static int AsyncDeQRegstr(struct chbuff *pChBuff, int iSize) { int i; ChannelCheck4Intrusion(pChBuff, pChBuff->pRead, iSize); i = MarkerAddLast(&(pChBuff->ReadMarkers), pChBuff->pRead, iSize, TRUE); if (i != -1) { /* adjust iNbrPendingReads */ __ASSERT_NO_MSG(0 <= pChBuff->iNbrPendingReads); (pChBuff->iNbrPendingReads)++; /* pWriteGuard changes? */ if (NULL == pChBuff->pWriteGuard) { pChBuff->pWriteGuard = pChBuff->pRead; } __ASSERT_NO_MSG(pChBuff->ReadMarkers.aMarkers [pChBuff->ReadMarkers.iFirstMarker].pointer == pChBuff->pWriteGuard); /* iAWAMarker changes? */ if (-1 == pChBuff->ReadMarkers.iAWAMarker && pChBuff->bReadWA) { pChBuff->ReadMarkers.iAWAMarker = i; } } return i; } static void AsyncDeQFinished(struct chbuff *pChBuff, int iTransferID) { pChBuff->ReadMarkers.aMarkers[iTransferID].bXferBusy = FALSE; if (pChBuff->ReadMarkers.iFirstMarker == iTransferID) { int iNewFirstMarker = ScanMarkers(&(pChBuff->ReadMarkers), &(pChBuff->iFreeSpaceCont), &(pChBuff->iFreeSpaceAWA), &(pChBuff->iNbrPendingReads)); if (-1 != iNewFirstMarker) { pChBuff->pWriteGuard = pChBuff->ReadMarkers.aMarkers[iNewFirstMarker].pointer; } else { pChBuff->pWriteGuard = NULL; } } } int BuffDeQ(struct chbuff *pChBuff, int iSize, unsigned char **ppRead) { int iTransferID; if (0 == BuffDeQA(pChBuff, iSize, ppRead, &iTransferID)) { return 0; } BuffDeQA_End(pChBuff, iTransferID, iSize /* optional */); return iSize; } int BuffDeQA(struct chbuff *pChBuff, int iSize, unsigned char **ppRead, int *piTransferID) { /* asynchronous data transfer; read guard pointers must be set */ if (iSize > pChBuff->iAvailDataCont) { /* free space is from read to guard pointer/end pointer */ return 0; } *piTransferID = AsyncDeQRegstr(pChBuff, iSize); if (-1 == *piTransferID) { return 0; } *ppRead = pChBuff->pRead; /* adjust read pointer and avail data */ pChBuff->pRead += OCTET_TO_SIZEOFUNIT(iSize); if (pChBuff->pEnd == pChBuff->pRead) { pChBuff->pRead = pChBuff->pBegin; pChBuff->iAvailDataCont = pChBuff->iAvailDataAWA; pChBuff->iAvailDataAWA = 0; pChBuff->bWriteWA = FALSE; pChBuff->bReadWA = TRUE; pChBuff->WriteMarkers.iAWAMarker = -1; } else { pChBuff->iAvailDataCont -= iSize; } if (pChBuff->pWrite == pChBuff->pRead) { pChBuff->BuffState = BUFF_EMPTY; } else { pChBuff->BuffState = BUFF_OTHER; } CHECK_CHBUFF_POINTER(pChBuff->pRead); return iSize; } void BuffDeQA_End(struct chbuff *pChBuff, int iTransferID, int iSize /* optional */) { ARG_UNUSED(iSize); /* An asynchronous data transfer from the buffer has finished */ AsyncDeQFinished(pChBuff, iTransferID); }