#define USBTMCLIB_BASIC_C #include "usbtmclib/usbtmclib_basic.h" #include "app/scpi/scpi_core.h" #include static eTMCLibStatus_t tmclib_bulkin_check_inprogress(sUSBTMCContext_t*); static eTMCLibStatus_t tmclib_bulkout_check_inprogress(sUSBTMCContext_t*); static eTMCLibStatus_t tmclib_new_data_transfer(sUSBTMCContext_t*, sUSBTransfer_t *); static eTMCLibStatus_t tmclib_bulkout_process( sUSBTMCContext_t *, sUSBTransfer_t * ); static eTMCLibStatus_t tmclib_bulkin_process( sUSBTMCContext_t *, sUSBTransfer_t * ); static eTMCLibStatus_t tmclib_interruptin_process( sUSBTMCContext_t *, sUSBTransfer_t * ); static void tmclib_bulkout_error( sUSBTMCContext_t *, sUSBTransfer_t *, eTMCLibStatus_t ); static void tmclib_bulkin_error( sUSBTMCContext_t *, sUSBTransfer_t *, eTMCLibStatus_t ); static void tmclib_interruptin_error( sUSBTMCContext_t *, sUSBTransfer_t *, eTMCLibStatus_t ); static sUSBTMCContext_t ctxUsbTmc; static eTMCLibStatus_t lastRequestStatus = tmclib_status_success; #if DEBUG_USBTMC > 0 // SCPI debug only volatile uint32_t gDebugUSBTMC_TxBytes = 0; #endif // ================================================================================= // tmclib_cleanup_bulkout() static void tmclib_cleanup_bulkout_header( sUSBTMCContext_t * ctx ) { ctx->bulkOutHeader.bTag = 0; ctx->bulkOutHeader.bTagInverse = 0; ctx->bulkOutHeader.MsgID = eUsbtmcMsg_Reserved; ctx->bulkOutHeader.Reserved = 0; memset( ctx->bulkOutHeader.cmdSpec.rawBytes, 0, sizeof(ctx->bulkOutHeader.cmdSpec.rawBytes) ); } // ================================================================================= // tmclib_cleanup_bulkin() static void tmclib_cleanup_bulkin_header( sUSBTMCContext_t * ctx ) { ctx->bulkInHeader.bTag = 0; ctx->bulkInHeader.bTagInverse = 0; ctx->bulkInHeader.MsgID = eUsbtmcMsg_Reserved; ctx->bulkInHeader.Reserved = 0; memset( ctx->bulkInHeader.cmdSpec.rawBytes, 0, sizeof(ctx->bulkInHeader.cmdSpec.rawBytes) ); } // ================================================================================= // tmclib_cleanup_interruptin() static void tmclib_cleanup_interruptin_header( sUSBTMCContext_t * ctx ) { memset( &ctx->interruptInHeader, 0, sizeof(ctx->interruptInHeader) ); } // ================================================================================= // tmclib_write_bulkin_header() // Writes current bulk-in header (DevDepMsgIn) into output transfer. static bool tmclib_write_bulkin_header( sUSBTMCContext_t * ctx ) { (void)ctx->bulkInHeader.bTag; (void)ctx->bulkInHeader.bTagInverse; (void)ctx->bulkInHeader.MsgID; (void)ctx->bulkInHeader.Reserved; (void)ctx->bulkInHeader.cmdSpec.DevDepMsgIn.transferSize; (void)ctx->bulkInHeader.cmdSpec.DevDepMsgIn.Reserved; (void)ctx->bulkInHeader.cmdSpec.DevDepMsgIn.bmTransferAttributes; // remember the raw Bulk-In header to be able to modify data later ctx->psActiveInHeader = (sUSBTMCBulkInHeader_t*)usb_transfer_raw_read(ctx->bulkInState.transf); // push entire header if( !usb_push_transfer( ctx->bulkInState.transf, ctx->bulkInHeader.rawBytes, sizeof(ctx->bulkInHeader) ) ) { ctx->psActiveInHeader = NULL; return false; } return true; } // ================================================================================= // tmclib_update_bulkin() // Writes current bulk-in header (DevDepMsgIn) into output transfer. static bool tmclib_update_bulkin( sUSBTMCContext_t * ctx ) { my_assert( ctx ); if( NULL != ctx->psActiveInHeader ) { // update current transfer BulkIN header: ctx->psActiveInHeader->cmdSpec.DevDepMsgIn.transferSize = ctx->bulkInState.transferSize; // update TransferSize field // check End-of-message indicator if( ctx->bulkInState.bEndOfMessage ) ctx->psActiveInHeader->cmdSpec.DevDepMsgIn.bmTransferAttributes |= bm_DEV_DEP_MSG_IN_EOM; // set End-Of-Message indicator else ctx->psActiveInHeader->cmdSpec.DevDepMsgIn.bmTransferAttributes &= ~bm_DEV_DEP_MSG_IN_EOM; // reset End-Of-Message indicator return true; } return false; } // ================================================================================= // tmclib_write_notification_header() // Writes current interrupt-in header into output transfer. static bool tmclib_write_notification_header( sUSBTMCContext_t * ctx ) { // remember the raw Notification (Interrupt-In) header to be able to modify data later ctx->psActiveNotificationHeader = (sUSBTMCNotificationHeader_t*)usb_transfer_raw_read(ctx->interruptInState.transf); // push entire header if( !usb_push_transfer( ctx->interruptInState.transf, ctx->interruptInHeader.rawBytes, sizeof(ctx->interruptInHeader) ) ) { ctx->psActiveNotificationHeader = NULL; return false; } // only for interrupt-in transfer: calculate the bytes including the header bytes ctx->interruptInState.bytesCounter += sizeof(ctx->interruptInHeader); return true; } // ================================================================================= // tmclib_update_notification() // Writes current notification header into output transfer. static bool tmclib_update_notification( sUSBTMCContext_t * ctx ) { my_assert( ctx ); if( NULL != ctx->psActiveNotificationHeader ) { // update current transfer BulkIN header: USBTMC_USB488NotificationHeader_SetTag( ctx->interruptInHeader, ctx->interruptInState.bTag ); USBTMC_USB488NotificationHeader_SetSTB( ctx->interruptInHeader, ctx->interruptInState.STB ); USBTMC_USB488NotificationHeader_SetTag( *(ctx->psActiveNotificationHeader), ctx->interruptInState.bTag ); USBTMC_USB488NotificationHeader_SetSTB( *(ctx->psActiveNotificationHeader), ctx->interruptInState.STB ); return true; } return false; } // ================================================================================= // @tmclib_init() // Initialize TMC library // @rx - receiving buffer object // @tx - transmitting buffer object // @ntx - interrupt/notify transmitting buffer object eTMCLibStatus_t tmclib_init( sUSBTransfer_t * rx, sUSBTransfer_t * tx, sUSBTransfer_t * ntx ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( usb_size_transfer( rx ) > SCPI_MAX_INPUT_COMMAND ) { // Invalid value SCPI_MAX_INPUT_COMMAND. // Maximum input command length shall be at least equal to @rx size or more (see 'USB_BULK_USBTMC_RX_BUFFER') // The longest input packet must fit to the command buffer. // This check can not be performed in compile time due to isolation between low-level buffer and SCPI parser level. my_assert( usb_size_transfer( rx ) <= SCPI_MAX_INPUT_COMMAND ); return tmclib_status_invalid_length; } tmclib_cleanup_bulkout_header( ctx ); tmclib_cleanup_bulkin_header( ctx ); tmclib_cleanup_interruptin_header( ctx ); ctx->bulkOutState.bInProgress = false; ctx->bulkOutState.bEndOfMessage = false; ctx->bulkOutState.transferSize = 0; ctx->bulkOutState.bytesCounter = 0; ctx->bulkOutState.transf = rx; ctx->bulkOutState.bNewTransfer = false; ctx->bulkOutState.bLatestTag = 0; ctx->bulkInState.bInProgress = false; ctx->bulkInState.bEndOfMessage = false; ctx->bulkInState.transferSize = 0; ctx->bulkInState.bytesCounter = 0; ctx->bulkInState.transf = tx; ctx->bulkInState.bNewTransfer = false; ctx->bulkInState.bLatestTag = 0; ctx->interruptInState.bInProgress = false; ctx->interruptInState.bEndOfMessage = false; ctx->interruptInState.bytesCounter = 0; ctx->interruptInState.bTag = 0; ///??? ctx->interruptInState.STB = 0; ///??? ctx->interruptInState.transf = ntx; ctx->interruptInState.bNewTransfer = false; if( eScpiStatus_success != sSCPILibHandle.fInit() ) { return (lastRequestStatus = tmclib_status_failure); } return (lastRequestStatus = tmclib_status_success); } // ================================================================================= // @tmclib_deinit() // Deinitialize TMC library eTMCLibStatus_t tmclib_deinit() { tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkin(0, true) ); tmclib_generic_event( COMPOUND_tmclib_event_context_abort_interruptin(0, true) ); sSCPILibHandle.fDeInit(); return (lastRequestStatus = tmclib_status_success); } // ================================================================================= // @tmclib_abort_bulkout() // Aborts currently running BulkOUT transfer // @eventCtx->abort: // * @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.2 [1] // * @force - makes to ignore the @transfer_bTag if true // Returns: // in case of success: @tmclib_status_success // in case of error: @tmclib_status_not_in_progress eTMCLibStatus_t tmclib_abort_bulkout( sTMCEventContext_t * eventCtx ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( eventCtx->event != eTMCEventBulkOutStop ) { my_assert( eventCtx->event == eTMCEventBulkOutStop ); return (lastRequestStatus = tmclib_status_invalid_param); // invalid event } if( eventCtx->abort.force || ctx->bulkOutState.bInProgress ) { if( eventCtx->abort.force || (eventCtx->abort.transfer_bTag == ctx->bulkOutHeader.bTag) ) { ctx->bulkOutState.bLatestTag = ctx->bulkOutHeader.bTag; // remember latest bTag ctx->bulkOutState.bInProgress = false; ctx->bulkOutState.bNewTransfer = false; (void)ctx->bulkOutState.bytesCounter; // do not clear the last transfer byte counter to respond with // force cancellation: forget about latest transaction if( eventCtx->abort.force ) ctx->bulkOutState.bLatestTag = 0; tmclib_cleanup_bulkout_header( ctx ); // check if No-Reset-Transfer feature is requested if( !eventCtx->abort.noResetTransfer ) {usb_reset_transfer( ctx->bulkOutState.transf );} // Not requested, just reset transfer else {usb_transfer_compress( ctx->bulkOutState.transf );} // Requested, compress transfer return (lastRequestStatus = tmclib_status_success); } } // check for already aborted transfer: if( (eventCtx->abort.transfer_bTag == ctx->bulkOutState.bLatestTag) ) { // forget about latest transaction: set zero value ctx->bulkOutState.bLatestTag = 0; // this transfer has been already aborted: return 'success' code for previous aborted transfer return (lastRequestStatus = tmclib_status_success); } // Error: BulkOut transfer is not in progress return (lastRequestStatus = tmclib_status_not_in_progress); } // ================================================================================= // @tmclib_abort_bulkin() // Aborts currently running BulkIN transfer // @eventCtx->abort: // * @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.4 [1] // * @force - makes to ignore the @transfer_bTag if true // Returns: // in case of success: @tmclib_status_success // in case of error: @tmclib_status_not_in_progress eTMCLibStatus_t tmclib_abort_bulkin( sTMCEventContext_t * eventCtx ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( eventCtx->event != eTMCEventBulkInStop ) { my_assert( eventCtx->event == eTMCEventBulkInStop ); return (lastRequestStatus = tmclib_status_invalid_param); // invalid event } if( eventCtx->abort.force || ctx->bulkInState.bInProgress ) { if( eventCtx->abort.force || (eventCtx->abort.transfer_bTag == ctx->bulkInHeader.bTag) ) { ctx->bulkInState.bLatestTag = ctx->bulkInHeader.bTag; // remember latest bTag ctx->bulkInState.bInProgress = false; ctx->bulkInState.bNewTransfer = false; ctx->psActiveInHeader = NULL; (void)ctx->bulkInState.bytesCounter; // do not clear the last transfer byte counter to respond with // force cancellation: forget about latest transaction if( eventCtx->abort.force ) ctx->bulkInState.bLatestTag = 0; tmclib_cleanup_bulkin_header( ctx ); // @noResetTransfer - not supported for BulkIN my_assert( !eventCtx->abort.noResetTransfer ); usb_reset_transfer( ctx->bulkInState.transf ); // reset short packet indicator? if( eventCtx->abort.clearShortPacketIndicator ) { // reset short packet indicator to NOT TO SEND zero packet usb_transfer_clear_shortpacket( ctx->bulkInState.transf ); } return (lastRequestStatus = tmclib_status_success); } } // check for already aborted transfer: if( (eventCtx->abort.transfer_bTag == ctx->bulkInState.bLatestTag) ) { // forget about latest transaction: set zero value ctx->bulkInState.bLatestTag = 0; // this transfer has been already aborted: return 'success' code for previous aborted transfer return (lastRequestStatus = tmclib_status_success); } // Error: BulkIn transfer is not in progress return (lastRequestStatus = tmclib_status_not_in_progress); } // ================================================================================= // @tmclib_abort_interruptin() // Aborts currently running InterruptIN transfer // @eventCtx->abort: // * @transfer_bTag - The bTag value associated with the transfer to abort, see 4.3.1 [2] // * @force - makes to ignore the @transfer_bTag if true // Returns: // in case of success: @tmclib_status_success // in case of error: @tmclib_status_not_in_progress static eTMCLibStatus_t tmclib_abort_interruptin( sTMCEventContext_t * eventCtx ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( eventCtx->event != eTMCEventInterruptInStop ) { my_assert( eventCtx->event == eTMCEventInterruptInStop ); return (lastRequestStatus = tmclib_status_invalid_param); // invalid event } if( eventCtx->abort.force || ctx->interruptInState.bInProgress ) { if( eventCtx->abort.force || (eventCtx->abort.transfer_bTag == ctx->interruptInState.bTag ) ) { ctx->interruptInState.bInProgress = false; ctx->interruptInState.bNewTransfer = false; ctx->interruptInState.bEndOfMessage = false; ctx->interruptInState.bytesCounter = 0; ctx->interruptInState.STB = 0; ///??? ctx->interruptInState.bTag = 0; ///??? ctx->psActiveNotificationHeader = NULL; tmclib_cleanup_interruptin_header( ctx ); // @noResetTransfer - not supported for InterruptIN my_assert( !eventCtx->abort.noResetTransfer ); usb_reset_transfer( ctx->interruptInState.transf ); return (lastRequestStatus = tmclib_status_success); } } // Error: BulkOut transfer is not in progress return (lastRequestStatus = tmclib_status_not_in_progress); } // ================================================================================= // @tmclib_complete_interruptin() // Aborts currently running InterruptIN transfer // @transfer_bTag - The bTag value associated with the transfer to abort, see 4.3.1 [2] // @force - makes to ignore the @transfer_bTag if true // Returns: // in case of success: @tmclib_status_success // in case of error: @tmclib_status_not_in_progress eTMCLibStatus_t tmclib_complete_interruptin( uint8_t transfer_bTag, bool force ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( force || ctx->interruptInState.bInProgress ) { if( force || (transfer_bTag == ctx->interruptInState.bTag ) ) { ctx->interruptInState.bInProgress = false; ctx->interruptInState.bNewTransfer = false; ctx->interruptInState.bEndOfMessage = true; (void)ctx->interruptInState.bytesCounter; (void)ctx->interruptInState.STB; ///??? (void)ctx->interruptInState.bTag; ///??? (void)ctx->psActiveNotificationHeader; (void)&ctx->interruptInHeader; (void)ctx->interruptInState.transf; return (lastRequestStatus = tmclib_status_success); } } // Error: Interrupt-IN transfer is not in progress return (lastRequestStatus = tmclib_status_not_in_progress); } // ================================================================================= // @tmclib_new_data_transfer() // Starts new BulkOUT/BulkIN transfer // @ctx - the USBTMC context static eTMCLibStatus_t tmclib_new_data_transfer( sUSBTMCContext_t * ctx, sUSBTransfer_t * rx ) { eTMCLibStatus_t status = tmclib_status_success; do { if( usb_count_transfer( rx ) < sizeof( ctx->bulkOutHeader ) ) { // 3.2 Bulk-OUT endpoint, [1] // error: BulkOut header is fragmented // Either the host sent the header fragmented, or the internal buffer // is too small. Processing is impossible. status = tmclib_status_header_fragmented_error; break; } // Read the BulkOut header from the transfer if( sizeof( ctx->bulkOutHeader ) != usb_read_transfer( rx, &ctx->bulkOutHeader, sizeof( ctx->bulkOutHeader ), false ) ) { // error: can not read the header status = tmclib_status_read_error; break; } if( ( (1ul << 8*sizeof(ctx->bulkOutHeader.bTag)) - 1 != (ctx->bulkOutHeader.bTag ^ ctx->bulkOutHeader.bTagInverse)) // @bTag must not be zero, 3.2 Bulk-OUT endpoint, [1] || 0 == ctx->bulkOutHeader.bTag ) { // error: invalid bTag field // 3.2.2.3 Bulk-OUT transfer protocol errors, [1] status = tmclib_status_header_error; break; } eUSBTMCMsgId_t msgId = (eUSBTMCMsgId_t)ctx->bulkOutHeader.MsgID; switch( msgId ) { case eUsbtmcMsg_DevDepMsgOut: { if( 0 == ctx->bulkOutHeader.cmdSpec.DevDepMsgOut.transferSize ) { // error: invalid @transferSize status = tmclib_status_header_error; } else { // Initialize BulkOut transfer ctx->bulkOutState.transferSize = ctx->bulkOutHeader.cmdSpec.DevDepMsgOut.transferSize; ctx->bulkOutState.bInProgress = true; ctx->bulkOutState.bNewTransfer = true; ctx->bulkOutState.bytesCounter = 0; ctx->bulkOutState.bEndOfMessage = USBTMC_CHECK_BITMAP( ctx->bulkOutHeader.cmdSpec.DevDepMsgOut.bmTransferAttributes, bm_DEV_DEP_MSG_OUT_EOM ); status = tmclib_status_success; } } break; case eUsbtmcMsg_ReqDevDepMsgIn: { if( 0 == ctx->bulkOutHeader.cmdSpec.ReqDevDepMsgIn.transferSize ) { // error: invalid @transferSize status = tmclib_status_header_error; } else { if( USBTMC_CHECK_BITMAP( ctx->bulkOutHeader.cmdSpec.ReqDevDepMsgIn.bmTransferAttributes, bm_REQUEST_DEV_DEP_MSG_IN_TRMCH ) ) { // error: TermChar is not supported status = tmclib_status_unsupported_termchar; } else { // Initialize BulkIn transfer ctx->bulkInState.transferSize = ctx->bulkOutHeader.cmdSpec.ReqDevDepMsgIn.transferSize; ctx->bulkInState.bInProgress = true; ctx->bulkInState.bNewTransfer = true; ctx->bulkInState.bytesCounter = 0; ctx->bulkInState.bEndOfMessage = false; // @bEndOfMessage can be changed later // Filling header: ctx->bulkInHeader.bTag = ctx->bulkOutHeader.bTag; ctx->bulkInHeader.bTagInverse = ctx->bulkOutHeader.bTagInverse; ctx->bulkInHeader.MsgID = eUsbtmcMsg_DevDepMsgIn; ctx->bulkInHeader.Reserved = 0; ctx->bulkInHeader.cmdSpec.DevDepMsgIn.transferSize = ctx->bulkInState.transferSize; ctx->bulkInHeader.cmdSpec.DevDepMsgIn.bmTransferAttributes = bm_DEV_DEP_MSG_IN_EOM; // set End-of-message indicator by default status = tmclib_status_success; } } } break; case eUsbtmcMsg_VendorSpecificOut: { // error: VENDOR_SPECIFIC_OUT message is not supported status = tmclib_status_unsupported_message_error; } break; case eUsbtmcMsg_ReqVendorSpecificIn: { // error: REQUEST_VENDOR_SPECIFIC_IN message is not supported status = tmclib_status_unsupported_message_error; } break; case eUsbtmcMsg_Usb488_Trigger: { // error: Trigger message is not supported status = tmclib_status_unsupported_message_error; } break; default: { // error: Invalid MsgID status = tmclib_status_invalid_message_error; } } } while( false ); return (lastRequestStatus = status); } // ================================================================================= // @tmclib_new_notification_transfer() // Starts new InterruptIN transfer // @ctx - the USBTMC context // @bTag - the Tag identifier, [2], 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request static eTMCLibStatus_t tmclib_new_notification_transfer( sUSBTMCContext_t * ctx, uint8_t bTag ) { eTMCLibStatus_t status = tmclib_status_success; do { if( usb_space_transfer( ctx->interruptInState.transf ) < sizeof( ctx->interruptInHeader ) ) { // 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request, [2] // error: no free space to place notification Interrupt-IN packet. Processing is impossible. status = tmclib_status_header_fragmented_error; break; } // Initialize InterruptIn transfer ctx->interruptInState.bTag = bTag; ///??? ctx->interruptInState.STB = 0; ///??? ctx->interruptInState.bInProgress = true; ctx->interruptInState.bNewTransfer = true; ctx->interruptInState.bEndOfMessage = false; ctx->interruptInState.bytesCounter = 0; // Filling header (can be modified later by @tmclib_update_notification): tmclib_cleanup_interruptin_header( ctx ); status = tmclib_status_success; } while( false ); return (lastRequestStatus = status); } // ================================================================================= // @tmclib_bulkout_gettag // Returns the bTag field for current BulkOut transfer // @pbTag - pointer to the bTag to be filled, can not be NULL // Note: if there no transaction is, function returns bTag=0 // Returns: returns @tmclib_status_success if @pbTag is not NULL, // otherwise returns @tmclib_status_invalid_param eTMCLibStatus_t tmclib_bulkout_gettag( uint8_t * pbTag ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pbTag ) { return ( lastRequestStatus = tmclib_status_invalid_param ); } if( ctx->bulkOutState.bInProgress ) { *pbTag = ctx->bulkOutHeader.bTag; } else { *pbTag = 0; } return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkin_gettag // Returns the bTag field for current BulkIn transfer // @pbTag - pointer to the bTag to be filled, can not be NULL // Note: if there no transaction is, function returns bTag=0 // Returns: returns @tmclib_status_success if @pbTag is not NULL, // otherwise returns @tmclib_status_invalid_param eTMCLibStatus_t tmclib_bulkin_gettag( uint8_t * pbTag ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pbTag ) { return ( lastRequestStatus = tmclib_status_invalid_param ); } if( ctx->bulkInState.bInProgress ) { *pbTag = ctx->bulkInHeader.bTag; } else { *pbTag = 0; } return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkout_gettag_latest // Returns the bTag field for latest valid BulkOut transfer // @pbTag - pointer to the bTag to be filled, can not be NULL // Note: if there no transaction is, function returns bTag for the latest valid // BulkOut transfer, or bTag for current transaction otherwise. // Returns: returns @tmclib_status_success if @pbTag is not NULL, // otherwise returns @tmclib_status_invalid_param eTMCLibStatus_t tmclib_bulkout_gettag_latest( uint8_t * pbTag ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pbTag ) { return ( lastRequestStatus = tmclib_status_invalid_param ); } if( ctx->bulkOutState.bInProgress ) { // current transaction *pbTag = ctx->bulkOutHeader.bTag; } else { if( 0 != ctx->bulkOutState.bLatestTag ) { *pbTag = ctx->bulkOutState.bLatestTag; } else { *pbTag = 0; } } return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkin_gettag_latest // Returns the bTag field for latest valid BulkIn transfer // @pbTag - pointer to the bTag to be filled, can not be NULL // Note: if there no transaction is, function returns bTag for the latest valid // BulkIn transfer, or bTag for current transaction otherwise. // Returns: returns @tmclib_status_success if @pbTag is not NULL, // otherwise returns @tmclib_status_invalid_param eTMCLibStatus_t tmclib_bulkin_gettag_latest( uint8_t * pbTag ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pbTag ) { return ( lastRequestStatus = tmclib_status_invalid_param ); } if( ctx->bulkInState.bInProgress ) { // current transaction *pbTag = ctx->bulkInHeader.bTag; } else { if( 0 != ctx->bulkInState.bLatestTag ) { *pbTag = ctx->bulkInState.bLatestTag; } else { *pbTag = 0; } } return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkout_getcounter // Returns the bytes counter for current BulkOut transfer // @pnBytes - pointer to the uint32_t variable to be filled, can not be NULL // Note: if there no transaction is, function returns the counter for the last transfer // Returns: returns @tmclib_status_success if @pnBytes is not NULL, // otherwise returns @tmclib_status_failure eTMCLibStatus_t tmclib_bulkout_getcounter( uint32_t * pnBytes ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pnBytes ) { return ( lastRequestStatus = tmclib_status_failure ); } *pnBytes = ctx->bulkOutState.bytesCounter; return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkin_getcounter // Returns the bytes counter for current BulkIn transfer // @pnBytes - pointer to the uint32_t variable to be filled, can not be NULL // Note: if there no transaction is, function returns the counter for the last transfer // Returns: returns @tmclib_status_success if @pnBytes is not NULL, // otherwise returns @tmclib_status_failure eTMCLibStatus_t tmclib_bulkin_getcounter( uint32_t * pnBytes ) { sUSBTMCContext_t * ctx = &ctxUsbTmc; if( NULL == pnBytes ) { return ( lastRequestStatus = tmclib_status_failure ); } *pnBytes = ctx->bulkInState.bytesCounter; return ( lastRequestStatus = tmclib_status_success ); } // ================================================================================= // @tmclib_bulkout_check_inprogress() // Checks weither the Bulk-OUT transfer is in progress // @ctx - the USBTMC context // Returns: tmclib_status_in_progress in case the transfer is in progress, // and tmclib_status_not_in_progress otherwise. static eTMCLibStatus_t tmclib_bulkout_check_inprogress( sUSBTMCContext_t * ctx ) { if( ! ctx->bulkOutState.bInProgress ) { // status: not in progrss return (lastRequestStatus = tmclib_status_not_in_progress); } // status: In progrss return (lastRequestStatus = tmclib_status_in_progress); } // ================================================================================= // @tmclib_bulkin_check_inprogress() // Checks weither the Bulk-IN transfer is in progress // @ctx - the USBTMC context // Returns: tmclib_status_in_progress in case the transfer is in progress, // and tmclib_status_not_in_progress otherwise. static eTMCLibStatus_t tmclib_bulkin_check_inprogress( sUSBTMCContext_t * ctx ) { if( ! ctx->bulkInState.bInProgress ) { // status: not in progrss return (lastRequestStatus = tmclib_status_not_in_progress); } // status: In progrss return (lastRequestStatus = tmclib_status_in_progress); } // ================================================================================= // @tmclib_interruptin_check_inprogress() // Checks weither the Interrupt-IN transfer is in progress // @ctx - the USBTMC context // Returns: tmclib_status_in_progress in case the transfer is in progress, // and tmclib_status_not_in_progress otherwise. static eTMCLibStatus_t tmclib_interruptin_check_inprogress( sUSBTMCContext_t * ctx ) { if( ! ctx->interruptInState.bInProgress ) { // status: not in progrss return (lastRequestStatus = tmclib_status_not_in_progress); } // status: In progrss return (lastRequestStatus = tmclib_status_in_progress); } // ================================================================================= // @tmclib_bulkout_event // Processes USBTMC Bulk-OUT event // @eventCtx - event context (sTMCBulkOutEventCtx_t part is used) // @eventCtx->event - event id to process (only eTMCEventIO is supported): // * eTMCEventIO - data I/O signal // @eventCtx->bulkOut.rx - the buffer object to be used to receive data // Returns: the operation status: // tmclib_status_success in case of success, // tmclib_status_in_progress in of no error has occurred, but it is required to receive more data, // tmclib_status_halt_bulkin in case of error and it is required to halt BulkIN EP, // tmclib_status_failure otherwise and it is requried to halt BulkOUT EP. static eTMCLibStatus_t tmclib_bulkout_event( sTMCEventContext_t * eventCtx ) { eTMCLibStatus_t status = tmclib_status_success; sUSBTMCContext_t * ctx = &ctxUsbTmc; bool bProcessed = false; switch( eventCtx->event ) { case eTMCEventBulkOut: { // Check if the BulkOut transfer is in progress if( tmclib_status_not_in_progress == tmclib_bulkout_check_inprogress( ctx ) ) { // No, the transfer either is aborted or not started yet. // create new BulkOut/BulkIn transfer status = tmclib_new_data_transfer( ctx, eventCtx->bulkOut.rx ); // check for BulkIn transfer if( tmclib_status_in_progress == tmclib_bulkin_check_inprogress( ctx ) ) { // new BulkIN transfer just has been created bProcessed = true; // at least one transfer is created // push the BULKIN header into output transfer: if( !tmclib_write_bulkin_header( ctx ) ) { status = tmclib_status_failure; } else { // this is to unlock transfer flags and reset short-packet indicator for new transfer usb_transfer_clear_shortpacket( ctx->bulkInState.transf ); // perform virtual BulkIN event status = tmclib_bulkin_process( ctx, ctx->bulkInState.transf ); // check status if( USBTMC_NOTERROR(status) ) { switch(status) { case tmclib_status_in_progress: { my_assert( status != tmclib_status_in_progress ); // 'tmclib_bulkin_process' never returns 'tmclib_status_in_progress' status = tmclib_status_failure; } break; case tmclib_status_not_in_progress: { my_assert( status != tmclib_status_not_in_progress ); // 'tmclib_bulkin_process' never returns 'tmclib_status_not_in_progress' status = tmclib_status_failure; } break; case tmclib_status_again: { my_assert( status != tmclib_status_again ); // 'tmclib_bulkin_process' never returns 'tmclib_status_again' status = tmclib_status_failure; } break; case tmclib_status_need_data: { my_assert( status != tmclib_status_need_data ); // 'tmclib_bulkin_process' never returns 'tmclib_status_need_data' status = tmclib_status_failure; } break; case tmclib_status_success: case tmclib_status_no_more_data: case tmclib_status_need_read: { // if BulkIN event succeded, it is required to // ... send data from output data. status = tmclib_status_need_read; } break; default: my_assert( status == tmclib_status_success ); // 'tmclib_bulkin_process' returns undefined code } } } } } } break; default: status = tmclib_status_invalid_param; // invalid event my_assert( false ); } if( USBTMC_NOTERROR(status) ) { // Yes, continue the transfer processing // Check if BulkOut transfer is in progress // NOTE: BulkOUT transfer also initiates BulkIN transfers, so, // ... event @status is success, the BulkOut transfer can be // ... not started due to it just initiates BulkIn transfer. if( tmclib_status_in_progress == tmclib_bulkout_check_inprogress( ctx ) ) { bProcessed = true; // at least one transfer is created status = tmclib_bulkout_process( ctx, eventCtx->bulkOut.rx ); } } if( !bProcessed ) { if( status == tmclib_status_success ) status = tmclib_status_not_in_progress; } lastRequestStatus = status; // check the error code: if( USBTMC_ERROR(status) ) { tmclib_bulkout_error( ctx, eventCtx->bulkOut.rx, status ); tmclib_abort_bulkout( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); if( status != tmclib_status_halt_bulkin ) { status = tmclib_status_failure; } } return ( status ); } // ================================================================================= // @tmclib_bulkin_event // Processes USBTMC Bulk-IN event // @eventCtx - event context (sTMCBulkInEventCtx_t part is used) // @eventCtx->event - event id to process (only eTMCEventIO is supported): // * eTMCEventIO - data I/O signal // @eventCtx->bulkIn.tx - the buffer object to be used to transmit data // Returns: the operation status: // =tmclib_status_success in case of success, // >tmclib_status_success in case of warning, // event ) { case eTMCEventBulkIn: { // Check if the BulkIn transfer is in progress if( tmclib_status_in_progress == tmclib_bulkin_check_inprogress( ctx ) ) { // Yes, the transfer is in progress // continue the transfer processing status = tmclib_bulkin_process( ctx, eventCtx->bulkIn.tx ); } else { // BulkIn transfer is not in progress // Possibilities: // 1. It is the last packet and it is would be good to clear EP IN // 2. It is an erroneous IN-transaction from host status = tmclib_status_not_in_progress; } } break; default: status = tmclib_status_invalid_param; // invalid event my_assert( false ); } lastRequestStatus = status; // Do to transform WARNING codes to ERRORs: generate BulkIN error on error codes only if( USBTMC_ERROR(status) ) { tmclib_bulkin_error( ctx, eventCtx->bulkIn.tx, status ); } return ( status ); } // ================================================================================= // @tmclib_interruptin_event // Processes USBTMC Interrupt-IN event // @eventCtx - event context (sTMCInterruptInEventCtx_t part is used) // @eventCtx->event - event id to process: // * eTMCEventInterruptInStart - startup signal to initiate InterruptIn trnasfer, must be generated before eTMCEventIO // * eTMCEventIO - data I/O signal // @eventCtx->interruptIn.ntx - the buffer object to be used to transmit data, must be NULL on 'eTMCEventInterruptInStart' event // @eventCtx->interruptIn.bTag - the Tag identifier, [2], 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request, // ... is valid only on 'eTMCEventInterruptInStart' event // // Returns: the operation status: // tmclib_status_success in case of success, // tmclib_status_failure otherwise // // Note: if @initiate is true, the function only creates new transfer without calling Tx-Handler, // and @ntx must be NULL // // Note: Each event generates a single INTERRUPT-IN packet begining with Interrupt-IN header // Note: Each interrupt-in event must be not longer than @ntx transfer length. // Note: In case it is required to send more data than @ntx can carry, you need: // - increase Interrupt-IN EP buffer (see USB specification) and increase @ntx transfer space // or: // - send the data using multiple packets (each packet begins with INTERRUPT-IN header), pay // attention that @ctx->interruptInState will not be reset until user layer return tmclib_status_success // or tmclib_status_no_more_data. static eTMCLibStatus_t tmclib_interruptin_event( sTMCEventContext_t * eventCtx ) { eTMCLibStatus_t status = tmclib_status_not_in_progress; // by default: transfer is not in progress sUSBTMCContext_t * ctx = &ctxUsbTmc; switch( eventCtx->event ) { case eTMCEventInterruptInStart: /* do not process event on start signal */ { // Only create the transfer on start signal if( NULL != eventCtx->interruptIn.ntx ) { // Formal checking: on @initiate=true no @ntx pointer is known // Do checking to catch errors. // Do not call @tmclib_interruptin_error due to @ntx is must be NULL, but it is not by error return tmclib_status_invalid_param; } // Create new transfer with specified @bTag status = tmclib_new_notification_transfer( ctx, eventCtx->interruptIn.bTag ); } break; case eTMCEventInterruptIn: { // check for Interrupt-In transfer // Continue only if transfer is already started if( tmclib_status_in_progress == tmclib_interruptin_check_inprogress( ctx ) ) { // new InterruptIN transfer just has been created // push the INTERRUPT-IN header into output transfer // Warning: each event begins with new notification header. if( !tmclib_write_notification_header( ctx ) ) { status = tmclib_status_failure; } else { /* No short packet control is required #if 0 // this is to unlock transfer flags and reset short-packet indicator for new transfer usb_transfer_clear_shortpacket( ntx ); #endif */ // perform virtual InterruptIN event status = tmclib_interruptin_process( ctx, eventCtx->interruptIn.ntx ); // check status if( USBTMC_NOTERROR(status) ) { switch(status) { case tmclib_status_in_progress: { my_assert( status != tmclib_status_in_progress ); // 'tmclib_interruptin_process' never returns 'tmclib_status_in_progress' status = tmclib_status_failure; } break; case tmclib_status_not_in_progress: { my_assert( status != tmclib_status_not_in_progress ); // 'tmclib_interruptin_process' never returns 'tmclib_status_not_in_progress' status = tmclib_status_failure; } break; case tmclib_status_again: { my_assert( status != tmclib_status_again ); // 'tmclib_interruptin_process' never returns 'tmclib_status_again' status = tmclib_status_failure; } break; case tmclib_status_need_data: { my_assert( status != tmclib_status_need_data ); // 'tmclib_interruptin_process' never returns 'tmclib_status_need_data' status = tmclib_status_failure; } break; case tmclib_status_success: case tmclib_status_no_more_data: { // Attention: complete transfer but do not interrupt (do not clear usb_transfer) ! // since no more data is planned to send: abort interrupt-in transfer status = tmclib_complete_interruptin( ctx->interruptInState.bTag, false ); } /*no break*/ case tmclib_status_need_read: { // if BulkIN event succeded, it is required to // ... send data from output data. status = tmclib_status_need_read; } break; default: my_assert( status == tmclib_status_success ); // 'tmclib_interruptin_process' returns undefined code } } } } } break; default: status = tmclib_status_invalid_param; // invalid event my_assert( false ); } lastRequestStatus = status; // check the error code: if( USBTMC_ERROR(status) ) { tmclib_interruptin_error( ctx, eventCtx->interruptIn.ntx, status ); tmclib_abort_interruptin( COMPOUND_tmclib_event_context_abort_interruptin(0, true) ); status = tmclib_status_failure; } return status; } // ================================================================================= // @tmclib_bulkout_process // static eTMCLibStatus_t tmclib_bulkout_process( sUSBTMCContext_t * ctx, sUSBTransfer_t * rx ) { // Bytes left in the @rx transfer size_t nBytes = usb_count_transfer( rx ); // Bytes left to receive during current USBTMC transfer size_t nBytesLeft = ctx->bulkOutState.transferSize - ctx->bulkOutState.bytesCounter; // Alignment bytes requried to receive size_t nBytesAlignment = (sizeof(uint32_t) - ctx->bulkOutState.transferSize % sizeof(uint32_t)) % sizeof(uint32_t); // retrieve the short packet flag bool bShortPacket = usb_transfer_check_shortpacket( rx ); // bulk-out: short packet received? // Error identifier: reset int32_t nError = SCPILIB_ERROR_SUCCESS; // new transfer indicator show if a new transfer started since this packet bool bNewTransfer = ctx->bulkOutState.bNewTransfer; // Check if the last packet is a short packet (no more data is expected after the short packet) if( bShortPacket ) { // If the short packet has been received, the transfer is running out. // This is the last BulkOut event in the transfer. // Check if the read data counters are match to the expected: // UPD. Host can join DEV_DEP_MSG_OUT and REQUEST_DEV_DEP_MSG_IN, and @rx will contain // more data than the transfer declares. if( nBytes < nBytesLeft + nBytesAlignment ) { nBytes = 0; nError = SCPILIB_ERROR_BUFFER_OVERFLOW; } else { // if a short packet received then pass @nBytesLeft instead of @nBytes nBytes = nBytesLeft; // cut of alignment bytes } } else { // Calculate whole transfer size size_t bytesRequied = (sizeof( ctx->bulkOutHeader ) + nBytesLeft + nBytesAlignment); // Since USBTMC Library uses Low-Level USB transfer buffers, it is required // the USB Bulk OUT transfer to be not frangmented, so it must fit to the // transfer @rx, check it: if( usb_size_transfer( rx ) <= bytesRequied ) { // overflow (void)nBytes; nError = SCPILIB_ERROR_BUFFER_OVERFLOW; } } // check error condition if( SCPILIB_ERROR_SUCCESS == nError ) { if( bNewTransfer ) { // Since new transfer started it is required to restart SCPI-parser sSCPILibHandle.fNewTransfer(); } switch( sSCPILibHandle.fWrite( usb_transfer_raw_read( rx ), // get raw pointer and pass it to @fWrite nBytes, bShortPacket, bNewTransfer ) ) { case eScpiStatus_success: // Both either failed or success statuses means case eScpiStatus_failed: // ... that the SCPI library has been processed request. { // Need to return @tmclib_status_success to not halt // ... Bulk-OUT endpoint. // @nBytes bytes have been processed lastRequestStatus = tmclib_status_success; // request processed } break; case eScpiStatus_invalid: { // @nBytes bytes have been processed lastRequestStatus = tmclib_status_failure; // development error } break; case eScpiStatus_need_data: { if( bShortPacket ) { lastRequestStatus = tmclib_status_failure; // development error } else { // Need more data to continue, it is required to keep // ... @nBytes bytes in the buffer til the next call. nBytes = 0; // clear @nBytes to keep data in the buffer lastRequestStatus = tmclib_status_need_data; } } break; } // Retrieve bytes from the buffer if( 0 < nBytes ) { // @nBytes bytes have been processed usb_transfer_virtual_read( rx, nBytes ); nBytesLeft -= nBytes; // Check if all the protocol data has been processed (except alignment data) if( 0 == nBytesLeft ) { if( 0 < nBytesAlignment ) { usb_transfer_virtual_read( rx, nBytesAlignment ); } } } if( bShortPacket ) { // Since the short packet received the transfer must be terminated // Check if there data left in the transfer. // If it is, check if there no error occurred? if( USBTMC_NOTERROR(lastRequestStatus) && (usb_count_transfer( rx ) > 0) ) { // Yes, there some data left, and no error occurred // Maybe it is another USBTMC header? tmclib_abort_bulkout( COMPOUND_tmclib_event_context_abort_bulkout_noreset(0, true) ); // Notify the caller to call the routine again to process the rest data in the transfer lastRequestStatus = tmclib_status_again; } else { // No data or error occurred, just reset transfer tmclib_abort_bulkout( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); } } // reset 'new transfer' indicator ctx->bulkOutState.bNewTransfer = false; } else { // Error condition. // Notify the SCPI level switch( sSCPILibHandle.fError( nError, NULL ) ) { case eScpiStatus_success: lastRequestStatus = tmclib_status_success; // error processed break; case eScpiStatus_failed: // Unrecoverable error case eScpiStatus_invalid: lastRequestStatus = tmclib_status_failure; break; case eScpiStatus_need_data: // Forbidden states default: my_assert( false ); lastRequestStatus = tmclib_status_failure; } // reset transfer tmclib_abort_bulkout( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); } // 3.2 Bulk-OUT endpoint, [1] // 3.2.2.3 Bulk-OUT transfer protocol errors, [1] return ( lastRequestStatus ); } // ================================================================================= // tmclib_bulkin_process() // Processes BulkIN transfer. // // Note: User shall reset Short-Packet indicator using @usb_transfer_modify_flags // ... routine to avoid sending short packet in case no data queued into the transfer // ... after successful call (return true with empty transfer) static eTMCLibStatus_t tmclib_bulkin_process( sUSBTMCContext_t * ctx, sUSBTransfer_t * tx ) { // Bytes free in the @tx transfer size_t nBytes = usb_space_transfer( tx ); size_t nBytesOut = 0; // Due to USBTMC required to send data by portions with // ... the length multiple of 4, it is required to limit the // ... buffers size up to the maximum length multiple of 4 to // ... avoid the case when it is there no free space in the // ... buffer to add alinment bytes in the end. nBytes -= nBytes%sizeof(uint32_t); #warning SCPI freeSpace+sentCount ????? // Check the transfer boundary: @bytesCounter (sent) + @nBytes (available space) if( nBytes + ctx->bulkInState.bytesCounter > ctx->bulkInState.transferSize ) { // limit the buffer space // TransferSize does not include the number of bytes in BulkIN header or alignment bytes nBytes = ctx->bulkInState.transferSize - ctx->bulkInState.bytesCounter; } // check if current transfer ran out: // check if all the data is already queued in transfer: if( 0 == nBytes ) { // set success status without queuing any data lastRequestStatus = tmclib_status_success; // check if all the data is already send from transfer: if( 0 == usb_count_transfer( tx ) ) { // close Bulk-IN transfer due to there no more data to send // Do not clear short packet indicator (use COMPOUND_tmclib_event_context_complete_bulkin instead of COMPOUND_tmclib_event_context_abort_bulkin!) tmclib_abort_bulkin( COMPOUND_tmclib_event_context_complete_bulkin(ctx->bulkInHeader.bTag, false) ); // set informative status: equal to 'tmclib_status_success' lastRequestStatus = tmclib_status_no_more_data; } // do not queue any data nBytesOut = 0; } else { bool EOM = true; // forward set // generate SCPI-parser read-event switch( sSCPILibHandle.fRead( usb_transfer_raw_write( tx ), // get raw pointer and pass it to @fRead nBytes, &nBytesOut, &EOM ) ) #warning SCPI write more than TransferSize due to sw buffer (@tx) is larger than TransferSize { case eScpiStatus_failed: { nBytesOut = 0; // due to error: clear @nBytesOut // // SCPI library has been failed during the request processing. // Need to return @eScpiStatus_failed to halt Bulk-OUT endpoint. tmclib_abort_bulkin( COMPOUND_tmclib_event_context_abort_bulkin(ctx->bulkInHeader.bTag, false) ); // this is to unlock transfer flags only usb_transfer_clear_shortpacket( tx ); lastRequestStatus = tmclib_status_read_error; } break; case eScpiStatus_success: { // // SCPI library has been successfully processed the request. // Need to return @tmclib_status_success to not halt Bulk-OUT endpoint. // @EOM: is true if there no data is planned to transfer, it is required // ... to indicate that this message is the last one in current transfer; // @EOM: is false if it is required to generate another READ event to indicate // that this message is NOT the last one in current transfer; ctx->bulkInState.bEndOfMessage = EOM; // set End-of-message indicator if( 0 < nBytesOut ) { // @nBytesOut bytes have been placed into the output buffer ctx->bulkInState.bytesCounter += nBytesOut; #if DEBUG_USBTMC > 0 // SCPI debug only gDebugUSBTMC_TxBytes += nBytesOut; #endif // since it is the last transaction, it is required adjust // ... the ending data boundary to fit the 32-bit alignment // ... using the entire transfer bytes counter @bytesCounter: nBytesOut += sizeof(uint32_t) - ctx->bulkInState.bytesCounter%sizeof(uint32_t); } // update current transfer size: set to current transfer counter ctx->bulkInState.transferSize = ctx->bulkInState.bytesCounter; // update bulk-in header in current buffer tmclib_update_bulkin( ctx ); lastRequestStatus = tmclib_status_success; // request processed } break; case eScpiStatus_invalid: { // @nBytes bytes have been processed lastRequestStatus = tmclib_status_failure; // development error } break; case eScpiStatus_need_data: { // Need more free space to continue, it is required to // ... send @nBytesOut bytes from output buffer. ctx->bulkInState.bytesCounter += nBytesOut; #if DEBUG_USBTMC > 0 // SCPI debug only gDebugUSBTMC_TxBytes += nBytesOut; #endif // since more data is planned to transfer, it is required // ... to indicate that this message is not the last one in // ... current transfer: ctx->bulkInState.bEndOfMessage = false; // reset End-of-message indicator // update current transfer size: set to current transfer counter ctx->bulkInState.transferSize = ctx->bulkInState.bytesCounter; // update bulk-in header in current buffer tmclib_update_bulkin( ctx ); // set status lastRequestStatus = tmclib_status_need_read; } break; } } // perform virtual write (the data is already in the buffer) usb_transfer_virtual_write( tx, nBytesOut ); // reset 'new transfer' indicator ctx->bulkInState.bNewTransfer = false; return lastRequestStatus; } // ================================================================================= // tmclib_interruptin_process() // Processes InterruptIN transfer. // static eTMCLibStatus_t tmclib_interruptin_process( sUSBTMCContext_t * ctx, sUSBTransfer_t * ntx ) { // Bytes free in the @tx transfer size_t nBytes = usb_space_transfer( ntx ); size_t nBytesOut = 0; // check if current transfer ran out: // check if all the data is already queued in transfer: if( 0 == nBytes ) { // set success status without queuing any data lastRequestStatus = tmclib_status_success; // check if all the data is already send from transfer: if( 0 == usb_count_transfer( ntx ) ) { // close Bulk-IN transfer due to there no more data to send tmclib_abort_interruptin( COMPOUND_tmclib_event_context_abort_interruptin(ctx->interruptInState.bTag, false) ); // set informative status: equal to 'tmclib_status_success' lastRequestStatus = tmclib_status_no_more_data; } // do not queue any data nBytesOut = 0; } else { // generate SCPI-parser read-event switch( sSCPILibHandle.fNotificationRead( usb_transfer_raw_write( ntx ), // get raw pointer and pass it to @fRead nBytes, &nBytesOut, &ctx->interruptInState.STB ) ) { case eScpiStatus_failed: { nBytesOut = 0; // due to error: clear @nBytesOut // SCPI library has been failed during the request processing. // Need to return @eScpiStatus_failed to halt Interrupt-IN endpoint. tmclib_abort_interruptin( COMPOUND_tmclib_event_context_abort_interruptin(ctx->interruptInState.bTag, false) ); // this is to unlock transfer flags only usb_transfer_clear_shortpacket( ntx ); lastRequestStatus = tmclib_status_read_error; } break; case eScpiStatus_success: { // // SCPI library has been successfully processed the request. // Need to return @tmclib_status_success to not halt endpoint. // since no data is planned to transfer, it is required // ... to indicate that this message is the last one in // ... current transfer: ctx->interruptInState.bEndOfMessage = true; // set End-of-message indicator if( 0 < nBytesOut ) { // @nBytesOut bytes have been placed into the output buffer ctx->interruptInState.bytesCounter += nBytesOut; } // update bulk-in header in current buffer tmclib_update_notification( ctx ); lastRequestStatus = tmclib_status_success; // request processed } break; case eScpiStatus_invalid: { // @nBytes bytes have been processed lastRequestStatus = tmclib_status_failure; // development error } break; case eScpiStatus_need_data: { // Need more free space to continue, it is required to // ... send @nBytesOut bytes from output buffer. ctx->interruptInState.bytesCounter += nBytesOut; // since more data is planned to transfer, it is required // ... to indicate that this message is not the last one in // ... current transfer: ctx->interruptInState.bEndOfMessage = false; // reset End-of-message indicator // update bulk-in header in current buffer tmclib_update_notification( ctx ); // set status lastRequestStatus = tmclib_status_need_read; } break; } } // perform virtual write (the data is already in the buffer) usb_transfer_virtual_write( ntx, nBytesOut ); // reset 'new transfer' indicator ctx->interruptInState.bNewTransfer = false; return lastRequestStatus; } // ================================================================================= // @tmclib_device_clear // Resets the USBTMC and SCPI state // @eventCtx - event context (sTMCDeviceClearEventCtx_t) // @eventCtx->event - event id to process: // * eTMCEventDeviceClear - resets the USBTMC and SCPI state // @eventCtx->clear.resetTransport - reset all the transfers // // Returns: the operation status: // tmclib_status_success in case of success, // tmclib_status_failure otherwise // static eTMCLibStatus_t tmclib_device_clear( sTMCEventContext_t * eventCtx ) { if( eventCtx->clear.resetTransport ) { eventCtx->abort.transfer_bTag = 0; eventCtx->abort.clearShortPacketIndicator = true; eventCtx->abort.force = true; eventCtx->abort.noResetTransfer = false; tmclib_abort_bulkin( eventCtx ); tmclib_abort_bulkout( eventCtx ); tmclib_abort_interruptin( eventCtx ); } // Reset SCPI Parser and FSM state if( eScpiStatus_success == sSCPILibHandle.fReset() ) { return tmclib_status_success; } return tmclib_status_failure; } // ================================================================================= static void tmclib_bulkout_error( sUSBTMCContext_t * ctx, sUSBTransfer_t * rx, eTMCLibStatus_t status ) { } // ================================================================================= static void tmclib_bulkin_error( sUSBTMCContext_t * ctx, sUSBTransfer_t * tx, eTMCLibStatus_t status ) { } // ================================================================================= static void tmclib_interruptin_error( sUSBTMCContext_t * ctx, sUSBTransfer_t * tx, eTMCLibStatus_t status ) { } // ================================================================================= // @tmclib_generic_event() // Processes USBTMC specific event. // @eventCtx - event context, all the fields must be filled in accordance to @eventCtx->event code // @eventCtx->event - event identifier: // // +------------------------------------------------------------------------------------------------------------ // * eTMCEventBulkInStop - Aborts currently running BulkIN transfer // |---> @eventCtx->abort: // | - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.4 [1] // | - @force - makes to ignore the @transfer_bTag if true // +------------------------------------------------------------------------------------------------------------ // * eTMCEventBulkOutStop - Aborts currently running BulkOUT transfer // |---> @eventCtx->abort: // | - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.2.1.2 [1] // | - @force - makes to ignore the @transfer_bTag if true // +------------------------------------------------------------------------------------------------------------ // * eTMCEventInterruptInStop - Aborts currently running InterruptIN transfer // |---> @eventCtx->abort: // | - @transfer_bTag - The bTag value associated with the transfer to abort, see 4.3.1 [2] // | - @force - makes to ignore the @transfer_bTag if true // +------------------------------------------------------------------------------------------------------------ // * eTMCEventBulkIn - Processes USBTMC Bulk-IN event // |---> @eventCtx->bulkIn: // | - @tx - the buffer object to be used to transmit data // +------------------------------------------------------------------------------------------------------------ // * eTMCEventBulkOut - Processes USBTMC Bulk-OUT event // |---> @eventCtx->bulkOut: // | - @rx - the buffer object to be used to receive data // +------------------------------------------------------------------------------------------------------------ // * eTMCEventInterruptInStart - Initialize new Interrupt-IN transfer w/o data processing // | ...The function only creates new transfer without calling Tx-Handler. This call must be // | ...provided before 'eTMCEventInterruptIn' event to prapare transfer. // |----> @eventCtx->interruptIn: // | - @ntx - must be NULL, or 'tmclib_status_invalid_param' will be returned otherwise // | - @bTag - the Tag identifier, [2], 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request, // +------------------------------------------------------------------------------------------------------------ // * eTMCEventInterruptIn - Processes USBTMC Interrupt-IN event. // | Note that 'eTMCEventInterruptInStart' must be generated before 'eTMCEventInterruptIn' for normal operation. // | Note: Each event generates a single INTERRUPT-IN packet begining with Interrupt-IN header // | Note: Each interrupt-in event must be not longer than @ntx transfer length. // | Note: In case it is required to send more data than @ntx can carry, you need: // | - increase Interrupt-IN EP buffer (see USB specification) and increase @ntx transfer space // | or: // | - send the data using multiple packets (each packet begins with INTERRUPT-IN header), pay // | attention that @ctx->interruptInState will not be reset until user layer return tmclib_status_success // | or tmclib_status_no_more_data. // |----> @eventCtx->interruptIn: // | - @ntx - the buffer object to be used to transmit data // | - @bTag - don't care // +------------------------------------------------------------------------------------------------------------ // // Returns: the operation status: // tmclib_status_success in case of success, // tmclib_status_failure otherwise // eTMCLibStatus_t tmclib_generic_event( sTMCEventContext_t * eventCtx ) { switch( eventCtx->event ) { case eTMCEventInterruptInStart: // InterruptIn initialize (only for Interrupt-IN) return tmclib_interruptin_event( eventCtx ); case eTMCEventBulkIn: // BulkIn event return tmclib_bulkin_event( eventCtx ); case eTMCEventBulkOut: // BulkOut event return tmclib_bulkout_event( eventCtx ); case eTMCEventInterruptIn: // InterruptIn event return tmclib_interruptin_event( eventCtx ); case eTMCEventBulkInStop: // BulkIn abort return tmclib_abort_bulkin( eventCtx ); case eTMCEventBulkOutStop: // BulkOut abort return tmclib_abort_bulkout( eventCtx ); case eTMCEventInterruptInStop: // InterruptIn abort return tmclib_abort_interruptin( eventCtx ); case eTMCEventDeviceClear: // Device Clear return tmclib_device_clear( eventCtx ); } return tmclib_status_invalid_param; }