zephyr/kernel/microkernel/k_pipe_buffer.c
Allan Stephens 11994f52bf Rename and relocate miscellaneous microkernel pipe files
Brings file names into alignment with current naming conventions,
and relocates them to the main microkernel directory.

Note: Since the microkernel/channel directory is now empty, the
build system no longer attempts to build its contents.

Change-Id: I936c7cdf2e08f675dd66a87cacf3b4fa5a7a6441
Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
2016-02-05 20:14:01 -05:00

683 lines
18 KiB
C

/* 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 <microkernel/k_types.h>
#include <k_pipe_buffer.h>
#include <string.h>
#include <toolchain.h>
#include <sections.h>
#include <misc/__assert.h>
#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;
BuffReset(pChBuff);
}
void BuffReset(struct chbuff *pChBuff)
{
/* 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));
}
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);
}
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 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);
}
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;
}
/*******************/
/* Buffer en-queuing:
********************/
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);
}
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;
}
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;
}
}
}
/**********************/
/* Buffer de-queuing: */
/**********************/
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);
}
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;
}
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 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);
}
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);
}
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);
}
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);
}