use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::config::*;
use crate::kernel::tasks::*;
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::memory::{pvPortMalloc, vPortFree};
use crate::port::*;
use crate::types::*;
pub const sbTYPE_STREAM_BUFFER: BaseType_t = 0;
pub const sbTYPE_MESSAGE_BUFFER: BaseType_t = 1;
pub const sbTYPE_STREAM_BATCHING_BUFFER: BaseType_t = 2;
const sbBYTES_TO_STORE_MESSAGE_LENGTH: usize =
core::mem::size_of::<configMESSAGE_BUFFER_LENGTH_TYPE>();
const sbFLAGS_IS_MESSAGE_BUFFER: u8 = 1;
const sbFLAGS_IS_STATICALLY_ALLOCATED: u8 = 2;
const sbFLAGS_IS_BATCHING_BUFFER: u8 = 4;
pub type configMESSAGE_BUFFER_LENGTH_TYPE = u32;
const tskDEFAULT_INDEX_TO_NOTIFY: UBaseType_t = 0;
pub type StreamBufferHandle_t = *mut c_void;
pub type StreamBufferCallbackFunction_t = Option<
extern "C" fn(
xStreamBuffer: StreamBufferHandle_t,
xIsInsideISR: BaseType_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
),
>;
#[repr(C)]
pub struct StreamBuffer_t {
pub xTail: AtomicUsize,
pub xHead: AtomicUsize,
pub xLength: usize,
pub xTriggerLevelBytes: AtomicUsize,
pub xTaskWaitingToReceive: AtomicPtr<c_void>,
pub xTaskWaitingToSend: AtomicPtr<c_void>,
pub pucBuffer: *mut u8,
pub ucFlags: u8,
#[cfg(feature = "trace-facility")]
pub uxStreamBufferNumber: UBaseType_t,
pub pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pub pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
pub uxNotificationIndex: AtomicUsize,
}
#[repr(transparent)]
pub struct StaticStreamBuffer_t {
_storage: core::mem::MaybeUninit<StreamBuffer_t>,
}
impl StaticStreamBuffer_t {
pub const fn new() -> Self {
StaticStreamBuffer_t {
_storage: core::mem::MaybeUninit::uninit(),
}
}
}
pub type StaticMessageBuffer_t = StaticStreamBuffer_t;
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub unsafe fn xStreamBufferGenericCreate(
xBufferSizeBytes: usize,
mut xTriggerLevelBytes: usize,
xStreamBufferType: BaseType_t,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
let ucFlags: u8;
if xBufferSizeBytes == 0
|| xTriggerLevelBytes > xBufferSizeBytes
|| (xStreamBufferType == sbTYPE_MESSAGE_BUFFER
&& xBufferSizeBytes <= sbBYTES_TO_STORE_MESSAGE_LENGTH)
{
return ptr::null_mut();
}
if xStreamBufferType == sbTYPE_MESSAGE_BUFFER {
ucFlags = sbFLAGS_IS_MESSAGE_BUFFER;
configASSERT(xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH);
} else if xStreamBufferType == sbTYPE_STREAM_BATCHING_BUFFER {
ucFlags = sbFLAGS_IS_BATCHING_BUFFER;
configASSERT(xBufferSizeBytes > 0);
} else {
ucFlags = 0;
configASSERT(xBufferSizeBytes > 0);
}
configASSERT(xTriggerLevelBytes <= xBufferSizeBytes);
if xTriggerLevelBytes == 0 {
xTriggerLevelBytes = 1;
}
let Some(xBufferSizeBytes) = xBufferSizeBytes.checked_add(1) else {
return ptr::null_mut();
};
let Some(total_size) = core::mem::size_of::<StreamBuffer_t>().checked_add(xBufferSizeBytes)
else {
return ptr::null_mut();
};
let pvAllocatedMemory = pvPortMalloc(total_size) as *mut u8;
if !pvAllocatedMemory.is_null() {
let pxStreamBuffer = pvAllocatedMemory as *mut StreamBuffer_t;
let pucBuffer = pvAllocatedMemory.add(core::mem::size_of::<StreamBuffer_t>());
prvInitialiseNewStreamBuffer(
pxStreamBuffer,
pucBuffer,
xBufferSizeBytes,
xTriggerLevelBytes,
ucFlags,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
);
pxStreamBuffer as StreamBufferHandle_t
} else {
ptr::null_mut()
}
}
pub unsafe fn xStreamBufferGenericCreateStatic(
xBufferSizeBytes: usize,
mut xTriggerLevelBytes: usize,
xStreamBufferType: BaseType_t,
pucStreamBufferStorageArea: *mut u8,
pxStaticStreamBuffer: *mut StaticStreamBuffer_t,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
configASSERT(!pucStreamBufferStorageArea.is_null());
configASSERT(!pxStaticStreamBuffer.is_null());
configASSERT(xTriggerLevelBytes <= xBufferSizeBytes);
if pucStreamBufferStorageArea.is_null()
|| pxStaticStreamBuffer.is_null()
|| xBufferSizeBytes == 0
|| xTriggerLevelBytes > xBufferSizeBytes
|| (xStreamBufferType == sbTYPE_MESSAGE_BUFFER
&& xBufferSizeBytes <= sbBYTES_TO_STORE_MESSAGE_LENGTH)
{
return ptr::null_mut();
}
if xTriggerLevelBytes == 0 {
xTriggerLevelBytes = 1;
}
let ucFlags: u8;
if xStreamBufferType == sbTYPE_MESSAGE_BUFFER {
ucFlags = sbFLAGS_IS_MESSAGE_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED;
configASSERT(xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH);
} else if xStreamBufferType == sbTYPE_STREAM_BATCHING_BUFFER {
ucFlags = sbFLAGS_IS_BATCHING_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED;
configASSERT(xBufferSizeBytes > 0);
} else {
ucFlags = sbFLAGS_IS_STATICALLY_ALLOCATED;
}
if !pucStreamBufferStorageArea.is_null() && !pxStaticStreamBuffer.is_null() {
let pxStreamBuffer = pxStaticStreamBuffer as *mut StreamBuffer_t;
prvInitialiseNewStreamBuffer(
pxStreamBuffer,
pucStreamBufferStorageArea,
xBufferSizeBytes,
xTriggerLevelBytes,
ucFlags,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
);
pxStreamBuffer as StreamBufferHandle_t
} else {
ptr::null_mut()
}
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xStreamBufferCreate(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreate(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BUFFER,
None,
None,
)
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xStreamBufferCreateWithCallback(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreate(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BUFFER,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xMessageBufferCreate(xBufferSizeBytes: usize) -> MessageBufferHandle_t {
xStreamBufferGenericCreate(xBufferSizeBytes, 0, sbTYPE_MESSAGE_BUFFER, None, None)
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xMessageBufferCreateWithCallback(
xBufferSizeBytes: usize,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> MessageBufferHandle_t {
xStreamBufferGenericCreate(
xBufferSizeBytes,
0,
sbTYPE_MESSAGE_BUFFER,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xStreamBatchingBufferCreate(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreate(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BATCHING_BUFFER,
None,
None,
)
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
#[inline(always)]
pub unsafe fn xStreamBatchingBufferCreateWithCallback(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreate(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BATCHING_BUFFER,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
#[inline(always)]
pub unsafe fn xStreamBufferCreateStatic(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pucStreamBufferStorageArea: *mut u8,
pxStaticStreamBuffer: *mut StaticStreamBuffer_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BUFFER,
pucStreamBufferStorageArea,
pxStaticStreamBuffer,
None,
None,
)
}
#[inline(always)]
pub unsafe fn xStreamBufferCreateStaticWithCallback(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pucStreamBufferStorageArea: *mut u8,
pxStaticStreamBuffer: *mut StaticStreamBuffer_t,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BUFFER,
pucStreamBufferStorageArea,
pxStaticStreamBuffer,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
#[inline(always)]
pub unsafe fn xMessageBufferCreateStatic(
xBufferSizeBytes: usize,
pucMessageBufferStorageArea: *mut u8,
pxStaticMessageBuffer: *mut StaticMessageBuffer_t,
) -> MessageBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
0,
sbTYPE_MESSAGE_BUFFER,
pucMessageBufferStorageArea,
pxStaticMessageBuffer,
None,
None,
)
}
#[inline(always)]
pub unsafe fn xMessageBufferCreateStaticWithCallback(
xBufferSizeBytes: usize,
pucMessageBufferStorageArea: *mut u8,
pxStaticMessageBuffer: *mut StaticMessageBuffer_t,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> MessageBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
0,
sbTYPE_MESSAGE_BUFFER,
pucMessageBufferStorageArea,
pxStaticMessageBuffer,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
#[inline(always)]
pub unsafe fn xStreamBatchingBufferCreateStatic(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pucStreamBufferStorageArea: *mut u8,
pxStaticStreamBuffer: *mut StaticStreamBuffer_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BATCHING_BUFFER,
pucStreamBufferStorageArea,
pxStaticStreamBuffer,
None,
None,
)
}
#[inline(always)]
pub unsafe fn xStreamBatchingBufferCreateStaticWithCallback(
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
pucStreamBufferStorageArea: *mut u8,
pxStaticStreamBuffer: *mut StaticStreamBuffer_t,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) -> StreamBufferHandle_t {
xStreamBufferGenericCreateStatic(
xBufferSizeBytes,
xTriggerLevelBytes,
sbTYPE_STREAM_BATCHING_BUFFER,
pucStreamBufferStorageArea,
pxStaticStreamBuffer,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
)
}
pub unsafe fn vStreamBufferDelete(xStreamBuffer: StreamBufferHandle_t) {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED) == 0 {
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
vPortFree(pxStreamBuffer as *mut c_void);
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
configASSERT(xStreamBuffer as usize == usize::MAX);
} else {
ptr::write_bytes(pxStreamBuffer, 0, 1);
}
}
pub unsafe fn xStreamBufferReset(xStreamBuffer: StreamBufferHandle_t) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReturn: BaseType_t = pdFAIL;
portENTER_CRITICAL();
{
if (*pxStreamBuffer)
.xTaskWaitingToReceive
.load(Ordering::Acquire)
.is_null()
&& (*pxStreamBuffer)
.xTaskWaitingToSend
.load(Ordering::Acquire)
.is_null()
{
let pxSendCompletedCallback = (*pxStreamBuffer).pxSendCompletedCallback;
let pxReceiveCompletedCallback = (*pxStreamBuffer).pxReceiveCompletedCallback;
#[cfg(feature = "trace-facility")]
let uxStreamBufferNumber = (*pxStreamBuffer).uxStreamBufferNumber;
prvInitialiseNewStreamBuffer(
pxStreamBuffer,
(*pxStreamBuffer).pucBuffer,
(*pxStreamBuffer).xLength,
(*pxStreamBuffer).xTriggerLevelBytes.load(Ordering::Relaxed),
(*pxStreamBuffer).ucFlags,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
);
#[cfg(feature = "trace-facility")]
{
(*pxStreamBuffer).uxStreamBufferNumber = uxStreamBufferNumber;
}
xReturn = pdPASS;
}
}
portEXIT_CRITICAL();
xReturn
}
pub unsafe fn xStreamBufferResetFromISR(xStreamBuffer: StreamBufferHandle_t) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReturn: BaseType_t = pdFAIL;
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
if (*pxStreamBuffer)
.xTaskWaitingToReceive
.load(Ordering::Acquire)
.is_null()
&& (*pxStreamBuffer)
.xTaskWaitingToSend
.load(Ordering::Acquire)
.is_null()
{
let pxSendCompletedCallback = (*pxStreamBuffer).pxSendCompletedCallback;
let pxReceiveCompletedCallback = (*pxStreamBuffer).pxReceiveCompletedCallback;
#[cfg(feature = "trace-facility")]
let uxStreamBufferNumber = (*pxStreamBuffer).uxStreamBufferNumber;
prvInitialiseNewStreamBuffer(
pxStreamBuffer,
(*pxStreamBuffer).pucBuffer,
(*pxStreamBuffer).xLength,
(*pxStreamBuffer).xTriggerLevelBytes.load(Ordering::Relaxed),
(*pxStreamBuffer).ucFlags,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
);
#[cfg(feature = "trace-facility")]
{
(*pxStreamBuffer).uxStreamBufferNumber = uxStreamBufferNumber;
}
xReturn = pdPASS;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
xReturn
}
pub unsafe fn xStreamBufferBytesAvailable(xStreamBuffer: StreamBufferHandle_t) -> usize {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
prvBytesInBuffer(pxStreamBuffer)
}
pub unsafe fn xStreamBufferSpacesAvailable(xStreamBuffer: StreamBufferHandle_t) -> usize {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
let xTail = (*pxStreamBuffer).xTail.load(Ordering::Acquire);
let xHead = (*pxStreamBuffer).xHead.load(Ordering::Relaxed);
let mut xSpace = (*pxStreamBuffer).xLength + xTail;
xSpace -= xHead;
xSpace -= 1;
if xSpace >= (*pxStreamBuffer).xLength {
xSpace -= (*pxStreamBuffer).xLength;
}
xSpace
}
pub unsafe fn xStreamBufferIsEmpty(xStreamBuffer: StreamBufferHandle_t) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
if (*pxStreamBuffer).xHead.load(Ordering::Acquire)
== (*pxStreamBuffer).xTail.load(Ordering::Acquire)
{
pdTRUE
} else {
pdFALSE
}
}
pub unsafe fn xStreamBufferIsFull(xStreamBuffer: StreamBufferHandle_t) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
let xBytesToStoreMessageLength = if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0
{
sbBYTES_TO_STORE_MESSAGE_LENGTH
} else {
0
};
if xStreamBufferSpacesAvailable(xStreamBuffer) <= xBytesToStoreMessageLength {
pdTRUE
} else {
pdFALSE
}
}
pub unsafe fn xStreamBufferSetTriggerLevel(
xStreamBuffer: StreamBufferHandle_t,
mut xTriggerLevel: usize,
) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
if xTriggerLevel == 0 {
xTriggerLevel = 1;
}
if xTriggerLevel < (*pxStreamBuffer).xLength {
(*pxStreamBuffer)
.xTriggerLevelBytes
.store(xTriggerLevel, Ordering::Relaxed);
pdPASS
} else {
pdFALSE
}
}
pub unsafe fn xStreamBufferNextMessageLengthBytes(xStreamBuffer: StreamBufferHandle_t) -> usize {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0 {
let xBytesAvailable = prvBytesInBuffer(pxStreamBuffer);
if xBytesAvailable > sbBYTES_TO_STORE_MESSAGE_LENGTH {
let mut xTempReturn: configMESSAGE_BUFFER_LENGTH_TYPE = 0;
prvReadBytesFromBuffer(
pxStreamBuffer,
&mut xTempReturn as *mut _ as *mut u8,
sbBYTES_TO_STORE_MESSAGE_LENGTH,
(*pxStreamBuffer).xTail.load(Ordering::Acquire),
);
xTempReturn as usize
} else {
configASSERT(xBytesAvailable == 0);
0
}
} else {
0
}
}
pub unsafe fn xStreamBufferSend(
xStreamBuffer: StreamBufferHandle_t,
pvTxData: *const c_void,
xDataLengthBytes: usize,
mut xTicksToWait: TickType_t,
) -> usize {
configASSERT(!pvTxData.is_null());
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xSpace: usize = 0;
let mut xRequiredSpace = xDataLengthBytes;
let xMaxReportedSpace = (*pxStreamBuffer).xLength - 1;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0 {
let Some(required_space) = xRequiredSpace.checked_add(sbBYTES_TO_STORE_MESSAGE_LENGTH)
else {
return 0;
};
xRequiredSpace = required_space;
if xRequiredSpace > xMaxReportedSpace {
xTicksToWait = 0;
}
} else {
if xRequiredSpace > xMaxReportedSpace {
xRequiredSpace = xMaxReportedSpace;
}
}
if xTicksToWait != 0 {
let mut xTimeOut = TimeOut_t::default();
unsafe { vTaskSetTimeOutState(&mut xTimeOut) };
loop {
portENTER_CRITICAL();
{
xSpace = xStreamBufferSpacesAvailable(xStreamBuffer);
if xSpace < xRequiredSpace {
let uxNotificationIndex = (*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed)
as UBaseType_t;
let _ = xTaskNotifyStateClearIndexed(ptr::null_mut(), uxNotificationIndex);
configASSERT(
(*pxStreamBuffer)
.xTaskWaitingToSend
.load(Ordering::Acquire)
.is_null(),
);
(*pxStreamBuffer)
.xTaskWaitingToSend
.store(xTaskGetCurrentTaskHandle(), Ordering::Release);
} else {
portEXIT_CRITICAL();
break;
}
}
portEXIT_CRITICAL();
let uxNotificationIndex = (*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t;
xTaskNotifyWaitIndexed(uxNotificationIndex, 0, 0, ptr::null_mut(), xTicksToWait);
(*pxStreamBuffer)
.xTaskWaitingToSend
.store(ptr::null_mut(), Ordering::Release);
if unsafe { xTaskCheckForTimeOut(&mut xTimeOut, &mut xTicksToWait) } != pdFALSE {
break;
}
}
}
if xSpace == 0 {
xSpace = xStreamBufferSpacesAvailable(xStreamBuffer);
}
let xReturn = prvWriteMessageToBuffer(
pxStreamBuffer,
pvTxData,
xDataLengthBytes,
xSpace,
xRequiredSpace,
);
if xReturn > 0 {
if prvBytesInBuffer(pxStreamBuffer)
>= (*pxStreamBuffer).xTriggerLevelBytes.load(Ordering::Relaxed)
{
prvSEND_COMPLETED(pxStreamBuffer);
}
}
xReturn
}
pub unsafe fn xStreamBufferSendFromISR(
xStreamBuffer: StreamBufferHandle_t,
pvTxData: *const c_void,
xDataLengthBytes: usize,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> usize {
configASSERT(!pvTxData.is_null());
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xRequiredSpace = xDataLengthBytes;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0 {
let Some(required_space) = xRequiredSpace.checked_add(sbBYTES_TO_STORE_MESSAGE_LENGTH)
else {
return 0;
};
xRequiredSpace = required_space;
}
let xSpace = xStreamBufferSpacesAvailable(xStreamBuffer);
let xReturn = prvWriteMessageToBuffer(
pxStreamBuffer,
pvTxData,
xDataLengthBytes,
xSpace,
xRequiredSpace,
);
if xReturn > 0 {
if prvBytesInBuffer(pxStreamBuffer)
>= (*pxStreamBuffer).xTriggerLevelBytes.load(Ordering::Relaxed)
{
prvSEND_COMPLETE_FROM_ISR(pxStreamBuffer, pxHigherPriorityTaskWoken);
}
}
xReturn
}
pub unsafe fn xStreamBufferReceive(
xStreamBuffer: StreamBufferHandle_t,
pvRxData: *mut c_void,
xBufferLengthBytes: usize,
xTicksToWait: TickType_t,
) -> usize {
configASSERT(!pvRxData.is_null());
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReceivedLength: usize = 0;
let mut xBytesAvailable: usize;
let xBytesToStoreMessageLength = if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0
{
sbBYTES_TO_STORE_MESSAGE_LENGTH
} else if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_BATCHING_BUFFER) != 0 {
(*pxStreamBuffer).xTriggerLevelBytes.load(Ordering::Relaxed)
} else {
0
};
if xTicksToWait != 0 {
portENTER_CRITICAL();
{
xBytesAvailable = prvBytesInBuffer(pxStreamBuffer);
if xBytesAvailable <= xBytesToStoreMessageLength {
let uxNotificationIndex = (*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed)
as UBaseType_t;
let _ = xTaskNotifyStateClearIndexed(ptr::null_mut(), uxNotificationIndex);
configASSERT(
(*pxStreamBuffer)
.xTaskWaitingToReceive
.load(Ordering::Acquire)
.is_null(),
);
(*pxStreamBuffer)
.xTaskWaitingToReceive
.store(xTaskGetCurrentTaskHandle(), Ordering::Release);
}
}
portEXIT_CRITICAL();
if xBytesAvailable <= xBytesToStoreMessageLength {
let uxNotificationIndex = (*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t;
xTaskNotifyWaitIndexed(uxNotificationIndex, 0, 0, ptr::null_mut(), xTicksToWait);
(*pxStreamBuffer)
.xTaskWaitingToReceive
.store(ptr::null_mut(), Ordering::Release);
xBytesAvailable = prvBytesInBuffer(pxStreamBuffer);
}
} else {
xBytesAvailable = prvBytesInBuffer(pxStreamBuffer);
}
if xBytesAvailable > xBytesToStoreMessageLength {
xReceivedLength = prvReadMessageFromBuffer(
pxStreamBuffer,
pvRxData,
xBufferLengthBytes,
xBytesAvailable,
);
if xReceivedLength != 0 {
prvRECEIVE_COMPLETED(pxStreamBuffer);
}
}
xReceivedLength
}
pub unsafe fn xStreamBufferReceiveFromISR(
xStreamBuffer: StreamBufferHandle_t,
pvRxData: *mut c_void,
xBufferLengthBytes: usize,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> usize {
configASSERT(!pvRxData.is_null());
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReceivedLength: usize = 0;
let xBytesToStoreMessageLength = if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0
{
sbBYTES_TO_STORE_MESSAGE_LENGTH
} else {
0
};
let xBytesAvailable = prvBytesInBuffer(pxStreamBuffer);
if xBytesAvailable > xBytesToStoreMessageLength {
xReceivedLength = prvReadMessageFromBuffer(
pxStreamBuffer,
pvRxData,
xBufferLengthBytes,
xBytesAvailable,
);
if xReceivedLength != 0 {
prvRECEIVE_COMPLETED_FROM_ISR(pxStreamBuffer, pxHigherPriorityTaskWoken);
}
}
xReceivedLength
}
pub unsafe fn uxStreamBufferGetStreamBufferNotificationIndex(
xStreamBuffer: StreamBufferHandle_t,
) -> UBaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t
}
pub unsafe fn vStreamBufferSetStreamBufferNotificationIndex(
xStreamBuffer: StreamBufferHandle_t,
uxNotificationIndex: UBaseType_t,
) {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
configASSERT(
(*pxStreamBuffer)
.xTaskWaitingToReceive
.load(Ordering::Acquire)
.is_null(),
);
configASSERT(
(*pxStreamBuffer)
.xTaskWaitingToSend
.load(Ordering::Acquire)
.is_null(),
);
configASSERT((uxNotificationIndex as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
if (uxNotificationIndex as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES {
(*pxStreamBuffer)
.uxNotificationIndex
.store(uxNotificationIndex as usize, Ordering::Relaxed);
}
}
pub unsafe fn xStreamBufferSendCompletedFromISR(
xStreamBuffer: StreamBufferHandle_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReturn: BaseType_t = pdFALSE;
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
let xTaskWaitingToReceive = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToReceive);
if !xTaskWaitingToReceive.is_null() {
xTaskNotifyIndexedFromISR(
xTaskWaitingToReceive,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
pxHigherPriorityTaskWoken,
);
xReturn = pdTRUE;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
xReturn
}
pub unsafe fn xStreamBufferReceiveCompletedFromISR(
xStreamBuffer: StreamBufferHandle_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let mut xReturn: BaseType_t = pdFALSE;
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
let xTaskWaitingToSend = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToSend);
if !xTaskWaitingToSend.is_null() {
xTaskNotifyIndexedFromISR(
xTaskWaitingToSend,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
pxHigherPriorityTaskWoken,
);
xReturn = pdTRUE;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
xReturn
}
unsafe fn prvInitialiseNewStreamBuffer(
pxStreamBuffer: *mut StreamBuffer_t,
pucBuffer: *mut u8,
xBufferSizeBytes: usize,
xTriggerLevelBytes: usize,
ucFlags: u8,
pxSendCompletedCallback: StreamBufferCallbackFunction_t,
pxReceiveCompletedCallback: StreamBufferCallbackFunction_t,
) {
ptr::write(
pxStreamBuffer,
StreamBuffer_t {
xTail: AtomicUsize::new(0),
xHead: AtomicUsize::new(0),
xLength: xBufferSizeBytes,
xTriggerLevelBytes: AtomicUsize::new(xTriggerLevelBytes),
xTaskWaitingToReceive: AtomicPtr::new(ptr::null_mut()),
xTaskWaitingToSend: AtomicPtr::new(ptr::null_mut()),
pucBuffer,
ucFlags,
#[cfg(feature = "trace-facility")]
uxStreamBufferNumber: 0,
pxSendCompletedCallback,
pxReceiveCompletedCallback,
uxNotificationIndex: AtomicUsize::new(tskDEFAULT_INDEX_TO_NOTIFY as usize),
},
);
}
#[inline(always)]
fn prvTakeWaitingTask(pxWaitingTask: &AtomicPtr<c_void>) -> TaskHandle_t {
let xTask = pxWaitingTask.load(Ordering::Acquire);
if !xTask.is_null() {
pxWaitingTask.store(ptr::null_mut(), Ordering::Release);
}
xTask
}
unsafe fn prvBytesInBuffer(pxStreamBuffer: *const StreamBuffer_t) -> usize {
let xHead = (*pxStreamBuffer).xHead.load(Ordering::Acquire);
let xTail = (*pxStreamBuffer).xTail.load(Ordering::Acquire);
let mut xCount = (*pxStreamBuffer).xLength + xHead;
xCount -= xTail;
if xCount >= (*pxStreamBuffer).xLength {
xCount -= (*pxStreamBuffer).xLength;
}
xCount
}
unsafe fn prvWriteBytesToBuffer(
pxStreamBuffer: *mut StreamBuffer_t,
pucData: *const u8,
xCount: usize,
mut xHead: usize,
) -> usize {
configASSERT(xCount > 0);
let xFirstLength = core::cmp::min((*pxStreamBuffer).xLength - xHead, xCount);
ptr::copy_nonoverlapping(
pucData,
(*pxStreamBuffer).pucBuffer.add(xHead),
xFirstLength,
);
if xCount > xFirstLength {
ptr::copy_nonoverlapping(
pucData.add(xFirstLength),
(*pxStreamBuffer).pucBuffer,
xCount - xFirstLength,
);
}
xHead += xCount;
if xHead >= (*pxStreamBuffer).xLength {
xHead -= (*pxStreamBuffer).xLength;
}
xHead
}
unsafe fn prvReadBytesFromBuffer(
pxStreamBuffer: *mut StreamBuffer_t,
pucData: *mut u8,
xCount: usize,
mut xTail: usize,
) -> usize {
configASSERT(xCount > 0);
let xFirstLength = core::cmp::min((*pxStreamBuffer).xLength - xTail, xCount);
ptr::copy_nonoverlapping(
(*pxStreamBuffer).pucBuffer.add(xTail),
pucData,
xFirstLength,
);
if xCount > xFirstLength {
ptr::copy_nonoverlapping(
(*pxStreamBuffer).pucBuffer,
pucData.add(xFirstLength),
xCount - xFirstLength,
);
}
xTail += xCount;
if xTail >= (*pxStreamBuffer).xLength {
xTail -= (*pxStreamBuffer).xLength;
}
xTail
}
unsafe fn prvWriteMessageToBuffer(
pxStreamBuffer: *mut StreamBuffer_t,
pvTxData: *const c_void,
mut xDataLengthBytes: usize,
xSpace: usize,
xRequiredSpace: usize,
) -> usize {
let mut xNextHead = (*pxStreamBuffer).xHead.load(Ordering::Relaxed);
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0 {
if xSpace >= xRequiredSpace {
let Ok(xMessageLength) = configMESSAGE_BUFFER_LENGTH_TYPE::try_from(xDataLengthBytes)
else {
return 0;
};
xNextHead = prvWriteBytesToBuffer(
pxStreamBuffer,
&xMessageLength as *const _ as *const u8,
sbBYTES_TO_STORE_MESSAGE_LENGTH,
xNextHead,
);
} else {
xDataLengthBytes = 0;
}
} else {
xDataLengthBytes = core::cmp::min(xDataLengthBytes, xSpace);
}
if xDataLengthBytes != 0 {
let xHead = prvWriteBytesToBuffer(
pxStreamBuffer,
pvTxData as *const u8,
xDataLengthBytes,
xNextHead,
);
(*pxStreamBuffer).xHead.store(xHead, Ordering::Release);
}
xDataLengthBytes
}
unsafe fn prvReadMessageFromBuffer(
pxStreamBuffer: *mut StreamBuffer_t,
pvRxData: *mut c_void,
xBufferLengthBytes: usize,
mut xBytesAvailable: usize,
) -> usize {
let mut xNextTail = (*pxStreamBuffer).xTail.load(Ordering::Relaxed);
let xNextMessageLength: usize;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER) != 0 {
let mut xTempMessageLength: configMESSAGE_BUFFER_LENGTH_TYPE = 0;
xNextTail = prvReadBytesFromBuffer(
pxStreamBuffer,
&mut xTempMessageLength as *mut _ as *mut u8,
sbBYTES_TO_STORE_MESSAGE_LENGTH,
xNextTail,
);
xNextMessageLength = xTempMessageLength as usize;
xBytesAvailable -= sbBYTES_TO_STORE_MESSAGE_LENGTH;
if xNextMessageLength > xBufferLengthBytes {
return 0;
}
} else {
xNextMessageLength = xBufferLengthBytes;
}
let xCount = core::cmp::min(xNextMessageLength, xBytesAvailable);
if xCount != 0 {
let xTail = prvReadBytesFromBuffer(pxStreamBuffer, pvRxData as *mut u8, xCount, xNextTail);
(*pxStreamBuffer).xTail.store(xTail, Ordering::Release);
}
xCount
}
#[inline(always)]
unsafe fn sbSEND_COMPLETED(pxStreamBuffer: *mut StreamBuffer_t) {
vTaskSuspendAll();
let xTaskWaitingToReceive = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToReceive);
if !xTaskWaitingToReceive.is_null() {
xTaskNotifyIndexed(
xTaskWaitingToReceive,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
);
}
xTaskResumeAll();
}
#[inline(always)]
unsafe fn prvSEND_COMPLETED(pxStreamBuffer: *mut StreamBuffer_t) {
if let Some(pxCallback) = (*pxStreamBuffer).pxSendCompletedCallback {
pxCallback(
pxStreamBuffer as StreamBufferHandle_t,
pdFALSE,
ptr::null_mut(),
);
} else {
sbSEND_COMPLETED(pxStreamBuffer);
}
}
#[inline(always)]
unsafe fn sbSEND_COMPLETE_FROM_ISR(
pxStreamBuffer: *mut StreamBuffer_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
let xTaskWaitingToReceive = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToReceive);
if !xTaskWaitingToReceive.is_null() {
xTaskNotifyIndexedFromISR(
xTaskWaitingToReceive,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
pxHigherPriorityTaskWoken,
);
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}
#[inline(always)]
unsafe fn prvSEND_COMPLETE_FROM_ISR(
pxStreamBuffer: *mut StreamBuffer_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
if let Some(pxCallback) = (*pxStreamBuffer).pxSendCompletedCallback {
pxCallback(
pxStreamBuffer as StreamBufferHandle_t,
pdTRUE,
pxHigherPriorityTaskWoken,
);
} else {
sbSEND_COMPLETE_FROM_ISR(pxStreamBuffer, pxHigherPriorityTaskWoken);
}
}
#[inline(always)]
unsafe fn sbRECEIVE_COMPLETED(pxStreamBuffer: *mut StreamBuffer_t) {
vTaskSuspendAll();
let xTaskWaitingToSend = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToSend);
if !xTaskWaitingToSend.is_null() {
xTaskNotifyIndexed(
xTaskWaitingToSend,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
);
}
xTaskResumeAll();
}
#[inline(always)]
unsafe fn prvRECEIVE_COMPLETED(pxStreamBuffer: *mut StreamBuffer_t) {
if let Some(pxCallback) = (*pxStreamBuffer).pxReceiveCompletedCallback {
pxCallback(
pxStreamBuffer as StreamBufferHandle_t,
pdFALSE,
ptr::null_mut(),
);
} else {
sbRECEIVE_COMPLETED(pxStreamBuffer);
}
}
#[inline(always)]
unsafe fn sbRECEIVE_COMPLETED_FROM_ISR(
pxStreamBuffer: *mut StreamBuffer_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
let xTaskWaitingToSend = prvTakeWaitingTask(&(*pxStreamBuffer).xTaskWaitingToSend);
if !xTaskWaitingToSend.is_null() {
xTaskNotifyIndexedFromISR(
xTaskWaitingToSend,
(*pxStreamBuffer)
.uxNotificationIndex
.load(Ordering::Relaxed) as UBaseType_t,
0,
eNoAction as i32,
pxHigherPriorityTaskWoken,
);
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}
#[inline(always)]
unsafe fn prvRECEIVE_COMPLETED_FROM_ISR(
pxStreamBuffer: *mut StreamBuffer_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
if let Some(pxCallback) = (*pxStreamBuffer).pxReceiveCompletedCallback {
pxCallback(
pxStreamBuffer as StreamBufferHandle_t,
pdTRUE,
pxHigherPriorityTaskWoken,
);
} else {
sbRECEIVE_COMPLETED_FROM_ISR(pxStreamBuffer, pxHigherPriorityTaskWoken);
}
}
const eNoAction: u32 = 0;
pub unsafe fn xStreamBufferGetStaticBuffers(
xStreamBuffer: StreamBufferHandle_t,
ppucStreamBufferStorageArea: *mut *mut u8,
ppxStaticStreamBuffer: *mut *mut StaticStreamBuffer_t,
) -> BaseType_t {
configASSERT(!xStreamBuffer.is_null());
configASSERT(!ppucStreamBufferStorageArea.is_null());
configASSERT(!ppxStaticStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
let xReturn: BaseType_t;
if ((*pxStreamBuffer).ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED) != 0 {
xReturn = pdTRUE;
*ppucStreamBufferStorageArea = (*pxStreamBuffer).pucBuffer;
*ppxStaticStreamBuffer = pxStreamBuffer as *mut StaticStreamBuffer_t;
} else {
xReturn = pdFALSE;
}
xReturn
}
#[inline(always)]
pub unsafe fn xMessageBufferGetStaticBuffers(
xMessageBuffer: MessageBufferHandle_t,
ppucMessageBufferStorageArea: *mut *mut u8,
ppxStaticMessageBuffer: *mut *mut StaticMessageBuffer_t,
) -> BaseType_t {
xStreamBufferGetStaticBuffers(
xMessageBuffer,
ppucMessageBufferStorageArea,
ppxStaticMessageBuffer,
)
}
#[inline(always)]
pub unsafe fn xMessageBufferSend(
xMessageBuffer: MessageBufferHandle_t,
pvTxData: *const c_void,
xDataLengthBytes: usize,
xTicksToWait: TickType_t,
) -> usize {
xStreamBufferSend(xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait)
}
#[inline(always)]
pub unsafe fn xMessageBufferSendFromISR(
xMessageBuffer: MessageBufferHandle_t,
pvTxData: *const c_void,
xDataLengthBytes: usize,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> usize {
xStreamBufferSendFromISR(
xMessageBuffer,
pvTxData,
xDataLengthBytes,
pxHigherPriorityTaskWoken,
)
}
#[inline(always)]
pub unsafe fn xMessageBufferReceive(
xMessageBuffer: MessageBufferHandle_t,
pvRxData: *mut c_void,
xBufferLengthBytes: usize,
xTicksToWait: TickType_t,
) -> usize {
xStreamBufferReceive(xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait)
}
#[inline(always)]
pub unsafe fn xMessageBufferReceiveFromISR(
xMessageBuffer: MessageBufferHandle_t,
pvRxData: *mut c_void,
xBufferLengthBytes: usize,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> usize {
xStreamBufferReceiveFromISR(
xMessageBuffer,
pvRxData,
xBufferLengthBytes,
pxHigherPriorityTaskWoken,
)
}
#[inline(always)]
pub unsafe fn vMessageBufferDelete(xMessageBuffer: MessageBufferHandle_t) {
vStreamBufferDelete(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferIsFull(xMessageBuffer: MessageBufferHandle_t) -> BaseType_t {
xStreamBufferIsFull(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferIsEmpty(xMessageBuffer: MessageBufferHandle_t) -> BaseType_t {
xStreamBufferIsEmpty(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferReset(xMessageBuffer: MessageBufferHandle_t) -> BaseType_t {
xStreamBufferReset(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferResetFromISR(xMessageBuffer: MessageBufferHandle_t) -> BaseType_t {
xStreamBufferResetFromISR(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferSpaceAvailable(xMessageBuffer: MessageBufferHandle_t) -> usize {
xStreamBufferSpacesAvailable(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferSpacesAvailable(xMessageBuffer: MessageBufferHandle_t) -> usize {
xStreamBufferSpacesAvailable(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferNextLengthBytes(xMessageBuffer: MessageBufferHandle_t) -> usize {
xStreamBufferNextMessageLengthBytes(xMessageBuffer)
}
#[inline(always)]
pub unsafe fn xMessageBufferSendCompletedFromISR(
xMessageBuffer: MessageBufferHandle_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
xStreamBufferSendCompletedFromISR(xMessageBuffer, pxHigherPriorityTaskWoken)
}
#[inline(always)]
pub unsafe fn xMessageBufferReceiveCompletedFromISR(
xMessageBuffer: MessageBufferHandle_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
xStreamBufferReceiveCompletedFromISR(xMessageBuffer, pxHigherPriorityTaskWoken)
}
#[cfg(feature = "trace-facility")]
pub unsafe fn uxStreamBufferGetStreamBufferNumber(
xStreamBuffer: StreamBufferHandle_t,
) -> UBaseType_t {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
(*pxStreamBuffer).uxStreamBufferNumber
}
#[cfg(feature = "trace-facility")]
pub unsafe fn vStreamBufferSetStreamBufferNumber(
xStreamBuffer: StreamBufferHandle_t,
uxStreamBufferNumber: UBaseType_t,
) {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *mut StreamBuffer_t;
(*pxStreamBuffer).uxStreamBufferNumber = uxStreamBufferNumber;
}
#[cfg(feature = "trace-facility")]
pub unsafe fn ucStreamBufferGetStreamBufferType(xStreamBuffer: StreamBufferHandle_t) -> u8 {
configASSERT(!xStreamBuffer.is_null());
let pxStreamBuffer = xStreamBuffer as *const StreamBuffer_t;
(*pxStreamBuffer).ucFlags & sbFLAGS_IS_MESSAGE_BUFFER
}
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::{AtomicUsize, Ordering};
static SEND_CALLBACKS: AtomicUsize = AtomicUsize::new(0);
static RECEIVE_CALLBACKS: AtomicUsize = AtomicUsize::new(0);
static ISR_CALLBACKS: AtomicUsize = AtomicUsize::new(0);
extern "C" fn send_callback(
_xStreamBuffer: StreamBufferHandle_t,
xIsInsideISR: BaseType_t,
_pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
SEND_CALLBACKS.fetch_add(1, Ordering::Relaxed);
if xIsInsideISR != pdFALSE {
ISR_CALLBACKS.fetch_add(1, Ordering::Relaxed);
}
}
extern "C" fn receive_callback(
_xStreamBuffer: StreamBufferHandle_t,
xIsInsideISR: BaseType_t,
_pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
RECEIVE_CALLBACKS.fetch_add(1, Ordering::Relaxed);
if xIsInsideISR != pdFALSE {
ISR_CALLBACKS.fetch_add(1, Ordering::Relaxed);
}
}
extern "C" fn no_op_callback(
_xStreamBuffer: StreamBufferHandle_t,
_xIsInsideISR: BaseType_t,
_pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
}
#[test]
fn static_stream_buffer_storage_matches_internal_layout() {
assert_eq!(
core::mem::size_of::<StaticStreamBuffer_t>(),
core::mem::size_of::<StreamBuffer_t>()
);
assert_eq!(
core::mem::align_of::<StaticStreamBuffer_t>(),
core::mem::align_of::<StreamBuffer_t>()
);
}
#[cfg(feature = "port-test")]
#[test]
fn callbacks_survive_reset_and_distinguish_isr_context() {
crate::port::test_port_reset();
SEND_CALLBACKS.store(0, Ordering::Relaxed);
RECEIVE_CALLBACKS.store(0, Ordering::Relaxed);
ISR_CALLBACKS.store(0, Ordering::Relaxed);
let mut storage = [0u8; 17];
let mut control = StaticStreamBuffer_t::new();
let handle = unsafe {
xStreamBufferGenericCreateStatic(
storage.len(),
1,
sbTYPE_STREAM_BUFFER,
storage.as_mut_ptr(),
&mut control,
Some(send_callback),
Some(receive_callback),
)
};
assert!(!handle.is_null());
#[cfg(feature = "trace-facility")]
unsafe {
vStreamBufferSetStreamBufferNumber(handle, 123);
}
let sent = [1u8, 2, 3];
let mut received = [0u8; 3];
assert_eq!(
unsafe { xStreamBufferSend(handle, sent.as_ptr().cast(), sent.len(), 0) },
sent.len()
);
assert_eq!(
unsafe {
xStreamBufferReceive(handle, received.as_mut_ptr().cast(), received.len(), 0)
},
received.len()
);
assert_eq!(received, sent);
assert_eq!(unsafe { xStreamBufferReset(handle) }, pdPASS);
#[cfg(feature = "trace-facility")]
assert_eq!(unsafe { uxStreamBufferGetStreamBufferNumber(handle) }, 123);
let mut higher_priority_task_woken = pdFALSE;
assert_eq!(
unsafe {
xStreamBufferSendFromISR(
handle,
sent.as_ptr().cast(),
sent.len(),
&mut higher_priority_task_woken,
)
},
sent.len()
);
assert_eq!(
unsafe {
xStreamBufferReceiveFromISR(
handle,
received.as_mut_ptr().cast(),
received.len(),
&mut higher_priority_task_woken,
)
},
received.len()
);
assert_eq!(unsafe { xStreamBufferResetFromISR(handle) }, pdPASS);
assert_eq!(
unsafe { xStreamBufferSend(handle, sent.as_ptr().cast(), sent.len(), 0) },
sent.len()
);
assert_eq!(
unsafe {
xStreamBufferReceive(handle, received.as_mut_ptr().cast(), received.len(), 0)
},
received.len()
);
#[cfg(feature = "trace-facility")]
assert_eq!(unsafe { uxStreamBufferGetStreamBufferNumber(handle) }, 123);
assert_eq!(SEND_CALLBACKS.load(Ordering::Relaxed), 3);
assert_eq!(RECEIVE_CALLBACKS.load(Ordering::Relaxed), 3);
assert_eq!(ISR_CALLBACKS.load(Ordering::Relaxed), 2);
}
#[cfg(feature = "port-test")]
#[test]
fn header_macro_veneers_preserve_buffer_types_and_message_boundaries() {
crate::port::test_port_reset();
let mut stream_storage = [0u8; 8];
let mut stream_control = StaticStreamBuffer_t::new();
let stream = unsafe {
xStreamBufferCreateStatic(
stream_storage.len(),
2,
stream_storage.as_mut_ptr(),
&mut stream_control,
)
};
assert!(!stream.is_null());
assert_eq!(
unsafe { (*(stream as *mut StreamBuffer_t)).ucFlags },
sbFLAGS_IS_STATICALLY_ALLOCATED,
);
let mut batching_storage = [0u8; 8];
let mut batching_control = StaticStreamBuffer_t::new();
let batching = unsafe {
xStreamBatchingBufferCreateStaticWithCallback(
batching_storage.len(),
2,
batching_storage.as_mut_ptr(),
&mut batching_control,
Some(no_op_callback),
Some(no_op_callback),
)
};
assert!(!batching.is_null());
assert_eq!(
unsafe { (*(batching as *mut StreamBuffer_t)).ucFlags },
sbFLAGS_IS_STATICALLY_ALLOCATED | sbFLAGS_IS_BATCHING_BUFFER,
);
let mut message_storage = [0u8; 24];
let mut message_control = StaticMessageBuffer_t::new();
let message_storage_ptr = message_storage.as_mut_ptr();
let message_control_ptr = core::ptr::addr_of_mut!(message_control);
let message = unsafe {
xMessageBufferCreateStaticWithCallback(
message_storage.len(),
message_storage_ptr,
message_control_ptr,
Some(no_op_callback),
Some(no_op_callback),
)
};
assert!(!message.is_null());
assert_eq!(
unsafe { (*(message as *mut StreamBuffer_t)).ucFlags },
sbFLAGS_IS_STATICALLY_ALLOCATED | sbFLAGS_IS_MESSAGE_BUFFER,
);
let payload = [1u8, 2, 3];
assert_eq!(
unsafe { xMessageBufferSend(message, payload.as_ptr().cast(), payload.len(), 0) },
payload.len(),
);
assert_eq!(
unsafe { xMessageBufferNextLengthBytes(message) },
payload.len(),
);
assert_eq!(unsafe { xMessageBufferIsEmpty(message) }, pdFALSE);
assert_eq!(unsafe { xMessageBufferSpaceAvailable(message) }, unsafe {
xMessageBufferSpacesAvailable(message)
},);
let mut received = [0u8; 3];
assert_eq!(
unsafe {
xMessageBufferReceive(message, received.as_mut_ptr().cast(), received.len(), 0)
},
received.len(),
);
assert_eq!(received, payload);
let mut returned_storage = ptr::null_mut();
let mut returned_control = ptr::null_mut();
assert_eq!(
unsafe {
xMessageBufferGetStaticBuffers(
message,
&mut returned_storage,
&mut returned_control,
)
},
pdTRUE,
);
assert_eq!(returned_storage, message_storage_ptr);
assert!(core::ptr::eq(returned_control, message_control_ptr));
crate::port::test_port_set_inside_interrupt(true);
let mut higher_priority_task_woken = pdFALSE;
assert_eq!(
unsafe {
xMessageBufferSendFromISR(
message,
payload.as_ptr().cast(),
payload.len(),
&mut higher_priority_task_woken,
)
},
payload.len(),
);
assert_eq!(
unsafe {
xMessageBufferReceiveFromISR(
message,
received.as_mut_ptr().cast(),
received.len(),
&mut higher_priority_task_woken,
)
},
received.len(),
);
assert_eq!(
unsafe { xMessageBufferSendCompletedFromISR(message, &mut higher_priority_task_woken) },
pdFALSE,
);
assert_eq!(
unsafe {
xMessageBufferReceiveCompletedFromISR(message, &mut higher_priority_task_woken)
},
pdFALSE,
);
assert_eq!(unsafe { xMessageBufferResetFromISR(message) }, pdPASS);
crate::port::test_port_set_inside_interrupt(false);
unsafe {
assert_eq!(xMessageBufferReset(message), pdPASS);
assert_eq!(xMessageBufferIsFull(message), pdFALSE);
vMessageBufferDelete(message);
}
}
#[cfg(feature = "port-test")]
#[test]
#[should_panic(expected = "FreeRTOS assertion failed")]
fn next_message_length_rejects_a_partial_published_prefix() {
let mut storage = [0u8; 16];
let mut control = StaticStreamBuffer_t::new();
let handle = unsafe {
xStreamBufferGenericCreateStatic(
storage.len(),
1,
sbTYPE_MESSAGE_BUFFER,
storage.as_mut_ptr(),
&mut control,
None,
None,
)
};
assert!(!handle.is_null());
let stream = handle as *mut StreamBuffer_t;
unsafe {
(*stream)
.xHead
.store(sbBYTES_TO_STORE_MESSAGE_LENGTH, Ordering::Release);
let _ = xStreamBufferNextMessageLengthBytes(handle);
}
}
#[cfg(all(
feature = "port-test",
not(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))
))]
#[test]
#[should_panic(expected = "FreeRTOS assertion failed")]
fn static_only_delete_rejects_a_corrupt_dynamic_allocation_flag() {
let mut storage = [0u8; 8];
let mut control = StaticStreamBuffer_t::new();
let handle = unsafe {
xStreamBufferGenericCreateStatic(
storage.len(),
1,
sbTYPE_STREAM_BUFFER,
storage.as_mut_ptr(),
&mut control,
None,
None,
)
};
assert!(!handle.is_null());
unsafe {
(*(handle as *mut StreamBuffer_t)).ucFlags &= !sbFLAGS_IS_STATICALLY_ALLOCATED;
vStreamBufferDelete(handle);
}
}
#[cfg(all(feature = "port-test", feature = "heap-4"))]
#[test]
fn dynamic_buffer_uses_and_returns_freertos_heap_storage() {
use crate::memory::{pvPortMalloc, vPortFree, vPortHeapResetState, xPortGetFreeHeapSize};
unsafe { vPortHeapResetState() };
unsafe {
let probe = pvPortMalloc(1);
assert!(!probe.is_null());
vPortFree(probe);
let initial_free = xPortGetFreeHeapSize();
let handle = xStreamBufferCreate(127, 1);
assert!(!handle.is_null());
assert!(xPortGetFreeHeapSize() < initial_free);
assert_eq!(handle as usize % crate::port::portBYTE_ALIGNMENT, 0,);
vStreamBufferDelete(handle);
assert_eq!(xPortGetFreeHeapSize(), initial_free);
}
unsafe { vPortHeapResetState() };
}
#[cfg(feature = "port-test")]
#[test]
fn atomic_spsc_handoff_preserves_bytes_under_host_threads() {
#[cfg(miri)]
const TRANSFERS: usize = 500;
#[cfg(not(miri))]
const TRANSFERS: usize = 50_000;
struct SendHandle(StreamBufferHandle_t);
impl SendHandle {
fn into_raw(self) -> StreamBufferHandle_t {
self.0
}
}
unsafe impl Send for SendHandle {}
let storage = std::boxed::Box::leak(std::boxed::Box::new([0u8; 257]));
let control = std::boxed::Box::leak(std::boxed::Box::new(StaticStreamBuffer_t::new()));
let handle = unsafe {
xStreamBufferGenericCreateStatic(
storage.len(),
1,
sbTYPE_STREAM_BUFFER,
storage.as_mut_ptr(),
control,
Some(no_op_callback),
Some(no_op_callback),
)
};
assert!(!handle.is_null());
let sender_handle = SendHandle(handle);
let sender = std::thread::spawn(move || {
let handle = sender_handle.into_raw();
let mut index = 0usize;
while index < TRANSFERS {
let byte = (index % 251) as u8;
if unsafe { xStreamBufferSend(handle, (&byte as *const u8).cast(), 1, 0) } == 1 {
index += 1;
} else {
std::thread::yield_now();
}
}
});
let receiver_handle = SendHandle(handle);
let receiver = std::thread::spawn(move || {
let handle = receiver_handle.into_raw();
let mut index = 0usize;
while index < TRANSFERS {
let mut byte = 0u8;
if unsafe { xStreamBufferReceive(handle, (&mut byte as *mut u8).cast(), 1, 0) } == 1
{
assert_eq!(byte, (index % 251) as u8);
index += 1;
} else {
std::thread::yield_now();
}
}
});
sender.join().unwrap();
receiver.join().unwrap();
assert_eq!(unsafe { xStreamBufferBytesAvailable(handle) }, 0);
}
}