Share via


Memory Utilization (Windows Embedded CE 6.0)

1/6/2010

The Bluetooth stack supports fast, efficient memory management that prevents excessive copying.

For internal memory structures, the Bluetooth stack uses fixed-memory allocation routines in Svsutil.hxx that provide O(1) allocation/free complexity for chunks of a fixed size.

For variable-length data, all functions that perform buffer transfer reads and writes will pass the buffer in the BD_BUFFER structure.

typedef void (*BD_BUFFER_FREE) (struct BD_BUFFER *pBuff);
struct BD_BUFFER {
   int              cSize;
   int              cStart;
   int              cEnd;
   BD_BUFFER_FREE   pFree;
   int              fMustCopy;
   unsigned char   *pBuffer;
};

cSize is the total size of the buffer pointed to by pBuffer. cStart and cEnd are offsets to user data at the current layer. What is between the start of pBuffer and pBuffer[cStart] and pBuffer[cEnd] and pBuffer[cSize] are protocol headers and trailers of lower levels of the stack.

This allows the constructor of the package to pre-allocate all space for all headers when the packet is just being constructed, to avoid excessive copying.

pFree is a pointer to the function that is responsible to free the data buffer, and fMustCopy is TRUE if the stack layers, to which the buffer is being passed, are not allowed to own the buffer after the function returns, and so they must copy it before they return.

The following utility functions are used to allocate the stack and access it.

inline int BufferTotal (BD_BUFFER *pB) {
   return pB->cEnd - pB->cStart;
}
inline int BufferGetByte (BD_BUFFER *pB, unsigned char *puc) {
   if (pB->cEnd - pB->cStart < 1)
      return FALSE;
   *puc = pB->pBuffer[pB->cStart++];
   return TRUE;
}
inline int BufferGetShort (BD_BUFFER *pB, unsigned short *pus) {
   if (pB->cEnd - pB->cStart < 2)
      return FALSE;

   *pus = pB->pBuffer[pB->cStart] | (pB->pBuffer[pB->cStart + 1] << 8);
   pB->cStart += 2;
   return TRUE;
}
inline int BufferGetChunk (BD_BUFFER *pB, int cChunk, unsigned char *pChunk) {
   if (pB->cEnd - pB->cStart < cChunk)
      return FALSE;
   memcpy (pChunk, pB->pBuffer + pB->cStart, cChunk);
   pB->cStart += cChunk;
   return TRUE;
}
void BufferFree (BD_BUFFER *pBuf);
BD_BUFFER   *BufferAlloc (int cSize);
BD_BUFFER   *BufferCopy (BD_BUFFER  *pBuffer);

Buffers do not have to be allocated by these functions, but pFree MUST be correctly set to prevent leaks.

Typical life cycle of a buffer on the way down for user data

The TDI layer allocates the packet using BufferAlloc including header and trailer sizes obtained during RFCOMM initialization. These values include the L2CAP header and trailer sizes, which in turn include the HCI transport header and trailer bytes, and other information.

The TDI layer puts its data in the middle and sets cStart and cEnd appropriately. It then passes its data down to RFCOMM. The RFCOMM inserts its headers and adjusts cStart and cEnd and gives buffer to the L2CAP.

The L2CAP inserts its own header in the same way and passes the buffers to the HCI.

The HCI queues the buffer for send. The thread then returns all the way up to the TDI. The TDI does not own the buffer any more because it did not set fMustCopy flag in the buffer. The TDI simply forgets about the buffer.

When the HCI finishes sending the buffer, it calls the pFree function in it, which is set to BufferFree by BufferAlloc.

Typical life cycle of a buffer on the way up for user data (small buffer)

If the buffer fits in just one HCI transport packet, meaning there is no fragmentation on the L2CAP layer, it is read into a static buffer in the HCI. The HCI then wraps the buffer into the BD_BUFFER structure, sets pFree to NULL, and fMustCopy to TRUE. The HCI then passes the buffer to the L2CAP.

The L2CAP then reads the L2CAP header out of that buffer using BufferGetChunk, which causes cStart to advance past the L2CAP header. The L2CAP then passes the buffer up to RFCOMM.

RFCOMM removes its own headers and trailers using BufferGetChunk. If it is a signaling command, the RFCOMM processes it and drops the buffer. If it is user data, RFCOMM calls up to the TDI.

The TDI sees user data and either immediately copies it into the user's buffers, if available, or queues for a read by duplicating the buffer with BufferCopy.

Typical life cycle of a buffer on the way up for user data (big buffer)

The big buffer is allocated by the L2CAP using BufferAlloc. This buffer is then passed up all the way to the consumer. The consumer is then responsible for freeing the buffer using pFree when it is done with the buffer.

Typical life cycle of RFCOMM signaling command

Buffer for a command is allocated on the stack and wrapped in the BD_BUFFER structure with pFree set to NULL and fMustCopy set to TRUE. All headers must be pre-allocated. The buffer is then passed down to the L2CAP layer. L2CAP inserts its own headers and passes the buffer to the HCI layer. HCI creates a new buffer for queueing by using the BufferCopy function. When the command is sent, HCI destroys the buffer that was created by BufferCopy in HCI.

See Also

Concepts

Common Layer Characteristics
Bluetooth Protocol Stack