// -----------------------------------------------------------------------------
// USB Library "usblib"
// Author:  Sychov A.
// Date:    29/07/2019
// Version: 1.1
// -----------------------------------------------------------------------------
// File:    Thread-unsafe internal buffer-object 'USB-transfer'
// Version: 1.1
// -----------------------------------------------------------------------------

#define __USER_USB_HEADER__
#include <string.h>
#include "usb/usb_transfers.h"
//----------------------------------------------------------------------------

// usb_reset_transfer()
//
// Non thread-safe!
//
// Resets the specified transfer.
// Note: the specified end-of-transfer handler isn't called.
void usb_reset_transfer( sUSBTransfer_t * pUsbTransfer )
{
   if( NULL != pUsbTransfer )
   {
       pUsbTransfer->pWriteData = pUsbTransfer->pDefaultBuffer;
       pUsbTransfer->pReadData = pUsbTransfer->pDefaultBuffer;

       #if USB_TRANSFER_SUPPORT_FLAGS
       // reset only unlocked flags
       pUsbTransfer->flags = pUsbTransfer->flags & (pUsbTransfer->flagsLock);
       #endif
   }
}
//----------------------------------------------------------------------------
// usb_reset_channel()
//
// Non thread-safe!
//
// Resets either RX and TX transfers in the channel
void usb_reset_channel( sUSBEpChannel_t * ch )
{
   usb_reset_transfer( &ch->RxTransfer ); 
   usb_reset_transfer( &ch->TxTransfer );
}
//----------------------------------------------------------------------------
// usb_reset_channel_tx()
//
// Non thread-safe!
//
// Resets the TX-transfer in the channel
void usb_reset_channel_tx( sUSBEpChannel_t * ch )
{   
   usb_reset_transfer( &ch->TxTransfer );
}
//----------------------------------------------------------------------------
// usb_reset_channel_rx()
//
// Non thread-safe!
//
// Resets the RX-transfer in the channel
void usb_reset_channel_rx( sUSBEpChannel_t * ch )
{   
   usb_reset_transfer( &ch->RxTransfer );
}
//----------------------------------------------------------------------------
// usb_create_channel()
// Creates a bidirectional linear-buffered logical-channel
// @ch - the channel to construct
// @pTxBuffer and @txSize - buffer and size of the buffer to Tx operations
// @pRxBuffer and @rxSize - buffer and size of the buffer to Rx operations
// @eotTxHandler - the event handler, is called when data run out in Tx Buffer
// @eotRxHandler - the event handler, is called when data run out in Rx Buffer
// @chContext - user defined context, will be passed into @eotTxHandler and @eotRxHandler
// Returns true in case the channel is successfully created
#if USB_TRANSFER_SUPPORT_EOTH
bool usb_create_channel( sUSBEpChannel_t * ch,
                      void * pTxBuffer, size_t txSize,
                      void * pRxBuffer, size_t rxSize, 
                      fEndOfTransferHandler_t eotTxHandler,
                      fEndOfTransferHandler_t eotRxHandler,
                      void * channelContext )
#else
bool usb_create_channel( sUSBEpChannel_t * ch,
                      void * pTxBuffer, size_t txSize,
                      void * pRxBuffer, size_t rxSize, 
                      const void * reserved1,
                      const void * reserved2,
                      void * channelContext )
#endif
{
   #if USB_TRANSFER_SUPPORT_EOTH
   return 
      usb_create_transfer( &ch->RxTransfer, pRxBuffer, rxSize, eotRxHandler, channelContext )
   && usb_create_transfer( &ch->TxTransfer, pTxBuffer, txSize, eotTxHandler, channelContext );
   #else
   return 
      usb_create_transfer( &ch->RxTransfer, pRxBuffer, rxSize, reserved1, channelContext )
   && usb_create_transfer( &ch->TxTransfer, pTxBuffer, txSize, reserved2, channelContext );
   #endif

   /*
   if( NULL == pTxBuffer || NULL == pRxBuffer || 
          0 == txSize    ||    0 == rxSize )
   {
       return false;
   }

   ch->RxTransfer.pDefaultBuffer = (uint8_t*)pTxBuffer;
   ch->RxTransfer.size           = txSize;
   ch->RxTransfer.fEndOfTransferHandler = eotRxHandler;
   ch->RxTransfer.ctx = channelContext;

   ch->TxTransfer.pDefaultBuffer = (uint8_t*)pRxBuffer;
   ch->TxTransfer.size           = rxSize;
   ch->TxTransfer.fEndOfTransferHandler = eotTxHandler;
   ch->TxTransfer.ctx = channelContext;

   usb_reset_channel( ch );

   return true;
   */
}

//----------------------------------------------------------------------------
// usb_create_transfer()
// Creates a signle linear-buffered transfer
// @transf - the transfer to construct
// @pBuffer and @size - buffer and size of the buffer for transfer operations
// @eotHandler - the event handler, is called when data run out in the buffer
// @chContext - user defined context, will be passed into @eotHandler
// Returns true in case the channel is successfully created
#if USB_TRANSFER_SUPPORT_EOTH
bool usb_create_transfer( sUSBTransfer_t * transf,
                      void * pBuffer, size_t size,                      
                      fEndOfTransferHandler_t eotHandler,
                      void * channelContext )
#else
bool usb_create_transfer( sUSBTransfer_t * transf,
                      void * pBuffer, size_t size,                      
                      const void * reserved,
                      void * channelContext )
#endif
{
   if( NULL == pBuffer || 0 == size )
   {
       return false;
   }

   transf->pDefaultBuffer = (uint8_t*)pBuffer;
   transf->size           = size;
   #if USB_TRANSFER_SUPPORT_EOTH
   transf->fEndOfTransferHandler = eotHandler;
   #endif
   transf->ctx = channelContext;

   #if USB_TRANSFER_SUPPORT_FLAGS
   transf->flags = 0;
   transf->flagsLock = 0;  // no lock by default
   #endif

   usb_reset_transfer( transf );

   return true;
}
// usb_push_transfer()
// Non thread-safe!
// Writes data into the transfer.
// @transf - specifies the transfer to operate with
// @pWriteBuffer - data to be written
// @pushSize - amount of bytes to write
// Returns the @pushSize in case all the data has been written into the transfer
// Otherwise, returns 0. In this case no write operation has been performed.
// Note: the write operation can be split up to several write operations,
// until the buffer is full.
// To prepare the transfer for next portion of data, use usb_reset_transfer()
size_t usb_push_transfer( sUSBTransfer_t * transf, const void * pWriteBuffer, size_t pushSize )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != pWriteBuffer && 0 != pushSize && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf && NULL != pWriteBuffer && 0 != pushSize )
   #endif
   {
       size_t length = ((ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pDefaultBuffer);

       if( length + pushSize <= transf->size )
       {
           memcpy( transf->pWriteData, pWriteBuffer, pushSize );

           transf->pWriteData  += pushSize;           

           return pushSize;
       }
   }

   return 0;
}

// usb_write_transfer()
// Non thread-safe!
// Writes data into the transfer.
// @transf - specifies the transfer to operate with
// @pWriteBuffer - data to be written
// @writeSize - amount of bytes to write
// Returns amount of bytes written into the transfer
// In this case no write operation has been performed the function returns 0.
// Note: the write operation can be split up to several write operations,
// until the buffer is full.
// To prepare the transfer for next portion of data, use usb_reset_transfer()
size_t usb_write_transfer( sUSBTransfer_t * transf, const void * pWriteBuffer, size_t writeSize )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != pWriteBuffer && 0 != writeSize && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf && NULL != pWriteBuffer && 0 != writeSize )
   #endif
   {
       size_t length = ((ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pDefaultBuffer);

       if( length + writeSize > transf->size )
       {
           writeSize = transf->size - length;
       }

       if( writeSize )
       {
           memcpy( transf->pWriteData, pWriteBuffer, writeSize );

           transf->pWriteData  += writeSize;           

           return writeSize;
       }
   }

   return 0;
}


// usb_read_transfer()
//
// Non thread-safe!
//
// Reads the data from the transfer.
// @transf - specifies the transfer to operate with
// @pReadBuffer - the buffer to receive data, can be NULL
// @readSize - amount of data to copy into the @pReadBuffer, can be 0.
// @enableEotHandler - enables/disables calling of End-Of-Transfer handler 
// during the call if the transfer is empty.
// In case, @readSize is equal or greater than amount of data in the transfer,
// the function reads actual amount of data and returns count of read bytes.
// Since all data is read from the transfer, the transfer is being reset 
// automatically. If the End-of-transfer handler is specified for this transfer, 
// it will be invoked after the transfer is reset only if @enableEotHandler is 
// true, so the handler can write the next portion of data into the transfer.
// Otherwise, if @readSize is less than amount of data in the transfer, then 
// only @readSize bytes will be copyied into @pReadBuffer, the rest data are 
// still stored in the transfer-buffer and should be either read by next call, 
// or reset by calling usb_reset_transfer().
// Note: if the data in transfer is reset by calling usb_reset_transfer(), the 
// End-of-pipe handler is not called.
// You can call the function with @pReadBuffer = NULL and @readSize = 0, and
// @enableEotHandler = true to check if the transfer empty and call the End-of-
// -transfer handler. In this case the function always returns zero.
// Note: if you specify @pReadBuffer = NULL, you must set @readSize = 0.
// To determine amount of bytes in the transfer use usb_count_transfer().
//
size_t usb_read_transfer( sUSBTransfer_t * transf, void * pReadBuffer, size_t readSize, bool enableEotHandler )
{  
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {
       // retireve the data length in transfer
       size_t length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pReadData;

       // check user params:
       if( NULL != pReadBuffer && 0 != readSize )
       {
           // for the length greater than 0:
           if( length > 0 )
           {
               // user requests the part of all the data:
               if( readSize < length )
               {
                   memcpy( pReadBuffer, transf->pReadData, readSize );
                   transf->pReadData += readSize;

                   return readSize;
               }
               // user request all the data:
               else
               {
                   memcpy( pReadBuffer, transf->pReadData, length );

                   // reset transfer
                   transf->pReadData = transf->pDefaultBuffer;
                   transf->pWriteData = transf->pDefaultBuffer;

                   // call the handler if allowed and specified:
                   #if USB_TRANSFER_SUPPORT_EOTH
                   if( enableEotHandler )                   
                   if( NULL != transf->fEndOfTransferHandler )
                   {
                       size_t rc = transf->fEndOfTransferHandler( transf, transf->ctx );

                       (void)rc;
                   }
                   #endif

                   return length;
               }
           }
       }
       else
       {
           // user want to check if the transfer empty and call the handler:

           #if USB_TRANSFER_SUPPORT_EOTH
           // check the length
           if( (length == 0)
               &&
               enableEotHandler  
               &&
               (NULL != transf->fEndOfTransferHandler) 
             )
           {
               // call the handler
               size_t rc = transf->fEndOfTransferHandler( transf, transf->ctx );

               (void)rc;
           }
           #endif

           return 0;  // by design, see description
       } 
   }

   return 0;
}

// usb_count_transfer():
//
// Non thread-safe!
//
// Returns amount of data in the transfer.
// @transf - the transfer to operate with, Non-NULL
// Returns amount of data to be read.
size_t usb_count_transfer( sUSBTransfer_t * transf )
{  
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {
       // retireve the data length in transfer
       size_t length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pReadData;

       return length;    
   }

   return 0;
}

// usb_truncate_transfer()
//
// Non thread-safe!
//
// Truncates the data into the transfer.
// @transf - the transfer to operate with, Non-NULL
// @size - the size limit to apply
// The function truncates the data in the transfer to the specified @size
// Returns acutal amount of data in the transfer.
size_t usb_truncate_transfer( sUSBTransfer_t * transf, size_t size )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {
       // retireve the data length in transfer
       size_t length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pReadData;

       // truncate only if the transfer contains more data than specified by @size
       if( length > size )
       {
           // cut off extra data
           transf->pWriteData = (uint8_t*)((ptrdiff_t)transf->pReadData + (ptrdiff_t)size);

           // return amount of cut bytes
           return (size);
       }

       return (length);
   }

   return 0;
}

#if USB_TRANSFER_SUPPORT_EOTH
// usb_transfer_override_handler:
// Replaces the End-of-transfer handler for the specified transfer @transf
// @transf - the transfer to operate with
// @handler - new end-of-transfer handler
// If succeeded, the previous handler pointer is returned by function, and NULL otherwise.
fEndOfTransferHandler_t usb_transfer_override_handler( sUSBTransfer_t * transf, fEndOfTransferHandler_t handler )
{
   if( NULL != transf )
   {
       fEndOfTransferHandler_t prevHandler = transf->fEndOfTransferHandler;

       transf->fEndOfTransferHandler = handler;

       return prevHandler;
   }

   return NULL;
}
#endif

// usb_transfer_overridelength()
//
// Non thread-safe!
//
// Overrides the transfer data length.
// @transf - the transfer to operate with, Non-NULL
// @length - new length
// The function overrides the original transfer data length with the specified @length
// Returns new length value.
size_t usb_transfer_overridelength( sUSBTransfer_t * transf, size_t length )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {       
       if( length > transf->size )
       {
           length = transf->size;
       }

       // set the length
       transf->pWriteData = (uint8_t*)((ptrdiff_t)transf->pDefaultBuffer + (ptrdiff_t)length);
          
       return (length);
   }

   return 0;
}


// usb_transfer_virtual_read()
//
// Non thread-safe!
//
// Performs virtual reading from transfer without actual data retrieving from.       
// @transf - the transfer to operate with, Non-NULL
// @length - the number of bytes for virtual reading
// The function changes the transfer data length.
// Returns the number of read bytes
size_t usb_transfer_virtual_read( sUSBTransfer_t * transf, size_t length )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( (NULL != transf) && (length > 0) && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( (NULL != transf) && (length > 0) )
   #endif   
   {   
       // retireve the data length in transfer
       size_t stored_length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pReadData;

       if( length > stored_length )
       {
           length = stored_length;
       }

       // perform virtual reading by shifting the pointer @pReadData
       transf->pReadData = (uint8_t*)((ptrdiff_t)transf->pReadData + (ptrdiff_t)length);
          
       return (length);
   }

   return 0;
}

// usb_transfer_virtual_write()
//
// Non thread-safe!
//
// Performs virtual writing to transfer without actual data placing in.       
// @transf - the transfer to operate with, Non-NULL
// @length - the number of bytes for virtual writing
// The function changes the transfer data length.
// Returns the number of written bytes
size_t usb_transfer_virtual_write( sUSBTransfer_t * transf, size_t length )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( (NULL != transf) && (length > 0) && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( (NULL != transf) && (length > 0) )
   #endif
   {   
       // retireve the remaining length in transfer
       size_t remaining_length = transf->size - ((ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pDefaultBuffer);

       if( length > remaining_length )
       {
           length = remaining_length;
       }

       // perform writing reading by shifting the pointer @pWriteData
       transf->pWriteData = (uint8_t*)((ptrdiff_t)transf->pWriteData + (ptrdiff_t)length);
          
       return (length);
   }

   return 0;
}

// usb_transfer_compress()
//
// Non thread-safe!
//
// Optimizes the transfer by size by packing data inside.
// @transf - the transfer to operate with, Non-NULL
// The function moves the data from middle of the transfer to the beginning
// with keeping actual size.
// Returns the number of bytes optimized
size_t usb_transfer_compress( sUSBTransfer_t * transf )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {       
       // retireve the data length in transfer
       size_t stored_length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pReadData;
       
       if( stored_length > 0 )
       {
           // move the contents by to the beginning of the transfer (re-use)
           memmove( transf->pDefaultBuffer, transf->pReadData, stored_length );

           transf->pWriteData = (uint8_t*)((ptrdiff_t)transf->pDefaultBuffer +  stored_length);
       }
       else
       {
           transf->pWriteData = transf->pDefaultBuffer;
       }

       size_t optimized_length = (ptrdiff_t)transf->pReadData - (ptrdiff_t)transf->pDefaultBuffer;

       transf->pReadData = transf->pDefaultBuffer;

       return optimized_length;
   }

   return 0;
}

// usb_size_transfer()
//
// Non thread-safe!
//
// Returns the transfer absoulte size.
// @transf - the transfer to operate with, Non-NULL
// Returns the size in bytes
size_t usb_size_transfer( sUSBTransfer_t * transf )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {       
       return (transf->size);
   }

   return 0;
}

// usb_space_transfer()
//
// Non thread-safe!
//
// Returns the transfer available space.
// @transf - the transfer to operate with, Non-NULL
// Returns amount of free space in the transfer in bytes.
size_t usb_space_transfer( sUSBTransfer_t * transf )
{
   #if USB_TRANSFER_MOVE_SEMANTIC
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   #else
   if( NULL != transf )
   #endif
   {    
       // retireve the data length in transfer
       size_t used_length = (ptrdiff_t)transf->pWriteData - (ptrdiff_t)transf->pDefaultBuffer;
       
       return (transf->size - used_length);
   }

   return 0;
}

#if USB_TRANSFER_MOVE_SEMANTIC
// usb_move_transfer()
//
// Non thread-safe!
//
// Implements a move-semantic on couple of transfer objects
// @transf_to - the transfer for move to, can not be NULL
// @transf_from - the transfer for move from, can not be NULL
// @copyContextValue - copy the context value assotiated with the source tranfer.
// The source object (@transf_from) must be valid, while it does 
// not matter if the destination object @transf_to is invalid or not.
// After operation the source object will be destroyed, you must
// call usb_move_transfer() or usb_create_transfer() function on the source
// transfer to recover it.
// Note: user context in the source transfer will not be cleared and it can
// be used after the function call.
// Returns the operation result, true if object had been moved 
// successfully, and false otherwise.
bool usb_move_transfer( sUSBTransfer_t * transf_to, sUSBTransfer_t * transf_from, bool copyContextValue )
{
   if( NULL != transf_to && NULL != transf_from && NULL != transf_from->pDefaultBuffer && 0 != transf_from->size )
   {
       void * pDestUserContext = transf_to->ctx;

       *transf_to = *transf_from;

       transf_from->pDefaultBuffer = NULL;
       transf_from->pWriteData = NULL;
       transf_from->pReadData = NULL;
       transf_from->size = 0;

       #if USB_TRANSFER_SUPPORT_FLAGS
       // reset only the bits that are reset in @flagsLock
       // If the bit is set in @flagsLock, the corresponding bit will be kept in @flags
       transf_from->flags = transf_from->flags & transf_from->flagsLock;
       #endif

       #if USB_TRANSFER_SUPPORT_EOTH
       transf_from->fEndOfTransferHandler = NULL;
       #endif

       if( !copyContextValue )
       {
           transf_to->ctx = pDestUserContext;
       }

       // keep the source transfer's context unchanged
       (void)transf_from->ctx;
   }

   return false;
}

// usb_copy_transfer()
// Copies the data from one transfer to another
// The source transfer is not being modified if @keepSource is true.
// Otherwise, if @keepSource is false, the source transfer will be changed, 
// and the data copied to destination transfer will be removed from the source transfer.
// @count - specifies amount of data to be copied.
// If the source transfer contains less bytes that specified in @count, or the 
// destination transfer does not have enough free space, only the minimum between
// these number of bytes will be copied.
// Returns: number of copied bytes
size_t usb_copy_transfer( sUSBTransfer_t * transf_to, sUSBTransfer_t * transf_from, size_t count, bool keepSource )
{   
   if( NULL == transf_to || NULL == transf_from || 0 == count )
   {
       return 0;
   }

   #if USB_TRANSFER_MOVE_SEMANTIC
   if(    NULL == transf_to->pDefaultBuffer   || 0 == transf_to->size 
       || NULL == transf_from->pDefaultBuffer || 0 == transf_from->size )
   {
       return 0;
   }
   #endif
       
   // retireve free space from the destination transfer
   size_t free_space = transf_to->size - ((ptrdiff_t)transf_to->pWriteData - (ptrdiff_t)transf_to->pDefaultBuffer);
   
   // retrieve used length from the source transfer
   size_t used_space = (ptrdiff_t)transf_from->pWriteData - (ptrdiff_t)transf_from->pDefaultBuffer;

   // check the limits: count must be less or equal to MIN( used_space, free_space )
   if( count > free_space ) count = free_space;
   if( count > used_space ) count = used_space;

   memcpy( transf_to->pWriteData, transf_from->pReadData, count );
   
   // perform virtual writing by shifting the pointer @pWriteData
   transf_to->pWriteData = (uint8_t*)((ptrdiff_t)transf_to->pWriteData + (ptrdiff_t)count);
        
   if( !keepSource )
   {
       // perform virtual reading by shifting the pointer @pReadData
       transf_from->pReadData = (uint8_t*)((ptrdiff_t)transf_from->pReadData + (ptrdiff_t)count);
   }

   return count;
}

// usb_validate_transfer()
//
// Non thread-safe!
//
// Validates the transfer parameters. Usefull for checking objects
// after moving by usb_move_transfer().
// @transf - the transfer to operate with, Non-NULL
// Returns true if the specified object is valid, false otherwise.
bool usb_validate_transfer( sUSBTransfer_t * transf )
{
   if( NULL != transf && NULL != transf->pDefaultBuffer && 0 != transf->size )
   if( NULL != transf->pWriteData && NULL != transf->pReadData )
   {
       if(  ((ptrdiff_t)transf->pDefaultBuffer + transf->size
             >=
             (ptrdiff_t)transf->pWriteData )
          &&
            ((ptrdiff_t)transf->pDefaultBuffer + transf->size
             >=
             (ptrdiff_t)transf->pReadData )
          &&
            ((ptrdiff_t)transf->pReadData <= (ptrdiff_t)transf->pWriteData)
         )
       {
         return true;
       }
   }

   return false;
}
#endif


#if USB_TRANSFER_SUPPORT_FLAGS
// @usb_transfer_modify_flags
// Modifies the user-dependent general-purpose flags using specified mask @mask and value @value
// Returns: true if no error occurred, false otherwise.
bool usb_transfer_modify_flags( sUSBTransfer_t * transf, tUSBTransferFlags_t mask, tUSBTransferFlags_t value )
{
   if( NULL != transf )
   {
       // Modify only the bits that are not locked by @flagsLock
       transf->flags = ((transf->flags & (~transf->flagsLock)) & (~mask)) | (mask & value) | (transf->flags & transf->flagsLock);
       return true;
   }

   return false;
}

// @usb_transfer_modify_flags_locks
// Modifies the lock mask for user-dependent general-purpose flags using specified mask @mask and value @value
// Returns: true if no error occurred, false otherwise.
bool usb_transfer_modify_flags_locks( sUSBTransfer_t * transf, tUSBTransferFlags_t lockMask, tUSBTransferFlags_t lockValue )
{
   if( NULL != transf )
   {       
       transf->flagsLock = (transf->flagsLock & (~lockMask)) | (lockMask & lockValue);
       return true;
   }

   return false;
}

// @usb_transfer_check_flags_wo_checking
//
static bool usb_transfer_check_flags_wo_checking( sUSBTransfer_t * transf, tUSBTransferFlags_t mask, tUSBTransferFlags_t value, eUSBTransferFlagMode_t mode )
{
   uint32_t value1 = (transf->flags & mask);
   uint32_t value2 = (value & mask);

   if( eUSBTransferFlagMode_Or == mode )
   {
       return (0 != ((~(value1 ^ value2)) & mask));
   }
   else
   {
       return (0 == (value1 ^ value2));
   }
}

// @usb_transfer_check_flags
// Check if the user-dependent general-purpose flags fit to the specified mask @mask  and value @value using comparing method @mode.
// @transf - the transfer object to operate with;
// @mask - specified mask to check
// @value - specified value to check
// @mode - comparing method:
//  - eUSBTransferFlagMode_Or: at least one transfer bit-flag must be equal to the specified value;
//  - eUSBTransferFlagMode_And: all the transfer bit-flag must be equal to the specified value;
// @pResult = the pointer to receive output result
// Returns: true if no error occurred, false otherwise.
bool usb_transfer_check_flags( sUSBTransfer_t * transf, tUSBTransferFlags_t mask, tUSBTransferFlags_t value, eUSBTransferFlagMode_t mode, bool * pResult )
{
   if( (NULL != transf) && (NULL != pResult) )
   {
       *pResult = usb_transfer_check_flags_wo_checking( transf, mask, value, mode );

       return true;
   }

   return false;
}

// @usb_transfer_check_flags_fast
// Check if the user-dependent general-purpose flags fit to the specified mask @mask  and value @value using comparing method @mode.
// @transf - the transfer object to operate with;
// @mask - specified mask to check
// @value - specified value to check
// @mode - comparing method:
//  - eUSBTransferFlagMode_Or: at least one transfer bit-flag must be equal to the specified value;
//  - eUSBTransferFlagMode_And: all the transfer bit-flag must be equal to the specified value;
// Returns: true if no error occurred and the comparing output is TRUE, false otherwise.
// Note: you can use this function instead of @usb_transfer_check_flags if you are sure that the @transf pointer is valid.
bool usb_transfer_check_flags_fast( sUSBTransfer_t * transf, tUSBTransferFlags_t mask, tUSBTransferFlags_t value, eUSBTransferFlagMode_t mode )
{
   if( NULL != transf )
   {
       return usb_transfer_check_flags_wo_checking( transf, mask, value, mode );
   }

   return false;
}
#endif