// ----------------------------------------------------------------------------- // 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 #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