Fix XPLAINBridge project discarding characters from the USB interface due to a double read from the endpoint.

Make XPLAINBridge and USBtoSerial projects more reliable by forcing a flush if the UART-to-USB buffer becomes nearly full.

Reduce locking in the LightweightRingBuffer.h header files by only locking on the update of the buffer count, and require insertions and removals from each buffer to occur in only one execution thread.

Fix CDC_*_ReceiveByte() returning 0 when the interface is not configured, instead of the new -1 error value.

Fix CDC_Host_ReceiveByte() not re-freezing the pipe if no packet has been received.

Remove redundant Pipe token set commands in the CDC and RNDIS host class drivers.
pull/1469/head
Dean Camera 14 years ago
parent fb0e6597b6
commit ff09cf9c73

@ -133,7 +133,7 @@ void CDC_Device_USBTask(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo)
} }
uint8_t CDC_Device_SendString(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo, uint8_t CDC_Device_SendString(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo,
char* const Data, const char* const Data,
const uint16_t Length) const uint16_t Length)
{ {
if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS)) if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS))
@ -219,20 +219,21 @@ uint16_t CDC_Device_BytesReceived(USB_ClassInfo_CDC_Device_t* const CDCInterface
int16_t CDC_Device_ReceiveByte(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo) int16_t CDC_Device_ReceiveByte(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo)
{ {
uint8_t ReceivedByte = -1;
if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS)) if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS))
return 0; return -1;
int16_t ReceivedByte = -1;
Endpoint_SelectEndpoint(CDCInterfaceInfo->Config.DataOUTEndpointNumber); Endpoint_SelectEndpoint(CDCInterfaceInfo->Config.DataOUTEndpointNumber);
if (!(Endpoint_IsOUTReceived())) if (Endpoint_IsOUTReceived())
return -1; {
else if (Endpoint_BytesInEndpoint()) if (Endpoint_BytesInEndpoint())
ReceivedByte = Endpoint_Read_Byte(); ReceivedByte = Endpoint_Read_Byte();
if (!(Endpoint_BytesInEndpoint())) if (!(Endpoint_BytesInEndpoint()))
Endpoint_ClearOUT(); Endpoint_ClearOUT();
}
return ReceivedByte; return ReceivedByte;
} }

@ -214,7 +214,7 @@
* \return A value from the \ref Endpoint_Stream_RW_ErrorCodes_t enum. * \return A value from the \ref Endpoint_Stream_RW_ErrorCodes_t enum.
*/ */
uint8_t CDC_Device_SendString(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo, uint8_t CDC_Device_SendString(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo,
char* const Data, const char* const Data,
const uint16_t Length) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2); const uint16_t Length) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2);
/** Sends a given byte to the attached USB host, if connected. If a host is not connected when the function is called, the /** Sends a given byte to the attached USB host, if connected. If a host is not connected when the function is called, the

@ -205,7 +205,6 @@ void CDC_Host_USBTask(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo)
return; return;
Pipe_SelectPipe(CDCInterfaceInfo->Config.NotificationPipeNumber); Pipe_SelectPipe(CDCInterfaceInfo->Config.NotificationPipeNumber);
Pipe_SetPipeToken(PIPE_TOKEN_IN);
Pipe_Unfreeze(); Pipe_Unfreeze();
if (Pipe_IsINReceived()) if (Pipe_IsINReceived())
@ -285,7 +284,7 @@ uint8_t CDC_Host_SendBreak(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo,
} }
uint8_t CDC_Host_SendString(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo, uint8_t CDC_Host_SendString(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo,
char* const Data, const char* const Data,
const uint16_t Length) const uint16_t Length)
{ {
if ((USB_HostState != HOST_STATE_Configured) || !(CDCInterfaceInfo->State.IsActive)) if ((USB_HostState != HOST_STATE_Configured) || !(CDCInterfaceInfo->State.IsActive))
@ -333,7 +332,6 @@ uint16_t CDC_Host_BytesReceived(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo
return 0; return 0;
Pipe_SelectPipe(CDCInterfaceInfo->Config.DataINPipeNumber); Pipe_SelectPipe(CDCInterfaceInfo->Config.DataINPipeNumber);
Pipe_SetPipeToken(PIPE_TOKEN_IN);
Pipe_Unfreeze(); Pipe_Unfreeze();
if (Pipe_IsINReceived()) if (Pipe_IsINReceived())
@ -360,22 +358,22 @@ uint16_t CDC_Host_BytesReceived(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo
int16_t CDC_Host_ReceiveByte(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo) int16_t CDC_Host_ReceiveByte(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo)
{ {
uint8_t ReceivedByte = -1;
if ((USB_HostState != HOST_STATE_Configured) || !(CDCInterfaceInfo->State.IsActive)) if ((USB_HostState != HOST_STATE_Configured) || !(CDCInterfaceInfo->State.IsActive))
return 0; return -1;
int16_t ReceivedByte = -1;
Pipe_SelectPipe(CDCInterfaceInfo->Config.DataINPipeNumber); Pipe_SelectPipe(CDCInterfaceInfo->Config.DataINPipeNumber);
Pipe_SetPipeToken(PIPE_TOKEN_IN);
Pipe_Unfreeze(); Pipe_Unfreeze();
if (!(Pipe_IsINReceived())) if (Pipe_IsINReceived())
return -1; {
else if (Pipe_BytesInPipe()) if (Pipe_BytesInPipe())
ReceivedByte = Pipe_Read_Byte(); ReceivedByte = Pipe_Read_Byte();
if (!(Pipe_BytesInPipe())) if (!(Pipe_BytesInPipe()))
Pipe_ClearIN(); Pipe_ClearIN();
}
Pipe_Freeze(); Pipe_Freeze();

@ -217,7 +217,7 @@
* \return A value from the \ref Pipe_Stream_RW_ErrorCodes_t enum. * \return A value from the \ref Pipe_Stream_RW_ErrorCodes_t enum.
*/ */
uint8_t CDC_Host_SendString(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo, uint8_t CDC_Host_SendString(USB_ClassInfo_CDC_Host_t* const CDCInterfaceInfo,
char* const Data, const char* const Data,
const uint16_t Length) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2); const uint16_t Length) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2);
/** Sends a given byte to the attached USB device, if connected. If a device is not connected when the function is called, the /** Sends a given byte to the attached USB device, if connected. If a device is not connected when the function is called, the

@ -386,12 +386,9 @@ bool RNDIS_Host_IsPacketReceived(USB_ClassInfo_RNDIS_Host_t* const RNDISInterfac
return false; return false;
Pipe_SelectPipe(RNDISInterfaceInfo->Config.DataINPipeNumber); Pipe_SelectPipe(RNDISInterfaceInfo->Config.DataINPipeNumber);
Pipe_SetPipeToken(PIPE_TOKEN_IN);
Pipe_Unfreeze(); Pipe_Unfreeze();
PacketWaiting = Pipe_IsINReceived(); PacketWaiting = Pipe_IsINReceived();
Pipe_Freeze(); Pipe_Freeze();
return PacketWaiting; return PacketWaiting;
@ -407,7 +404,6 @@ uint8_t RNDIS_Host_ReadPacket(USB_ClassInfo_RNDIS_Host_t* const RNDISInterfaceIn
return PIPE_READYWAIT_DeviceDisconnected; return PIPE_READYWAIT_DeviceDisconnected;
Pipe_SelectPipe(RNDISInterfaceInfo->Config.DataINPipeNumber); Pipe_SelectPipe(RNDISInterfaceInfo->Config.DataINPipeNumber);
Pipe_SetPipeToken(PIPE_TOKEN_IN);
Pipe_Unfreeze(); Pipe_Unfreeze();
if (!(Pipe_IsReadWriteAllowed())) if (!(Pipe_IsReadWriteAllowed()))

@ -61,6 +61,7 @@
* - SD Card reader: http://elasticsheep.com/2010/04/teensy2-usb-mass-storage-with-an-sd-card/ * - SD Card reader: http://elasticsheep.com/2010/04/teensy2-usb-mass-storage-with-an-sd-card/
* - SEGA Megadrive/Genesis Development Cartridge: http://www.makestuff.eu/wordpress/?page_id=398 * - SEGA Megadrive/Genesis Development Cartridge: http://www.makestuff.eu/wordpress/?page_id=398
* - Stripe Snoop, a Magnetic Card reader: http://www.ossguy.com/ss_usb/ * - Stripe Snoop, a Magnetic Card reader: http://www.ossguy.com/ss_usb/
* - Touchscreen Input Device: http://capnstech.blogspot.com/2010/07/touchscreen-update.html
* - USB Interface for Playstation Portable Devices: http://forums.ps2dev.org/viewtopic.php?t=11001 * - USB Interface for Playstation Portable Devices: http://forums.ps2dev.org/viewtopic.php?t=11001
* - Userial, a USB to Serial converter with SPI, I2C and other protocols: http://www.tty1.net/userial/ * - Userial, a USB to Serial converter with SPI, I2C and other protocols: http://www.tty1.net/userial/
* - XUM1541, a Commodore 64 floppy drive to USB adapter: http://www.root.org/~nate/c64/xum1541/ * - XUM1541, a Commodore 64 floppy drive to USB adapter: http://www.root.org/~nate/c64/xum1541/

@ -127,13 +127,14 @@ int main(void)
LEDs_TurnOffLEDs(LEDMASK_RX); LEDs_TurnOffLEDs(LEDMASK_RX);
/* Check if the receive buffer flush period has expired */ /* Check if the receive buffer flush period has expired */
if (!(--FlushPeriodRemaining) || (Tx_Buffer.Count > 200)) RingBuff_Count_t BufferCount = RingBuffer_GetCount(&Tx_Buffer);
if (!(--FlushPeriodRemaining) || (BufferCount > 200))
{ {
/* Echo bytes from the target to the host via the virtual serial port */ /* Echo bytes from the target to the host via the virtual serial port */
if (Tx_Buffer.Count) if (BufferCount)
{ {
while (Tx_Buffer.Count) while (BufferCount--)
CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_AtomicRemove(&Tx_Buffer)); CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_Remove(&Tx_Buffer));
LEDs_TurnOnLEDs(LEDMASK_RX); LEDs_TurnOnLEDs(LEDMASK_RX);
PulseMSRemaining.RxLEDPulse = TX_RX_LED_PULSE_MS; PulseMSRemaining.RxLEDPulse = TX_RX_LED_PULSE_MS;

@ -49,6 +49,15 @@
/** Type of data to store into the buffer. */ /** Type of data to store into the buffer. */
#define RingBuff_Data_t uint8_t #define RingBuff_Data_t uint8_t
/** Datatype which may be used to store the count of data stored in a buffer, retrieved
* via a call to \ref RingBuffer_GetCount().
*/
#if (BUFFER_SIZE <= 0xFF)
#define RingBuff_Count_t uint8_t
#else
#define RingBuff_Count_t uint16_t
#endif
/* Type Defines: */ /* Type Defines: */
/** Type define for a new ring buffer object. Buffers should be initialized via a call to /** Type define for a new ring buffer object. Buffers should be initialized via a call to
* \ref RingBuffer_InitBuffer() before use. * \ref RingBuffer_InitBuffer() before use.
@ -58,86 +67,85 @@
RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */ RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */
RingBuff_Data_t* In; /**< Current storage location in the circular buffer */ RingBuff_Data_t* In; /**< Current storage location in the circular buffer */
RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */ RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */
uint8_t Count; /**< Total number of bytes stored in the circular buffer */ RingBuff_Count_t Count;
} RingBuff_t; } RingBuff_t;
/* Inline Functions: */ /* Inline Functions: */
/** Initialises a ring buffer ready for use. Buffers must be initialized via this function /** Initializes a ring buffer ready for use. Buffers must be initialized via this function
* before any operations are called upon them. Already initialized buffers may be reset * before any operations are called upon them. Already initialized buffers may be reset
* by re-initializing them using this function. * by re-initializing them using this function.
* *
* \param[out] Buffer Pointer to a ring buffer structure to initialize * \param[out] Buffer Pointer to a ring buffer structure to initialize
*/ */
static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer) static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
Buffer->Count = 0; }
} }
/** Atomically determines if the specified ring buffer contains any free space. This should /** Retrieves the minimum number of bytes stored in a particular buffer. This value is computed
* be tested before storing data to the buffer, to ensure that no data is lost due to a * by entering an atomic lock on the buffer while the IN and OUT locations are fetched, so that
* buffer overrun. * the buffer cannot be modified while the computation takes place. This value should be cached
* when reading out the contents of the buffer, so that as small a time as possible is spent
* in an atomic lock.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \note The value returned by this function is guaranteed to only be the minimum number of bytes
* stored in the given buffer; this value may change as other threads write new data and so
* the returned number should be used only to determine how many successive reads may safely
* be performed on the buffer.
* *
* \return Boolean true if the buffer contains no free space, false otherwise * \param[in] Buffer Pointer to a ring buffer structure whose count is to be computed
*/ */
static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer) static inline RingBuff_Count_t RingBuffer_GetCount(RingBuff_t* const Buffer)
{ {
bool IsFull; RingBuff_Count_t Count;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
IsFull = (Buffer->Count == BUFFER_SIZE); Count = Buffer->Count;
} }
return IsFull; return Count;
} }
/** Atomically inserts an element into the ring buffer. /** Atomically determines if the specified ring buffer contains any free space. This should
* be tested before storing data to the buffer, to ensure that no data is lost due to a
* buffer overrun.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer *
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline void RingBuffer_AtomicInsert(RingBuff_t* const Buffer, static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer)
const RingBuff_Data_t Data)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
*Buffer->In = Data; return (RingBuffer_GetCount(Buffer) == BUFFER_SIZE);
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer;
Buffer->Count++;
}
} }
/** Atomically retrieves an element from the ring buffer. /** Atomically determines if the specified ring buffer contains any data. This should
* be tested before removing data from the buffer, to ensure that the buffer does not
* underflow.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * If the data is to be removed in a loop, store the total number of bytes stored in the
* buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable
* to reduce the time spent in atomicity locks.
* *
* \return Next data element stored in the buffer * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
*
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline RingBuff_Data_t RingBuffer_AtomicRemove(RingBuff_t* const Buffer) static inline bool RingBuffer_IsEmpty(RingBuff_t* const Buffer)
{ {
RingBuff_Data_t Data; return (RingBuffer_GetCount(Buffer) == 0);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Data = *Buffer->Out;
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer;
Buffer->Count--;
}
return Data;
} }
/** Inserts an element into the ring buffer. /** Inserts an element into the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may insert into a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer * \param[in] Data Data element to insert into the buffer
@ -150,10 +158,17 @@
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count++; Buffer->Count++;
} }
}
/** Retrieves an element from the ring buffer. /** Removes an element from the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may remove from a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from
* *
@ -166,10 +181,12 @@
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count--; Buffer->Count--;
}
return Data; return Data;
} }
#endif #endif

@ -49,6 +49,15 @@
/** Type of data to store into the buffer. */ /** Type of data to store into the buffer. */
#define RingBuff_Data_t uint8_t #define RingBuff_Data_t uint8_t
/** Datatype which may be used to store the count of data stored in a buffer, retrieved
* via a call to \ref RingBuffer_GetCount().
*/
#if (BUFFER_SIZE <= 0xFF)
#define RingBuff_Count_t uint8_t
#else
#define RingBuff_Count_t uint16_t
#endif
/* Type Defines: */ /* Type Defines: */
/** Type define for a new ring buffer object. Buffers should be initialized via a call to /** Type define for a new ring buffer object. Buffers should be initialized via a call to
* \ref RingBuffer_InitBuffer() before use. * \ref RingBuffer_InitBuffer() before use.
@ -58,86 +67,85 @@
RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */ RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */
RingBuff_Data_t* In; /**< Current storage location in the circular buffer */ RingBuff_Data_t* In; /**< Current storage location in the circular buffer */
RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */ RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */
uint8_t Count; /**< Total number of bytes stored in the circular buffer */ RingBuff_Count_t Count;
} RingBuff_t; } RingBuff_t;
/* Inline Functions: */ /* Inline Functions: */
/** Initialises a ring buffer ready for use. Buffers must be initialized via this function /** Initializes a ring buffer ready for use. Buffers must be initialized via this function
* before any operations are called upon them. Already initialized buffers may be reset * before any operations are called upon them. Already initialized buffers may be reset
* by re-initializing them using this function. * by re-initializing them using this function.
* *
* \param[out] Buffer Pointer to a ring buffer structure to initialize * \param[out] Buffer Pointer to a ring buffer structure to initialize
*/ */
static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer) static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
Buffer->Count = 0; }
} }
/** Atomically determines if the specified ring buffer contains any free space. This should /** Retrieves the minimum number of bytes stored in a particular buffer. This value is computed
* be tested before storing data to the buffer, to ensure that no data is lost due to a * by entering an atomic lock on the buffer while the IN and OUT locations are fetched, so that
* buffer overrun. * the buffer cannot be modified while the computation takes place. This value should be cached
* when reading out the contents of the buffer, so that as small a time as possible is spent
* in an atomic lock.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \note The value returned by this function is guaranteed to only be the minimum number of bytes
* stored in the given buffer; this value may change as other threads write new data and so
* the returned number should be used only to determine how many successive reads may safely
* be performed on the buffer.
* *
* \return Boolean true if the buffer contains no free space, false otherwise * \param[in] Buffer Pointer to a ring buffer structure whose count is to be computed
*/ */
static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer) static inline RingBuff_Count_t RingBuffer_GetCount(RingBuff_t* const Buffer)
{ {
bool IsFull; RingBuff_Count_t Count;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
IsFull = (Buffer->Count == BUFFER_SIZE); Count = Buffer->Count;
} }
return IsFull; return Count;
} }
/** Atomically inserts an element into the ring buffer. /** Atomically determines if the specified ring buffer contains any free space. This should
* be tested before storing data to the buffer, to ensure that no data is lost due to a
* buffer overrun.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer *
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline void RingBuffer_AtomicInsert(RingBuff_t* const Buffer, static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer)
const RingBuff_Data_t Data)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
*Buffer->In = Data; return (RingBuffer_GetCount(Buffer) == BUFFER_SIZE);
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer;
Buffer->Count++;
}
} }
/** Atomically retrieves an element from the ring buffer. /** Atomically determines if the specified ring buffer contains any data. This should
* be tested before removing data from the buffer, to ensure that the buffer does not
* underflow.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * If the data is to be removed in a loop, store the total number of bytes stored in the
* buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable
* to reduce the time spent in atomicity locks.
* *
* \return Next data element stored in the buffer * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
*
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline RingBuff_Data_t RingBuffer_AtomicRemove(RingBuff_t* const Buffer) static inline bool RingBuffer_IsEmpty(RingBuff_t* const Buffer)
{ {
RingBuff_Data_t Data; return (RingBuffer_GetCount(Buffer) == 0);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Data = *Buffer->Out;
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer;
Buffer->Count--;
}
return Data;
} }
/** Inserts an element into the ring buffer. /** Inserts an element into the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may insert into a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer * \param[in] Data Data element to insert into the buffer
@ -150,10 +158,17 @@
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count++; Buffer->Count++;
} }
}
/** Retrieves an element from the ring buffer. /** Removes an element from the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may remove from a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from
* *
@ -166,10 +181,12 @@
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count--; Buffer->Count--;
}
return Data; return Data;
} }
#endif #endif

@ -84,21 +84,22 @@ int main(void)
/* Read bytes from the USB OUT endpoint into the USART transmit buffer */ /* Read bytes from the USB OUT endpoint into the USART transmit buffer */
int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface); int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
if (!(ReceivedByte < 0) && !(RingBuffer_IsFull(&USBtoUSART_Buffer))) if (!(ReceivedByte < 0) && !(RingBuffer_IsFull(&USBtoUSART_Buffer)))
RingBuffer_AtomicInsert(&USBtoUSART_Buffer, (uint8_t)ReceivedByte); RingBuffer_Insert(&USBtoUSART_Buffer, ReceivedByte);
/* Check if the UART receive buffer flush timer has expired */ /* Check if the UART receive buffer flush timer has expired or the buffer is nearly full */
if (TIFR0 & (1 << TOV0)) RingBuff_Count_t BufferCount = RingBuffer_GetCount(&USARTtoUSB_Buffer);
if ((TIFR0 & (1 << TOV0)) || (BufferCount > 200))
{ {
TIFR0 |= (1 << TOV0); TIFR0 |= (1 << TOV0);
/* Read bytes from the USART receive buffer into the USB IN endpoint */ /* Read bytes from the USART receive buffer into the USB IN endpoint */
while (USARTtoUSB_Buffer.Count) while (BufferCount--)
CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_AtomicRemove(&USARTtoUSB_Buffer)); CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_Remove(&USARTtoUSB_Buffer));
} }
/* Load the next byte from the USART transmit buffer into the USART */ /* Load the next byte from the USART transmit buffer into the USART */
if (USBtoUSART_Buffer.Count) if (!(RingBuffer_IsEmpty(&USBtoUSART_Buffer)))
Serial_TxByte(RingBuffer_AtomicRemove(&USBtoUSART_Buffer)); Serial_TxByte(RingBuffer_Remove(&USBtoUSART_Buffer));
CDC_Device_USBTask(&VirtualSerial_CDC_Interface); CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
USB_USBTask(); USB_USBTask();

@ -49,6 +49,15 @@
/** Type of data to store into the buffer. */ /** Type of data to store into the buffer. */
#define RingBuff_Data_t uint8_t #define RingBuff_Data_t uint8_t
/** Datatype which may be used to store the count of data stored in a buffer, retrieved
* via a call to \ref RingBuffer_GetCount().
*/
#if (BUFFER_SIZE <= 0xFF)
#define RingBuff_Count_t uint8_t
#else
#define RingBuff_Count_t uint16_t
#endif
/* Type Defines: */ /* Type Defines: */
/** Type define for a new ring buffer object. Buffers should be initialized via a call to /** Type define for a new ring buffer object. Buffers should be initialized via a call to
* \ref RingBuffer_InitBuffer() before use. * \ref RingBuffer_InitBuffer() before use.
@ -58,86 +67,85 @@
RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */ RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */
RingBuff_Data_t* In; /**< Current storage location in the circular buffer */ RingBuff_Data_t* In; /**< Current storage location in the circular buffer */
RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */ RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */
uint8_t Count; /**< Total number of bytes stored in the circular buffer */ RingBuff_Count_t Count;
} RingBuff_t; } RingBuff_t;
/* Inline Functions: */ /* Inline Functions: */
/** Initialises a ring buffer ready for use. Buffers must be initialized via this function /** Initializes a ring buffer ready for use. Buffers must be initialized via this function
* before any operations are called upon them. Already initialized buffers may be reset * before any operations are called upon them. Already initialized buffers may be reset
* by re-initializing them using this function. * by re-initializing them using this function.
* *
* \param[out] Buffer Pointer to a ring buffer structure to initialize * \param[out] Buffer Pointer to a ring buffer structure to initialize
*/ */
static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer) static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
Buffer->Count = 0; }
} }
/** Atomically determines if the specified ring buffer contains any free space. This should /** Retrieves the minimum number of bytes stored in a particular buffer. This value is computed
* be tested before storing data to the buffer, to ensure that no data is lost due to a * by entering an atomic lock on the buffer while the IN and OUT locations are fetched, so that
* buffer overrun. * the buffer cannot be modified while the computation takes place. This value should be cached
* when reading out the contents of the buffer, so that as small a time as possible is spent
* in an atomic lock.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \note The value returned by this function is guaranteed to only be the minimum number of bytes
* stored in the given buffer; this value may change as other threads write new data and so
* the returned number should be used only to determine how many successive reads may safely
* be performed on the buffer.
* *
* \return Boolean true if the buffer contains no free space, false otherwise * \param[in] Buffer Pointer to a ring buffer structure whose count is to be computed
*/ */
static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer) static inline RingBuff_Count_t RingBuffer_GetCount(RingBuff_t* const Buffer)
{ {
bool IsFull; RingBuff_Count_t Count;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
IsFull = (Buffer->Count == BUFFER_SIZE); Count = Buffer->Count;
} }
return IsFull; return Count;
} }
/** Atomically inserts an element into the ring buffer. /** Atomically determines if the specified ring buffer contains any free space. This should
* be tested before storing data to the buffer, to ensure that no data is lost due to a
* buffer overrun.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer *
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline void RingBuffer_AtomicInsert(RingBuff_t* const Buffer, static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer)
const RingBuff_Data_t Data)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{ {
*Buffer->In = Data; return (RingBuffer_GetCount(Buffer) == BUFFER_SIZE);
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer;
Buffer->Count++;
}
} }
/** Atomically retrieves an element from the ring buffer. /** Atomically determines if the specified ring buffer contains any data. This should
* be tested before removing data from the buffer, to ensure that the buffer does not
* underflow.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * If the data is to be removed in a loop, store the total number of bytes stored in the
* buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable
* to reduce the time spent in atomicity locks.
* *
* \return Next data element stored in the buffer * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
*
* \return Boolean true if the buffer contains no free space, false otherwise
*/ */
static inline RingBuff_Data_t RingBuffer_AtomicRemove(RingBuff_t* const Buffer) static inline bool RingBuffer_IsEmpty(RingBuff_t* const Buffer)
{ {
RingBuff_Data_t Data; return (RingBuffer_GetCount(Buffer) == 0);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Data = *Buffer->Out;
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer;
Buffer->Count--;
}
return Data;
} }
/** Inserts an element into the ring buffer. /** Inserts an element into the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may insert into a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to insert into * \param[in,out] Buffer Pointer to a ring buffer structure to insert into
* \param[in] Data Data element to insert into the buffer * \param[in] Data Data element to insert into the buffer
@ -150,10 +158,17 @@
if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE])
Buffer->In = Buffer->Buffer; Buffer->In = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count++; Buffer->Count++;
} }
}
/** Retrieves an element from the ring buffer. /** Removes an element from the ring buffer.
*
* \note Only one execution thread (main program thread or an ISR) may remove from a single buffer
* otherwise data corruption may occur. Insertion and removal may occur from different execution
* threads.
* *
* \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from * \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from
* *
@ -166,10 +181,12 @@
if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE]) if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE])
Buffer->Out = Buffer->Buffer; Buffer->Out = Buffer->Buffer;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
Buffer->Count--; Buffer->Count--;
}
return Data; return Data;
} }
#endif #endif

@ -140,7 +140,7 @@ ISR(TIMER3_COMPA_vect, ISR_BLOCK)
TX_Data >>= 1; TX_Data >>= 1;
TX_BitsRemaining--; TX_BitsRemaining--;
} }
else if (USBtoUART_Buffer.Count && !(RX_BitsRemaining)) else if (!(RX_BitsRemaining) && !(RingBuffer_IsEmpty(&USBtoUART_Buffer)))
{ {
/* Start bit - TX line low */ /* Start bit - TX line low */
STXPORT &= ~(1 << STX); STXPORT &= ~(1 << STX);

@ -36,8 +36,8 @@
#include "XPLAINBridge.h" #include "XPLAINBridge.h"
/* Current firmware mode, making the device behave as either a programmer or a USART bridge */ /** Current firmware mode, making the device behave as either a programmer or a USART bridge */
bool CurrentFirmwareMode = MODE_PDI_PROGRAMMER; bool CurrentFirmwareMode = MODE_USART_BRIDGE;
/** LUFA CDC Class driver interface configuration and state information. This structure is /** LUFA CDC Class driver interface configuration and state information. This structure is
* passed to all CDC Class driver functions, so that multiple instances of the same class * passed to all CDC Class driver functions, so that multiple instances of the same class
@ -122,16 +122,17 @@ void UARTBridge_Task(void)
/* Read bytes from the USB OUT endpoint into the UART transmit buffer */ /* Read bytes from the USB OUT endpoint into the UART transmit buffer */
int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface); int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
if (!(ReceivedByte < 0) && !(RingBuffer_IsFull(&USBtoUART_Buffer))) if (!(ReceivedByte < 0) && !(RingBuffer_IsFull(&USBtoUART_Buffer)))
RingBuffer_AtomicInsert(&USBtoUART_Buffer, CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface)); RingBuffer_Insert(&USBtoUART_Buffer, ReceivedByte);
/* Check if the UART receive buffer flush timer has expired */ /* Check if the UART receive buffer flush timer has expired or buffer is nearly full */
if (TIFR0 & (1 << TOV0)) RingBuff_Count_t BufferCount = RingBuffer_GetCount(&UARTtoUSB_Buffer);
if ((TIFR0 & (1 << TOV0)) || (BufferCount > 200))
{ {
TIFR0 |= (1 << TOV0); TIFR0 |= (1 << TOV0);
/* Read bytes from the UART receive buffer into the USB IN endpoint */ /* Read bytes from the UART receive buffer into the USB IN endpoint */
while (UARTtoUSB_Buffer.Count) while (BufferCount--)
CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_AtomicRemove(&UARTtoUSB_Buffer)); CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_Remove(&UARTtoUSB_Buffer));
} }
CDC_Device_USBTask(&VirtualSerial_CDC_Interface); CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
@ -184,6 +185,9 @@ void EVENT_USB_Device_ConfigurationChanged(void)
/* Initialize ring buffers used to hold serial data between USB and software UART interfaces */ /* Initialize ring buffers used to hold serial data between USB and software UART interfaces */
RingBuffer_InitBuffer(&USBtoUART_Buffer); RingBuffer_InitBuffer(&USBtoUART_Buffer);
RingBuffer_InitBuffer(&UARTtoUSB_Buffer); RingBuffer_InitBuffer(&UARTtoUSB_Buffer);
/* Start the software USART */
SoftUART_Init();
} }
else else
{ {

Loading…
Cancel
Save