#define USB_APPLICATION_USBTMC_C #include "usb/usb_config.h" #include "usb/usb_bridge.h" #include "usbd_usbtmc.h" #include "usbapp/usb_application_usbtmc.h" #include "usbapp/usbtmclib/usbtmclib_basic.h" #include "app/led/led.h" #include "my_assert.h" //---------------------------------------------------------------- // Refer to: // [1] USBTMC Standard, rev. 1.0, 14/04/2003 // "Universal Serial Bus Test and Measurement Class Specification (USBTMC)" // [2] USBTMC-USB488 Standard, rev. 1.0, 14/04/2003 // "Universal Serial Bus Test and Measurement Class, Subclass USB488 Specification (USBTMC-USB488) // [3] IEEE 488.1 Standard, ANSI/IEEE Std 488.1-1987 // "IEEE Standard Digital Interface, ANSI/IEEE Std 488.1-1987" // [4] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE // Standard Digital Interface for Programmable Instrumentation" // [5] SCPI Specification, revision 1999.0 // "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999" //---------------------------------------------------------------- #if DEBUG_USBTMC_REQUESTS > 0 #define MAX_DEBUG_LOG 10 volatile int gDebugLogIdx = 0; volatile int gDebugLogRollover = 0; static uint32_t gDebugLog[ MAX_DEBUG_LOG ] = {0}; void debug_log( uint8_t value, uint16_t value2 ) { if( gDebugLogIdx == 0 ) { memset( gDebugLog, 0, sizeof(gDebugLog) ); } if( gDebugLogIdx >= MAX_DEBUG_LOG ) { memset( gDebugLog, 0, sizeof(gDebugLog) ); gDebugLogIdx = 0; gDebugLogRollover++; } gDebugLog[ gDebugLogIdx++ ] = (((uint32_t)value2) << 16) | value; } #define DEBUG_LOG_STATE_INITABIN 0x01 #define DEBUG_LOG_STATE_CHKSTATUS_PEND 0x02 #define DEBUG_LOG_STATE_CHKSTATUS_OK 0x03 #define DEBUG_LOG_STATE_DATAIN 0x04 #endif //---------------------------------------------------------------- #define INDICATOR_PULSE_REQUEST_SUPPORT (CONFIG_LEDS) // enable INDICATOR_PULSE if CONFIG_LEDS is available //---------------------------------------------------------------- static int8_t fUSBTMCProto_Init(); static int8_t fUSBTMCProto_DeInit(); static void fUSBTMCProto_Reset(); static bool fUSBTMCProto_Setup( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success ); static size_t fUSBTMCProto_ControlRx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining ); static size_t fUSBTMCProto_ControlTx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining ); const sUSBAppEntry_Control_t usbapplication_USBTMC_control = { .fUsbInit = fUSBTMCProto_Init, .fUsbDeInit = fUSBTMCProto_DeInit, .fUsbSetup = fUSBTMCProto_Setup, .fUsbCtlEpRx = fUSBTMCProto_ControlRx, .fUsbCtlEpTx = fUSBTMCProto_ControlTx, .fResetEvent = fUSBTMCProto_Reset, }; // USBTMC_datain_beginsend() // Begin the transmission using DataIN handler virtual call. // Due to the hardware does not support NAK-sent-interrupt, it is // required to initiate the first packet sending to start TX-flow (DataIN). // This function issues the first packet sending using virtual DataIN event. uint8_t USBTMC_datain_beginsend( uint8_t epnum ); // USBTMC_datain_sendprepared() // Begin the transmission using already prepared TX-transfer. // Due to the hardware does not support NAK-sent-interrupt, it is // required to initiate the first packet sending to start TX-flow (DataIN). // This function issues the first packet sending using virtual DataIN event. uint8_t USBTMC_datain_sendprepared( uint8_t epnum ); // USBTMC_datain_zerosend() // Queue zero packet in BulkIN EP without calling TX-handler uint8_t USBTMC_datain_zerosend( uint8_t epnum ); static bool fUSBTMCProto_DataRxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * rx ); static bool fUSBTMCProto_DataTxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * tx ); static bool fUSBTMCProto_DataErrHandler( uint8_t bEpLogAddress, uint32_t error ); static bool fUSBTMCProto_IntTxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * tx ); static bool fUSBTMCProto_IntErrHandler( uint8_t bEpLogAddress, uint32_t error ); // @usbapplication_USBTMC_dataapp // Data channel handler descriptor const sUSBAppEntry_Data_t usbapplication_USBTMC_dataapp = { .fDataInitHandler = NULL, .fDataRxHandler = fUSBTMCProto_DataRxHandler, .fDataTxHandler = fUSBTMCProto_DataTxHandler, .fDataErrHandler = fUSBTMCProto_DataErrHandler, .fDataDeInitHandler = NULL, }; // @usbapplication_USBTMC_intapp // Interrupt channel handler descriptor const sUSBAppEntry_Data_t usbapplication_USBTMC_intapp = { .fDataInitHandler = NULL, .fDataRxHandler = NULL, // no data receiving is expected .fDataTxHandler = fUSBTMCProto_IntTxHandler, .fDataErrHandler = fUSBTMCProto_IntErrHandler, .fDataDeInitHandler = NULL, }; //=========================================================================================== #if INDICATOR_PULSE_REQUEST_SUPPORT // 4.2.1.8 GET_CAPABILITIES, table 37, [1] // Refer to @sUSBTMC_GetCapabilities_Response_t #define USBTMC_IFACE_CAPABILITIES (USBTMC_IFACE_CAPABILITIES_INDICATOR_PULSE) // Indicator Pulse request is supported :) #else // 4.2.1.8 GET_CAPABILITIES, table 37, [1] // Refer to @sUSBTMC_GetCapabilities_Response_t #define USBTMC_IFACE_CAPABILITIES (0) // No Indicator Pulse request supported :( #endif // 4.2.1.8 GET_CAPABILITIES, table 37, [1] // Refer to @sUSBTMC_GetCapabilities_Response_t #define USBTMC_DEVICE_CAPABILITIES (0) // 4.2.2 GET_CAPABILITIES, table 8, [2] // Refer to @sUSB488_GetCapabilities_Response_t #define USB488_IFACE_CAPABILITIES (USB488_IFACE_CAPABILITIES_4882IF) // 4.2.2 GET_CAPABILITIES, table 8, [2] // Refer to @sUSB488_GetCapabilities_Response_t #define USB488_DEVICE_CAPABILITIES (USB488_DEVICE_CAPABILITIES_SERVICE_REQUEST | USB488_DEVICE_CAPABILITIES_SCPI) // =================================================================================== // @sUsbTmcContext // Local module context variables static struct { //sUSBTransfer_t * xBulkOutTransfer; // data buffer for Bulk-Out transfer //sUSBTransfer_t * xBulkInTransfer; // data buffer for Bulk-In transfer const USBD_DescriptorsTypeDef * xDescHandlers; // class GetDescriptor handlers set bool bInitiateBulkOutAbort; // Initiate BulkOut abort request flag: true if received, false otherwise bool bInitiateBulkInAbort; // Initiate BulkIn abort request flag: true if received, false otherwise bool bInitiateClear; // Initiate Clear request flag: true if received, false otherwise struct { union { uint8_t rawBytesResponse[1]; // byte-array access to avoid casting and warnings sUSBTMC_InitiateAbortBulkInOut_Response_t responseInitiateAbortBulkX; sUSBTMC_CheckAbortBulkOut_Response_t responseCheckAbortBulkOut; sUSBTMC_CheckAbortBulkIn_Response_t responseCheckAbortBulkIn; sUSBTMC_InitiateClear_Response_t responseInitiateClear; sUSBTMC_CheckClear_Response_t responseCheckClear; sUSBTMC_GetCapabilities_Response_t responseGetCapabilities; sUSBTMC_IndicatorPulse_Response_t responseIndicatorPulse; sUSBTMC_USB488_ReadStatusByte_Response_t responseUsb488ReadStatusByte; }; } tmpSetup; // temporary variables are valid during single request: Setup -> Rx/Tx } sUsbTmcContext; // =================================================================================== static int8_t fUSBTMCProto_Init() { // Check the Control-OUT buffer size // If the buffer size is less than the maximum packet size to be sent: if( usbd_usbtmc_get_control_tx_transfer_size() < sizeof( sUsbTmcContext.tmpSetup ) ) { // Error: the buffer size is less than the maximum packet size to be sent. // See @fUSBTMCProto_ControlTx for details. // The function @fUSBTMCProto_ControlTx does not support multi-packet mode return -1; // error: } // Register Bulk-Data handler -> @usbapplication_USBTMC_dataapp usb_bridge_register_dataapp( USBTMC_BULKIO_EP, &usbapplication_USBTMC_dataapp ); // Register Interrupt-Data handler -> @usbapplication_USBTMC_intapp usb_bridge_register_dataapp( USBTMC_INTIO_EP, &usbapplication_USBTMC_intapp ); sUsbTmcContext.bInitiateBulkOutAbort = false; sUsbTmcContext.bInitiateBulkInAbort = false; sUsbTmcContext.bInitiateClear = false; //sUsbTmcContext.xBulkOutTransfer = ; //sUsbTmcContext.xBulkInTransfer = USBD_USBTMC_GetDataTxTransferHandle(); sUsbTmcContext.xDescHandlers = USBD_USBTMC_GetDescriptorHandlers(); if( tmclib_status_success != tmclib_init( USBD_USBTMC_GetDataRxTransferHandle(), USBD_USBTMC_GetDataTxTransferHandle(), USBD_USBTMC_GetInterruptTxTransferHandle() ) ) { return -1; // error } return 0; } // =================================================================================== static int8_t fUSBTMCProto_DeInit() { usb_bridge_unregister_dataapp( USBTMC_BULKIO_EP ); usb_bridge_unregister_dataapp( USBTMC_INTIO_EP ); tmclib_deinit(); return 0; } // =================================================================================== // @fUSBTMCProto_Reset // USB Bus reset event static void fUSBTMCProto_Reset() { 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) ); sUsbTmcContext.bInitiateBulkOutAbort = false; sUsbTmcContext.bInitiateBulkInAbort = false; sUsbTmcContext.bInitiateClear = false; } // =================================================================================== // @fUSBTMCProto_Setup // USBTMC protocol, USB Setup Handler // Processes the class requests and reacts to some standard requests static bool fUSBTMCProto_Setup( const tUSBSetupPacket_t * pSetup, bool bFirstStage, bool success ) { // Check the request type switch( pSetup->bmRequest & USB_REQ_TYPE_MASK ) { // Standard request: this call is performed BEFORE the ENDPOINT clearing by core case USB_REQ_TYPE_STANDARD: { // Check request ID: switch( pSetup->bRequest ) { // USBTMC must process CLEAR_FEATURE standard request with type ENDPOINT_HALT: // see section 4.1 "Standard requests", [1] case USB_REQ_CLEAR_FEATURE: { // process ENDPOINT_HALT request type: if( USB_FEATURE_EP_HALT == pSetup->wValue ) { // Host tries to restore synchronization: clear stalled endpoint // check the endpoint address: switch( pSetup->wIndex ) { case USBTMC_BULKOUT_EP: // section 4.1.1.1 "USBTMC interface Bulk-OUT endpoints", [1] tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); // forced interrupt current transfer // Clear EP and reset the buffer, restart receiving USBD_USBTMC_ClearEP( USBTMC_BULKOUT_EP ); return true; // pass this request, core does the job break; case USBTMC_BULKIN_EP: // section 4.1.1.2 "USBTMC interface Bulk-IN endpoints", [1] tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkin(0, true) ); // forced interrupt current transfer // Clear EP and reset the buffer USBD_USBTMC_NakEP( USBTMC_BULKIN_EP ); // set NAK instead of VALID return true; // pass this request, core does the job break; case USBTMC_INTERRUPTIN_EP: // [1] has no information about such event // Do processing in formal way: abort the transfer and set NAK state over EP tmclib_generic_event( COMPOUND_tmclib_event_context_abort_interruptin(0, true) ); // forced interrupt current transfer // Clear EP and reset the buffer USBD_USBTMC_NakEP( USBTMC_INTERRUPTIN_EP ); // set NAK instead of VALID return true; // pass this request, core does the job break; } } } break; } } break; // see 4.2.1 "USBTMC requests", [1] case USB_REQ_TYPE_CLASS: { // In accordance with USBTMC standard [1], the code "STATUS_SUCCESS" means // the device queued the INITIATE request to be performed. // @bInitiateBulkInAbort must remember if such request is queued in case // the host will try to queue another request. If host performs another // request, the device must discard the response for previously queued request // and queue new request only if the previous request is already done. // But if the device executes this request synchroniusly, there no previously // request pending, and the request is always done. // In other words, @bInitiateBulkInAbort shows only if is there the response // prepared for previously INITIATE request or not for next CHECK_STATUS request. // 4.2.1.1 "USBTMC split transactions", point 3, [1] if( sUsbTmcContext.bInitiateBulkOutAbort && (eUSBTMC_INITIATE_ABORT_BULK_OUT == pSetup->bRequest || eUSBTMC_INITIATE_CLEAR == pSetup->bRequest ) ) { // reset the prepared response: comes down to reseting the flag sUsbTmcContext.bInitiateBulkOutAbort = false; } // --- // 4.2.1.1 "USBTMC split transactions", point 3, [1] if( sUsbTmcContext.bInitiateBulkInAbort && (eUSBTMC_INITIATE_ABORT_BULK_IN == pSetup->bRequest || eUSBTMC_INITIATE_CLEAR == pSetup->bRequest ) ) { // reset the prepared response: comes down to reseting the flag sUsbTmcContext.bInitiateBulkInAbort = false; } // --- // 4.2.1.1 "USBTMC split transactions", point 3, [1] if( sUsbTmcContext.bInitiateClear && (eUSBTMC_CHECK_CLEAR_STATUS != pSetup->bRequest) ) { // reset the prepared response: comes down to reseting the flag sUsbTmcContext.bInitiateClear = false; } // --- // Check request ID: switch( pSetup->bRequest ) { // Aborts a Bulk-OUT transfer. // see 4.2.1.2 "INITIATE_ABORT_BULK_OUT", [1] case eUSBTMC_INITIATE_ABORT_BULK_OUT: { if( pSetup->wIndex != USBTMC_BULKOUT_EP ) return false; // Error, 4.2.1.2 INITIATE_ABORT_BULK_OUT, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX) ); // preserve the bTag to put the value into the response (fUSBTMCProto_ControlRx) tmclib_bulkout_gettag_latest( &sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.bTag ); // try to abort BulkOut transfer with the specified bTag if( tmclib_status_success == tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkout((uint8_t)pSetup->wValue, false) ) ) { // Halt the BulkOut endpoint to interrupt the transfer, 4.2.1.2, table 18 [1] USBD_USBTMC_StallEP( USBTMC_BULKOUT_EP ); sUsbTmcContext.bInitiateBulkOutAbort = true; // INITIATE response prepared sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.USBTMC_status = eUSBTMC_STATUS_SUCCESS; } else { // error: transfer isn't in progress sUsbTmcContext.bInitiateBulkOutAbort = false; // INITIATE response not prepared sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.USBTMC_status = eUSBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS; } return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Returns the status of the previously sent INITIATE_ABORT_BULK_OUT request. case eUSBTMC_CHECK_ABORT_BULK_OUT_STATUS: { if( pSetup->wIndex != USBTMC_BULKOUT_EP ) return false; // Error, 4.2.1.3 CHECK_ABORT_BULK_OUT_STATUS, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut) ); if( sUsbTmcContext.bInitiateBulkOutAbort ) { uint32_t NBYTES_RXD = 0; // retireve number of received bytes during last transfer tmclib_bulkout_getcounter( &NBYTES_RXD ); sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut.NBYTES_RXD = NBYTES_RXD; sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfully aborted // clear the request indicator sUsbTmcContext.bInitiateBulkOutAbort = false; } else { sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut.USBTMC_status = eUSBTMC_STATUS_SPLIT_NOT_IN_PROGRESS; // not INITIATE request received } return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Aborts a Bulk-IN transfer. case eUSBTMC_INITIATE_ABORT_BULK_IN: { if( pSetup->wIndex != USBTMC_BULKIN_EP ) return false; // Error, 4.2.1.4 INITIATE_ABORT_BULK_IN, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX) ); // preserve the bTag to put the value into the response (fUSBTMCProto_ControlTx) tmclib_bulkin_gettag_latest( &sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.bTag ); // try to abort BulkIn transfer with the specified bTag if( tmclib_status_success == tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkin((uint8_t)pSetup->wValue, false) ) ) { // Restore endpoint, BulkIN EP must not be halted, due to: // - 4.2.1.4, table 26; // - 3.3.2.4 "Halt", table 12 USBD_USBTMC_NakEP( USBTMC_BULKIN_EP ); // [1], 4.2.1.4 INITIATE_ABORT_BULK_IN, Table 26 // "If a short packet has not been queued, queue a shortpacket to terminate the transfer." // "If a short packet can not yet be queued, wait until a short packet can be queued." USBTMC_datain_zerosend( USBTMC_BULKIN_EP ); // queue a short packet to terminate transfer #if DEBUG_USBTMC_REQUESTS > 0 debug_log( DEBUG_LOG_STATE_INITABIN, 0 ); #endif sUsbTmcContext.bInitiateBulkInAbort = true; // INITIATE response prepared sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.USBTMC_status = eUSBTMC_STATUS_SUCCESS; } else { // error: transfer isn't in progress sUsbTmcContext.bInitiateBulkInAbort = false; // INITIATE response not prepared sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX.USBTMC_status = eUSBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS; } return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Returns the status of the previously sent INITIATE_ABORT_BULK_IN request. case eUSBTMC_CHECK_ABORT_BULK_IN_STATUS: { if( pSetup->wIndex != USBTMC_BULKIN_EP ) return false; // Error, 4.2.1.5 CHECK_ABORT_BULK_IN_STATUS, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn) ); if( sUsbTmcContext.bInitiateBulkInAbort ) { uint32_t NBYTES_TXD = 0; // retireve number of transferred bytes during last transfer tmclib_bulkin_getcounter( &NBYTES_TXD ); if( ! USBD_USBTMC_GetTxQueuedSize( USBTMC_BULKIN_EP, NULL ) ) { // [1]. 4.2.1.5 CHECK_ABORT_BULK_IN_STATUS, Table 29 -- CHECK_ABORT_BULK_IN_STATUS USBTMC_status values // The device must set NBYTES_TXD to the appropriate value. sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.NBYTES_TXD = NBYTES_TXD; // BulkIN transfer already reset, FIFO is empty, so bmAbortBulkIn.D0 is zero. sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.bmAbortBulkIn = 0; sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfully aborted #if DEBUG_USBTMC_REQUESTS > 0 debug_log( DEBUG_LOG_STATE_CHKSTATUS_OK, 0 ); #endif // clear the request status sUsbTmcContext.bInitiateBulkInAbort = false; } else { // [1]. 4.2.1.5 CHECK_ABORT_BULK_IN_STATUS, Table 29 -- CHECK_ABORT_BULK_IN_STATUS USBTMC_status values // The device must set NBYTES_TXD = 0x00000000. sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.NBYTES_TXD = 0; // BulkIN transfer already reset, but FIFO is not empty and short packet must be send, so bmAbortBulkIn.D0 is set. sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.bmAbortBulkIn = 1; sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.USBTMC_status = eUSBTMC_STATUS_PENDING; // successfully aborted #if DEBUG_USBTMC_REQUESTS > 0 debug_log( DEBUG_LOG_STATE_CHKSTATUS_PEND, 0 ); #endif // keep the request status true sUsbTmcContext.bInitiateBulkInAbort = true; } } else { sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn.USBTMC_status = eUSBTMC_STATUS_SPLIT_NOT_IN_PROGRESS; // not INITIATE request received } return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Clears all previously sent pending and // unprocessed Bulk-OUT USBTMC message // content and clears all pending Bulk-IN transfers // from the USBTMC interface. case eUSBTMC_INITIATE_CLEAR: { //#error sUsbTmcContext.xDescHandler if( pSetup->wIndex != USB_TMC_INTERFACE_VALUE ) return false; // Error, see 4.2.1.6 INITIATE_CLEAR, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseInitiateClear) ); // 4.2.1.6 INITIATE_CLEAR, [2] // Halt Bulk-OUT EP and clear FIFO USBD_USBTMC_StallEP( USBTMC_BULKOUT_EP ); // Abort bulk-out transfer and clear the state tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkout(0, true) ); // 4.2.1.6 INITIATE_CLEAR, [2] // Remove all queued packets and clear FIFO for BulkIn // Set NAK status to interrupt the transfer and clear buffer USBD_USBTMC_NakEP( USBTMC_BULKIN_EP ); // Abort bulk-in transfer and clear the state tmclib_generic_event( COMPOUND_tmclib_event_context_abort_bulkin(0, true) ); // --------------- Non-standard ----------------------------------------------------- // There no recomendations for Interrupt-IN EP during INITIATE_CLEAR request // But it is good to restore the initial state to clear Interrupt-IN EP here. // Set NAK state for Interrupt-IN EP USBD_USBTMC_NakEP( USBTMC_INTERRUPTIN_EP ); // abort interrupt-in transfer tmclib_generic_event( COMPOUND_tmclib_event_context_abort_interruptin(0, true) ); // ---------------------------------------------------------------------------------- // Clean up USBTMC and SCPI module tmclib_generic_event( COMPOUND_tmclib_event_context_device_clear(false) ); sUsbTmcContext.tmpSetup.responseInitiateClear.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfully cleared sUsbTmcContext.bInitiateClear = true; #if DEBUG_USB > 0 // SCPI debug only extern void spy_tx_log_cleanup(); spy_tx_log_cleanup(); #endif return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Returns the status of the previously sent INITIATE_CLEAR request. case eUSBTMC_CHECK_CLEAR_STATUS: { if( pSetup->wIndex != USB_TMC_INTERFACE_VALUE ) return false; // Error, see 4.2.1.7 CHECK_CLEAR_STATUS, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseCheckClear) ); // [1], 4.2.1.7 CHECK_CLEAR_STATUS if( sUsbTmcContext.bInitiateClear ) { sUsbTmcContext.tmpSetup.responseCheckClear.bmClear = 0; // all FIFOs are cleared, bmClear.D0 is zero sUsbTmcContext.tmpSetup.responseCheckClear.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfully aborted // clear the request indicator sUsbTmcContext.bInitiateClear = false; } else { sUsbTmcContext.tmpSetup.responseCheckClear.USBTMC_status = eUSBTMC_STATUS_SPLIT_NOT_IN_PROGRESS; // not INITIATE request received } return true; // pass this request -> fUSBTMCProto_ControlTx } break; // Returns attributes and capabilities of the USBTMC interface. case eUSBTMC_GET_CAPABILITIES: { if( pSetup->wIndex != USB_TMC_INTERFACE_VALUE ) return false; // Error, see 4.2.1.8 GET_CAPABILITIES, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseGetCapabilities) ); sUsbTmcContext.tmpSetup.responseGetCapabilities.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfull sUsbTmcContext.tmpSetup.responseGetCapabilities.bcdUSBTMC = USBTMC_VERSION; sUsbTmcContext.tmpSetup.responseGetCapabilities.ifaceCaps = USBTMC_IFACE_CAPABILITIES; sUsbTmcContext.tmpSetup.responseGetCapabilities.devCaps = USBTMC_DEVICE_CAPABILITIES; sUsbTmcContext.tmpSetup.responseGetCapabilities.usb488.bcdUSB488 = USB488_VERSION; sUsbTmcContext.tmpSetup.responseGetCapabilities.usb488.ifaceCaps = USB488_IFACE_CAPABILITIES; sUsbTmcContext.tmpSetup.responseGetCapabilities.usb488.devCaps = USB488_DEVICE_CAPABILITIES; return true; // pass this request -> fUSBTMCProto_ControlTx } break; // A mechanism to turn on an activity indicator for // identification purposes. The device indicates // whether or not it supports this request in the // GET_CAPABILITIES response packet. case eUSBTMC_INDICATOR_PULSE: { #if INDICATOR_PULSE_REQUEST_SUPPORT if( pSetup->wIndex != USB_TMC_INTERFACE_VALUE ) return false; // Error, see 4.2.1.9 INDICATOR_PULSE, [1] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseIndicatorPulse) ); sUsbTmcContext.tmpSetup.responseIndicatorPulse.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfull // Perform LED Signaling LEDHandle.SetMode( eLedMode_Signaling ); return true; // pass this request -> fUSBTMCProto_ControlTx #else return false; // Error, see 4.2.1.9 INDICATOR_PULSE, [1] #endif } break; #warning SCPI Add "default:" and route to USB488 dependent request processor (4.3 USB488 subclass specific requests) // Allows to read the status byte using class request case eUSBTMC_USB488_READ_STATUS_BYTE: { if( pSetup->wIndex != USB_TMC_INTERFACE_VALUE ) return false; // Error, see 4.3.1 READ_STATUS_BYTE, [2] if( pSetup->wValue & 0x80 || (pSetup->wValue & 0x7F) < 2 ) return false; // Error, see 4.3.1 READ_STATUS_BYTE, [2] // zero init the response bytes memset( sUsbTmcContext.tmpSetup.rawBytesResponse, 0, sizeof(sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte) ); sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte.USBTMC_status = eUSBTMC_STATUS_SUCCESS; // successfull sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte.bTag = pSetup->wValue & 0x7F; // 4.3.1.2 Response format for USB488 interfaces with an Interrupt-IN endpoint, [2] sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte.bStatusByte = 0; // forward set to constant // Set NAK state for Interrupt-IN EP USBD_USBTMC_NakEP( USBTMC_BULKIN_EP ); // Initiate Interrupt-In transfer tmclib_generic_event( COMPOUND_tmclib_event_context_interruptin_start( eventCtx, NULL, sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte.bTag ) ); return true; // pass this request -> fUSBTMCProto_ControlTx } break; } } break; } return false; } // =================================================================================== // fUSBTMCProto_ControlRx() // The setup packet has been processed and filtered already in @fUSBTMCProto_Setup() // This function receives the SETUP packet DATA payload static size_t fUSBTMCProto_ControlRx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * rx, size_t idx, size_t bytesRemaining ) { // No incoming packets expected via CONTROL-transfer return 0; } // =================================================================================== // fUSBTMCProto_ControlTx() // The setup packet has been processed and filtered already in @fUSBTMCProto_Setup() // This function queues the response only. static size_t fUSBTMCProto_ControlTx( const tUSBSetupPacket_t * pSetup, sUSBTransfer_t * tx, size_t idx, size_t bytesRemaining ) { // All the followed requests require the response. // To simplify the implementation this function requires // the control endpoint MAX_PACKET_SIZE to be not less than // the higest incoming packet. // This function does not support multi-packet mode, the buffer size // must have enough space to place the biggest possible response. // see @fUSBTMCProto_Init for details. size_t bytes = 0; if( 0 != idx ) return 0; // multi-packet transaction: wtf? see the comment above // Check the request type switch( pSetup->bmRequest & USB_REQ_TYPE_MASK ) { // see 4.2.1 "USBTMC requests", [1] case USB_REQ_TYPE_CLASS: { // Check request ID: switch( pSetup->bRequest ) { case eUSBTMC_INITIATE_ABORT_BULK_OUT: // Aborts a Bulk-OUT transfer. case eUSBTMC_INITIATE_ABORT_BULK_IN: // Aborts a Bulk-IN transfer. { if( sizeof(sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX) == bytesRemaining ) // 4.2.1.2, Table 19, [1] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseInitiateAbortBulkX) ); } } break; // Returns the status of the previously sent INITIATE_ABORT_BULK_OUT request. case eUSBTMC_CHECK_ABORT_BULK_OUT_STATUS: { if( sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut) == bytesRemaining ) // 4.2.1.3, Table 22, [1] { bytes += usb_write_transfer( tx, &sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkOut) ); } } break; // Returns the status of the previously sent INITIATE_ABORT_BULK_IN request. case eUSBTMC_CHECK_ABORT_BULK_IN_STATUS: { if( sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn) == bytesRemaining ) // 4.2.1.5, Table 28, [1] { bytes += usb_write_transfer( tx, &sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseCheckAbortBulkIn) ); } } break; // Clears all previously sent pending and // unprocessed Bulk-OUT USBTMC message // content and clears all pending Bulk-IN transfers // from the USBTMC interface. case eUSBTMC_INITIATE_CLEAR: { if( sizeof(sUsbTmcContext.tmpSetup.responseInitiateClear) == bytesRemaining ) // 4.2.1.6, Table 30, [1] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseInitiateClear) ); } } break; // Returns the status of the previously sent INITIATE_CLEAR request. case eUSBTMC_CHECK_CLEAR_STATUS: { if( sizeof(sUsbTmcContext.tmpSetup.responseCheckClear) == bytesRemaining ) // 4.2.1.7, Table 34, [1] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseCheckClear) ); } } break; // Returns attributes and capabilities of the USBTMC interface. case eUSBTMC_GET_CAPABILITIES: { if( sizeof(sUsbTmcContext.tmpSetup.responseGetCapabilities) == bytesRemaining ) // 4.2.1.8 GET_CAPABILITIES, table 37, [1] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseGetCapabilities) ); } } break; // A mechanism to turn on an activity indicator for // identification purposes. The device indicates // whether or not it supports this request in the // GET_CAPABILITIES response packet. case eUSBTMC_INDICATOR_PULSE: { if( sizeof(sUsbTmcContext.tmpSetup.responseIndicatorPulse) == bytesRemaining ) // 4.2.1.9 INDICATOR_PULSE, table 39, [1] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseIndicatorPulse) ); } } break; // Allows to read the status byte using class request case eUSBTMC_USB488_READ_STATUS_BYTE: { if( sizeof(sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte) == bytesRemaining ) // 4.3.1 READ_STATUS_BYTE, [2] { bytes += usb_write_transfer( tx, sUsbTmcContext.tmpSetup.rawBytesResponse, sizeof(sUsbTmcContext.tmpSetup.responseUsb488ReadStatusByte) ); // need to force-call Interrupt-IN handler (fUSBTMCProto_IntTxHandler();) USBTMC_datain_beginsend( USBTMC_INTERRUPTIN_EP ); } } break; } } break; } return bytes; } // =================================================================================== // @fUSBTMCProto_DataRxHandler // USBTMC Data Incoming handler (Bulk-Out) static bool fUSBTMCProto_DataRxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * rx ) { eTMCLibStatus_t status; bulk_out_label: status = tmclib_generic_event( COMPOUND_tmclib_event_context_bulkout( eventCtx, rx ) ); if( USBTMC_NOTERROR(status) ) { if( USBTMC_WARNING(status) ) { switch( status ) { case tmclib_status_invalid_param: { my_assert( status != tmclib_status_invalid_param ); // 'tmclib_bulkout_event' SHALL never return 'tmclib_status_invalid_param' } break; case tmclib_status_in_progress: { my_assert( status != tmclib_status_in_progress ); // 'tmclib_bulkout_event' never returns 'tmclib_status_in_progress' } break; case tmclib_status_again: { // there is some data in @rx transfer required to be processed } goto bulk_out_label;; case tmclib_status_need_data: { my_assert( status != tmclib_status_need_data ); // 'tmclib_bulkout_event' never returns 'tmclib_status_need_data' } break; case tmclib_status_not_in_progress: { my_assert( status != tmclib_status_not_in_progress ); // 'tmclib_bulkout_event' never returns 'tmclib_status_not_in_progress' } break; case tmclib_status_no_more_data: { my_assert( status != tmclib_status_no_more_data ); // 'tmclib_bulkout_event' never returns 'tmclib_status_no_more_data' } break; case tmclib_status_need_read: { // normal operation uint8_t rc = USBTMC_datain_sendprepared( USBTMC_BULKIN_EP ); (void)rc; } break; default: my_assert( status == tmclib_status_success ); // 'tmclib_bulkout_event' returns undefined code } } } else { if( status == tmclib_status_halt_bulkin ) { // 3.3.2.4 Halt, [1] USBD_USBTMC_StallEP( USBTMC_BULKIN_EP ); } // 3.2.2.3 Bulk-OUT transfer protocol errors, [1] // 3.2.2.4 Halt, [1] USBD_USBTMC_StallEP( USBTMC_BULKOUT_EP ); } return USBTMC_NOTERROR(status); } // =================================================================================== // @fUSBTMCProto_DataTxHandler // USBTMC Data Outcoming handler (Bulk-IN) // 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 bool fUSBTMCProto_DataTxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * tx ) { eTMCLibStatus_t status = tmclib_generic_event( COMPOUND_tmclib_event_context_bulkin( eventCtx, tx ) ); if( USBTMC_NOTERROR(status) ) { if( USBTMC_WARNING(status) ) { switch( status ) { case tmclib_status_invalid_param: { my_assert( status != tmclib_status_invalid_param ); // 'tmclib_bulkin_event' SHALL never return 'tmclib_status_invalid_param' } break; case tmclib_status_in_progress: { my_assert( status != tmclib_status_in_progress ); // 'tmclib_bulkin_event' never returns 'tmclib_status_in_progress' } break; case tmclib_status_again: { my_assert( status != tmclib_status_again ); // 'tmclib_bulkin_event' never returns 'tmclib_status_again' } break; case tmclib_status_need_data: { my_assert( status != tmclib_status_need_data ); // 'tmclib_bulkin_event' never returns 'tmclib_status_need_data' } break; case tmclib_status_not_in_progress: case tmclib_status_no_more_data: // need set NAK condition over EP { // cleanup EP IN to provide NAK response in next IN transaction USBD_USBTMC_NakEP( USBTMC_BULKIN_EP ); } break; case tmclib_status_need_read: { // normal operation } break; default: my_assert( status == tmclib_status_success ); // 'tmclib_bulkin_event' returns undefined code } } return true; } return false; } // =================================================================================== // @fUSBTMCProto_DataErrHandler // USBTMC Error handler for Data channel static bool fUSBTMCProto_DataErrHandler( uint8_t bEpLogAddress, uint32_t error ) { #warning BulkOut -> Stall return true; // bibb } // =================================================================================== // @fUSBTMCProto_IntTxHandler // USBTMC Interrupt Outcoming handler (Interrupt-IN) static bool fUSBTMCProto_IntTxHandler( uint8_t bEpLogAddress, sUSBTransfer_t * ntx ) { eTMCLibStatus_t status = tmclib_generic_event( COMPOUND_tmclib_event_context_interruptin( eventCtx, ntx ) ); if( USBTMC_NOTERROR(status) ) { switch( status ) { case tmclib_status_invalid_param: { my_assert( status != tmclib_status_invalid_param ); // 'tmclib_interruptin_event' SHALL never return 'tmclib_status_invalid_param' } break; case tmclib_status_in_progress: { my_assert( status != tmclib_status_in_progress ); // 'tmclib_interruptin_event' never returns 'tmclib_status_in_progress' } break; case tmclib_status_again: { my_assert( status != tmclib_status_again ); // 'tmclib_interruptin_event' never returns 'tmclib_status_again' } break; case tmclib_status_need_data: { my_assert( status != tmclib_status_need_data ); // 'tmclib_interruptin_event' never returns 'tmclib_status_need_data' } break; case tmclib_status_not_in_progress: case tmclib_status_no_more_data: // need set NAK condition over EP { // cleanup EP IN to provide NAK response in next IN transaction USBD_USBTMC_NakEP( USBTMC_INTERRUPTIN_EP ); } break; case tmclib_status_success: case tmclib_status_need_read: { // normal operation } break; default: my_assert( status == tmclib_status_success ); // 'tmclib_interruptin_event' returns undefined code } return true; } return false; } // =================================================================================== // @fUSBTMCProto_IntErrHandler // USBTMC Error handler for Interrupt channel static bool fUSBTMCProto_IntErrHandler( uint8_t bEpLogAddress, uint32_t error ) { return true; // bibb } // =================================================================================== // USBTMC_datain_beginsend() // Begin the transmission using DataIN handler virtual call. // Due to the hardware does not support NAK-sent-interrupt, it is // required to initiate the first packet sending to start TX-flow (DataIN). // This function issues the first packet sending using virtual DataIN event. // uint8_t USBTMC_datain_beginsend( uint8_t epnum ) { // issue virtual DataIN event: return USBD_USBTMC_DataIn_BeginSend( epnum, false ); } // =================================================================================== // USBTMC_datain_sendprepared() // Begin the transmission using already prepared TX-transfer. // Due to the hardware does not support NAK-sent-interrupt, it is // required to initiate the first packet sending to start TX-flow (DataIN). // This function issues the first packet sending using virtual DataIN event. // uint8_t USBTMC_datain_sendprepared( uint8_t epnum ) { // issue virtual DataIN event: return USBD_USBTMC_DataIn_BeginSend( epnum, true ); } // =================================================================================== // USBTMC_datain_zerosend() // Queue zero packet in BulkIN EP without calling TX-handler // uint8_t USBTMC_datain_zerosend( uint8_t epnum ) { return USBD_USBTMC_DataIn_ZeroSend( epnum ); }