#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h' #define SCPI_CORE_C // access to const values in 'scpi_core_private.h' structures #include "app/scpi/scpi_core.h" #include "app/scpi/scpi_core_private.h" #include "app/scpi/scpi_parser.h" #include "app/scpi/scpi_commands.h" #include "app/scpi/scpi_errq.h" #include "app/scpi/CommandParserStates/reset_state.h" #include "app/scpi/CommandParserStates/search_for_command.h" #include "app/scpi/CommandParserStates/process_command.h" #include "app/scpi/CommandParserStates/process_data.h" #include "app/scpi/CommandParserStates/endof_process_data.h" #include "app/scpi/CommandParserStates/handle_error.h" #include #include "my_assert.h" //---------------------------------------------------------------- // Refer to: // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992) // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE // Standard Digital Interface for Programmable Instrumentation" // [2] "Programming Guide", "Keysight Models 664xA, 665xA, 667xA, 668xA, and 669xA GPIB DC Power Supplies", Edition 2, December 2014, "5964-8269" static eScpiStatus_t fSCPIInit( ); static eScpiStatus_t fSCPIReset( ); static eScpiStatus_t fSCPINewOutTransfer( ); static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer ); static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM ); static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB ); static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx ); static eScpiStatus_t fSCPIDeInit(); const sSCPILibHandle_t sSCPILibHandle = { .fInit = fSCPIInit, .fReset = fSCPIReset, .fNewTransfer = fSCPINewOutTransfer, .fWrite = fSCPIWrite, .fRead = fSCPIRead, .fNotificationRead = fSCPINotificationRead, .fError = fSCPIError, .fDeInit = fSCPIDeInit }; // ----------------------------------------------------------- // @seqScpiProcessor // The SCPI module functor-sequencer for command iterator static xFSeqObj_t seqScpiProcessor; // ----------------------------------------------------------- // @ctx_ScpiParser // SCPI parser common context static sScpiParserContext_t ctx_ScpiParser; // ----------------------------------------------------------- const sScpiParserStateEntry_t sParserStateMap[] = { // State: eParserResetState [eParserResetState] = SCPI_STATE_PARSERRESETSTATE, // State: SearchForCommandHeader [eSearchForCommandHeader] = SCPI_STATE_SEARCH4COMMAND, // State: ProcessCommandHeader [eProcessCommandHeader] = SCPI_STATE_PROCESSCOMMAND, // State: ProcessProgramData [eProcessProgramData] = SCPI_STATE_PROCESSDATA, // State: EndOfProcessProgramData [eProcessEndOfProgramData] = SCPI_STATE_ENDOFPROCESSDATA, // State: HandleError [eHandleError] = SCPI_STATE_HANDLEERROR, }; // ----------------------------------------------------------- // @fsq_GetPrivateCtxRef // Get access to the state's private context by pointer // @this - the state entry, passed into main state function, or // ... entry-/leave- routines. // Returns: the state private context pointer that represented by the // @ctx_ref field in @sScpiParserStateEntry_t structure. void * fsq_GetPrivateCtxRef( const struct fFSeqEntry_t * this ) { // Refer to: @sParserStateMap[], @sScpiParserStateEntry_t const sScpiParserStateEntry_t * container = (const sScpiParserStateEntry_t *)this; return container->ctx_ref; } // ----------------------------------------------------------- // @fsq_GetStateById_unsafe // Get the state entry by state identifier @sid // @sid - the state identifier (eScpiParserStateId_t) // Returns: the state entry pointer static const struct fFSeqEntry_t * fsq_GetStateById_unsafe( eScpiParserStateId_t sid ) { if( sid < cellsof(sParserStateMap) ) return &sParserStateMap[sid].entry; return NULL; } // ----------------------------------------------------------- // @fsq_GetStateById // Get the state entry by state identifier @sid // @this - current state pointer // @sid - the state identifier (eScpiParserStateId_t) // Returns: the state entry pointer // Note: the caller must be the state from the same entity const struct fFSeqEntry_t * fsq_GetStateById( const struct fFSeqEntry_t * this, eScpiParserStateId_t sid ) { my_assert( sid < cellsof(sParserStateMap) ); if( sid < cellsof(sParserStateMap) ) { ptrdiff_t range_begin = (ptrdiff_t)&sParserStateMap[0]; ptrdiff_t range_end = (ptrdiff_t)&sParserStateMap[ cellsof(sParserStateMap) - 1 ]; my_assert( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end ); if( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end ) return &sParserStateMap[sid].entry; } return NULL; } // ----------------------------------------------------------- // @fsq_RaiseErrorEx // Generate internal error event to place it into SCPI Error Log // @scpiError - SCPI error code // @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number // @msgDescription - optional string message with @msgDescLength length // @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription) // @msgDescriptionEx - extended optional string message with @msgDescLength length // @msgDescriptionEndEx - end of extended optional message string, is used to determine the string length (@msgDescriptionEx) void fsq_RaiseErrorEx( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd, const char * msgDescriptionEx, const char * msgDescriptionEndEx ) { ctx_ScpiParser.sExecution.runtimeError = scpiError; size_t nPos = 0; size_t nSize = SCPI_ERRORMSG_MAX - 1; if( NULL != msgHeader ) { char errNumber[8] = { '\0' }; _snprintf( errNumber, sizeof(errNumber), "%d, ", scpiError ); size_t plen = strlen( errNumber ); if( plen < nSize ) { strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], errNumber ); nSize -= plen; nPos += plen; } plen = strlen( msgHeader ); my_assert( plen < SCPI_ERRORMSG_MAX ); if( plen < nSize ) { strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgHeader ); nSize -= plen; nPos += plen; } } if( NULL == msgDescription || NULL == msgDescriptionEnd ) { msgDescription = msgDescriptionEx; msgDescriptionEnd = msgDescriptionEndEx; msgDescriptionEx = NULL; msgDescriptionEndEx = NULL; } if( NULL != msgDescription && NULL != msgDescriptionEnd ) { // remove trailing NL-character if( scpi_isnl( *(msgDescriptionEnd-1) ) ) { msgDescriptionEnd--; } if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription == 0 ) { msgDescription = msgDescriptionEx; msgDescriptionEnd = msgDescriptionEndEx; msgDescriptionEx = NULL; msgDescriptionEndEx = NULL; } if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription > 0 ) { size_t msgDescLength = (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription; size_t plen = msgDescLength; if( NULL != msgHeader ) { // Default message mode: @msgHeader and @msgDescription const char prfstr[] = ": '"; plen += strlen(prfstr); const char psfstr[] = "'"; plen += strlen(psfstr); if( plen < nSize ) { // Full message fits to the buffer strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr ); strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr ); } else { const char sfxstr[] = "..."; plen = strlen(sfxstr); plen += strlen(prfstr); plen += strlen(psfstr); // there is free space for a part of description? if( nSize >= plen ) { msgDescLength = nSize - plen; if( msgDescLength > 8 ) { strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr ); strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr ); plen += msgDescLength; } else plen = 0; } else plen = 0; } } else { // Custom message mode: Only @msgDescription if( plen < nSize ) { strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescription, msgDescLength ); } else { plen = 0; } } nSize -= plen; nPos += plen; } } if( NULL != msgDescriptionEx && NULL != msgDescriptionEndEx ) { // remove trailing NL-character if( scpi_isnl( *(msgDescriptionEndEx-1) ) ) { msgDescriptionEndEx--; } if( (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx > 0 ) { size_t msgDescLength = (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx; size_t plen = msgDescLength; if( NULL != msgHeader || NULL != msgDescription ) { // Default message mode: @msgHeader or @msgDescription already exist const char prfstr[] = " in '"; plen += strlen(prfstr); const char psfstr[] = "'"; plen += strlen(psfstr); if( plen < nSize ) { // Full message fits to the buffer strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr ); strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr ); } else { const char sfxstr[] = "..."; plen = strlen(sfxstr); plen += strlen(prfstr); plen += strlen(psfstr); // there is free space for a part of description? if( nSize >= plen ) { msgDescLength = nSize - plen; if( msgDescLength > 12 ) { strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr ); strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr ); strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr ); plen += msgDescLength; } else plen = 0; } else plen = 0; } } else { // Custom message mode: Only @msgDescriptionEx if( plen < nSize ) { strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescriptionEx, msgDescLength ); } else { plen = 0; } } nSize -= plen; nPos += plen; } } ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos] = '\0'; } // ----------------------------------------------------------- // @fsq_RaiseError // Generate internal error event to place it into SCPI Error Log // @scpiError - SCPI error code // @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number // @msgDescription - optional string message with @msgDescLength length // @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription) void fsq_RaiseError( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd ) { fsq_RaiseErrorEx( scpiError, msgHeader, msgDescription, msgDescriptionEnd, NULL, NULL ); } // ----------------------------------------------------------- int32_t scpi_WriteChunkOutput( const void * src, size_t length ) { // This routine may be called even if no output buffer is prepared. // Check it if( NULL == ctx_ScpiParser.sRead.pBufferIdx || 0 == ctx_ScpiParser.sRead.nBufferSize || NULL == ctx_ScpiParser.sRead.pBuffer ) { return 0; } size_t chunkSize = ((ctx_ScpiParser.sRead.nBufferSize > length)? length : ctx_ScpiParser.sRead.nBufferSize); // min(nBufferSize, length) if( chunkSize > 0 ) { memcpy( ctx_ScpiParser.sRead.pBufferIdx, src, chunkSize ); ctx_ScpiParser.sRead.pBufferIdx += chunkSize; ctx_ScpiParser.sRead.nDataLength += chunkSize; ctx_ScpiParser.sRead.nTotalLength += chunkSize; ctx_ScpiParser.sRead.nBufferSize -= chunkSize; } return chunkSize; } int32_t scpi_WriteCharOutput( uint8_t ch ) { return scpi_WriteChunkOutput( &ch, sizeof(ch) ); } // ----------------------------------------------------------- // @scpi_UpdateMessageAvailable // Updates the message available status, MAV // "6.1.10.2.1 Message Available Message (MAV)", [1] void scpi_UpdateMessageAvailable() { // Check if .sRead structure is available: // Note this routine can be called on Read and Write events not only from SCPI-core, but // by changing Write event to Read event, in this case .sRead structure is initialized. if( (NULL != ctx_ScpiParser.sRead.pBuffer) && (0 != ctx_ScpiParser.sRead.nBufferSize) ) { // Message available: only if EOM is reset the MAV is set if is there data in the output buffer. // Otherwise, if EOM is set, these bytes will be send as soon as possible, so this means there no data to send anymore // in SCPI core. bool message_available = (ctx_ScpiParser.sRead.nDataLength > 0) && (!ctx_ScpiParser.sMessage.r_bEndOfMessage); // Is there data to send to host: set Message-Available indicator GPIBMachine.fGPIB_set_message_available_bit( &ctx_ScpiParser.sGPIB.registers, message_available ); } } // ----------------------------------------------------------- // @GPIB_Init // Initialize the GPIB machine state to the startup defaults static void GPIB_Init() { GPIBMachine.fGPIB_set_event_status_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 ); // Reset ESE GPIBMachine.fGPIB_set_service_request_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 ); // Reset SRE GPIBMachine.fGPIB_set_event_summary_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.ESB GPIBMachine.fGPIB_set_message_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.MAV GPIBMachine.fGPIB_set_error_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.EAV // "11.5.1.1.2 Bit 7 - Power On (PON)", [1] // "6.1.8.2.1 Power-On Message (pon)", [1] // "6.3.1.1 IDLE State", [1] // "The PON (Power-On) Bit", "Service Request Enable Register", [2] GPIBMachine.fGPIB_set_event_status_register_power_on_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set ESR.PON // "5.6.1 Control and Operation Definitions", [1] GPIBMachine.fGPIB_set_event_status_register_user_request_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.5.1.1.4 Bit 5 - Command ERROR (CME)", [1] GPIBMachine.fGPIB_set_event_status_register_command_error_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.5.1.1.5 Bit 4 - Execution ERROR (E)", [1] GPIBMachine.fGPIB_set_event_status_register_execution_error_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.5.1.1.6 Bit 3 - Device-Specific ERROR (DDE)", [1] GPIBMachine.fGPIB_set_event_status_register_device_specific_error_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.5.1.1.7 Bit 2 - Query ERROR (QYE)", [1] GPIBMachine.fGPIB_set_event_status_register_query_error_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.5.1.1.8 Bit 1 - Request Control (RQC)", [1] GPIBMachine.fGPIB_set_event_status_register_request_control_state( &ctx_ScpiParser.sGPIB.registers, false ); // Set OPC: // Note, since the device supports only syncrounious commands, the OPC can be set once and have never been reset. // "11.5.1.1.9 Bit 0 - Operation Complete (OPC)", [1] // "<...> This event bit is generated in response to the *OPC command. <...>" GPIBMachine.fGPIB_set_event_status_register_operation_complete_state( &ctx_ScpiParser.sGPIB.registers, false ); // "11.3.3 Service Request Generation", [1] // Service Request Generation - reset status GPIBMachine.fGPIB_set_service_request_status( &ctx_ScpiParser.sGPIB.registers, false ); } // ----------------------------------------------------------- // @fSCPIInit // Initialize parser static eScpiStatus_t fSCPIInit() { // prepare global context: see @eParserResetState state dispatch function (void)ctx_ScpiParser; // construct state machine object, link the first state 'eSearchForCommandHeader' fSeqConstruct( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState), &ctx_ScpiParser ); // Initialize SCPI Error Log Queue ctx_ScpiParser.sExecution.errorQueueOverflow = false; // reset overflow indicator bool errqrc = errq_init( &ctx_ScpiParser.sExecution.xScpiErrorQueue, ctx_ScpiParser.sExecution.xScpiErrorLogStorage, sizeof(ctx_ScpiParser.sExecution.xScpiErrorLogStorage) ); my_assert( errqrc ); (void)errqrc; // "5.8 Device Clear Requirements", [1] // In Device Clear event: DO NOT clear Error Queue and any bits other than clearing MAV bit in GPIB // Due to the 'reset_state' is executes on DEVICE CLEAR (INITIATE_CLEAR), it is impossible to // initialize GPIB in 'reset_state'. // So, the initialization is placed here: GPIB_Init(); // startup the SCPI parser state machine fSeqStartup( &seqScpiProcessor, NULL ); // ... It is requried to execute the ResetState job: initialize and // ... go next state fSeqDispatch( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPIReset // Reset parser static eScpiStatus_t fSCPIReset() { // shutdown the SCPI parser state machine fSeqShutdown( &seqScpiProcessor ); // startup the SCPI parser state machine a reset state fSeqStartup( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState) ); // process the state machine state: // ... It is requried to execute the ResetState job: initialize and // ... go next state fSeqDispatch( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPINewOutTransfer // New Transfer Event static eScpiStatus_t fSCPINewOutTransfer() { // prepare global context: set the buffer properties ctx_ScpiParser.sWrite.pData = NULL; ctx_ScpiParser.sWrite.nDataLength = NULL; ctx_ScpiParser.sMessage.w_bEndOfMessage = false; ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // IN-transfer is reset ctx_ScpiParser.sMessage.bQuery = false; ctx_ScpiParser.sMessage.pStr = NULL; ctx_ScpiParser.sMessage.bLeadResponseSep = false; ctx_ScpiParser.sRead.pBuffer = NULL; ctx_ScpiParser.sRead.pBufferIdx = NULL; ctx_ScpiParser.sRead.nDataLength = 0; ctx_ScpiParser.sRead.nBufferSize = 0; ctx_ScpiParser.sRead.nTotalLength = 0; ctx_ScpiParser.sRead.vLastBufferIdx = NULL; //ctx_ScpiParser.sEvent.bNewTransfer = true; // pass New Transfer indicator ctx_ScpiParser.sEvent.eCode = eScpiEventRestart; // send RESTART event // process the state machine state fSeqDispatch( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPIWrite // Write parser event static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer ) { my_assert( pData ); my_assert( nDataLen ); // Bufferizing input command string: if( bNewTransfer ) { // New transfer: cleanup cached input string and replace it with new one my_assert( nDataLen < sizeof(ctx_ScpiParser.sCache.inputCommand) ); if( nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) ) { return eScpiStatus_failed; // buffer overflow } memcpy( &ctx_ScpiParser.sCache.inputCommand[0], pData, nDataLen ); ctx_ScpiParser.sCache.nBytes = nDataLen; } else { // Continue transfer: concatinate cached input string with new part if( ctx_ScpiParser.sCache.nBytes + nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) ) { return eScpiStatus_failed; // buffer overflow } memcpy( &ctx_ScpiParser.sCache.inputCommand[ctx_ScpiParser.sCache.nBytes], pData, nDataLen ); ctx_ScpiParser.sCache.nBytes += nDataLen; } // prepare global context: set the buffer properties ctx_ScpiParser.sWrite.pData = ctx_ScpiParser.sCache.inputCommand; ctx_ScpiParser.sWrite.nDataLength = ctx_ScpiParser.sCache.nBytes; ctx_ScpiParser.sMessage.w_bEndOfMessage = bEndOfMessage; (void)ctx_ScpiParser.sMessage.r_bEndOfMessage; ctx_ScpiParser.sMessage.bQuery = false; ctx_ScpiParser.sMessage.pStr = pData; ctx_ScpiParser.sMessage.pEnd = (pData + nDataLen); ctx_ScpiParser.sMessage.bLeadResponseSep = false; ctx_ScpiParser.sRead.pBuffer = NULL; ctx_ScpiParser.sRead.pBufferIdx = NULL; ctx_ScpiParser.sRead.nBufferSize = 0; ctx_ScpiParser.sRead.nDataLength = 0; ctx_ScpiParser.sRead.vLastBufferIdx = NULL; (void)ctx_ScpiParser.sRead.nTotalLength; //ctx_ScpiParser.sEvent.bNewTransfer = bNewTransfer; // pass New Transfer indicator ctx_ScpiParser.sEvent.eCode = eScpiEventWrite; // send WRITE event // process the state machine state fSeqDispatch( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPIRead // Read parser event // @pBuffer - response buffer pointer // @nBufferLen - response buffer size // @pnBytesOut - pointer to the variable to store the value of written bytes to response buffer // @pEOM - pointer to the variable to store the End-Of-Message indicator. This indicator is set by // the SCPI library to notify the caller to specify SCPI End-of-message flag in Bulk-IN header that // is responsible for the stream control. If this flag is set, the Host must generate next READ event // even if the current buffer is not completely full. It is used together with complex command that // require several responses. Note: this value is only valid if the return value is not the error code. static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM ) { my_assert( pBuffer ); my_assert( nBufferLen ); my_assert( pnBytesOut ); // prepare global context: set the buffer properties (void)ctx_ScpiParser.sWrite.pData; // keep value (void)ctx_ScpiParser.sWrite.nDataLength; // keep value (void)ctx_ScpiParser.sMessage.w_bEndOfMessage; // keep value ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // reset by default, need to set to interrupt reading (void)ctx_ScpiParser.sMessage.bQuery; // keep value (void)ctx_ScpiParser.sMessage.pStr; // keep value (void)ctx_ScpiParser.sMessage.pEnd; // keep value (void)ctx_ScpiParser.sMessage.bLeadResponseSep; // keep value ctx_ScpiParser.sRead.pBuffer = pBuffer; ctx_ScpiParser.sRead.pBufferIdx = pBuffer; ctx_ScpiParser.sRead.nBufferSize = nBufferLen; ctx_ScpiParser.sRead.nDataLength = 0; ctx_ScpiParser.sRead.vLastBufferIdx = pBuffer; (void)ctx_ScpiParser.sRead.nTotalLength; ctx_ScpiParser.sEvent.eCode = eScpiEventRead; // send READ event ctx_ScpiParser.sEvent.eStatus = eScpiStatus_failed; // forward set by default //ctx_ScpiParser.sEvent.bNewTransfer = false; // process the state machine state fSeqDispatch( &seqScpiProcessor ); *pnBytesOut = ctx_ScpiParser.sRead.nDataLength; // @nDataLength shall be modified *pEOM = ctx_ScpiParser.sMessage.r_bEndOfMessage; #warning SCPI missing EOM bit set if TransferSize is full (return eScpiStatus_need_data) return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPINotificationRead // Read notification data (Interrupt-IN read) static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB ) { my_assert( pBuffer ); my_assert( nBufferLen ); my_assert( pnBytesOut ); // "11.2.2.1 Reading with a Serial Poll", [1] // "11.2.2.2 Reading With the *STB? Query", [1] GPIBMachine.fGPIB_get_status_byte_serial_poll( &ctx_ScpiParser.sGPIB.registers, pSTB ); *pnBytesOut = 0; // no more bytes will be returned in notification, [2] 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request ctx_ScpiParser.sEvent.eStatus = eScpiStatus_success; // forward set by default return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPIError // Error notification from USBTMC level. // @error - error id, see @SCPILIB_ERROR_* macro group // @errorCtx - error dependent context, optional; // Return: // - eScpiStatus_success: error processed successfully // - eScpiStatus_failed: unrecoveral error, need to process it on USBTMC level. static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx ) { // shall not be SCPILIB_ERROR_SUCCESS my_assert( error != SCPILIB_ERROR_SUCCESS ); // prepare global context: set the buffer properties ctx_ScpiParser.sWrite.pData = NULL; ctx_ScpiParser.sWrite.nDataLength = 0; (void)ctx_ScpiParser.sMessage.w_bEndOfMessage; (void)ctx_ScpiParser.sMessage.r_bEndOfMessage; (void)ctx_ScpiParser.sMessage.bQuery; ctx_ScpiParser.sMessage.pStr = NULL; ctx_ScpiParser.sMessage.pEnd = NULL; (void)ctx_ScpiParser.sMessage.bLeadResponseSep; ctx_ScpiParser.sRead.pBuffer = NULL; ctx_ScpiParser.sRead.pBufferIdx = NULL; ctx_ScpiParser.sRead.nBufferSize = 0; ctx_ScpiParser.sRead.nDataLength = 0; ctx_ScpiParser.sRead.vLastBufferIdx = NULL; (void)ctx_ScpiParser.sRead.nTotalLength; ctx_ScpiParser.sEvent.eCode = eScpiEventError; // send ERROR event ctx_ScpiParser.sEvent.eStatus = eScpiStatus_invalid; switch( error ) { case SCPILIB_ERROR_BUFFER_OVERFLOW: { fsq_RaiseError( SCPI_ERROR_INBUF_OVERFLOW, SCPI_ERROR_INBUF_OVERFLOW_MSG, NULL, 0 ); } break; default: return eScpiStatus_invalid; // invalid error code, interrupt processing } // process the state machine state fSeqDispatch( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; } // ----------------------------------------------------------- // @fSCPIDeInit // Deinitialize parser static eScpiStatus_t fSCPIDeInit() { // prepare global context: ctx_ScpiParser.sEvent.eCode = eScpiEventEmpty; // no event occurred // shutdown the SCPI parser state machine fSeqShutdown( &seqScpiProcessor ); return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus; }