scpi_core.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. #define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
  2. #define SCPI_CORE_C // access to const values in 'scpi_core_private.h' structures
  3. #include "app/scpi/scpi_core.h"
  4. #include "app/scpi/scpi_core_private.h"
  5. #include "app/scpi/scpi_parser.h"
  6. #include "app/scpi/scpi_commands.h"
  7. #include "app/scpi/scpi_errq.h"
  8. #include "app/scpi/CommandParserStates/reset_state.h"
  9. #include "app/scpi/CommandParserStates/search_for_command.h"
  10. #include "app/scpi/CommandParserStates/process_command.h"
  11. #include "app/scpi/CommandParserStates/process_data.h"
  12. #include "app/scpi/CommandParserStates/endof_process_data.h"
  13. #include "app/scpi/CommandParserStates/handle_error.h"
  14. #include <stdio.h>
  15. #include "my_assert.h"
  16. //----------------------------------------------------------------
  17. // Refer to:
  18. // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992)
  19. // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE
  20. // Standard Digital Interface for Programmable Instrumentation"
  21. // [2] "Programming Guide", "Keysight Models 664xA, 665xA, 667xA, 668xA, and 669xA GPIB DC Power Supplies", Edition 2, December 2014, "5964-8269"
  22. static eScpiStatus_t fSCPIInit( );
  23. static eScpiStatus_t fSCPIReset( );
  24. static eScpiStatus_t fSCPINewOutTransfer( );
  25. static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer );
  26. static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM );
  27. static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB );
  28. static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx );
  29. static eScpiStatus_t fSCPIDeInit();
  30. const sSCPILibHandle_t sSCPILibHandle = {
  31. .fInit = fSCPIInit,
  32. .fReset = fSCPIReset,
  33. .fNewTransfer = fSCPINewOutTransfer,
  34. .fWrite = fSCPIWrite,
  35. .fRead = fSCPIRead,
  36. .fNotificationRead = fSCPINotificationRead,
  37. .fError = fSCPIError,
  38. .fDeInit = fSCPIDeInit
  39. };
  40. // -----------------------------------------------------------
  41. // @seqScpiProcessor
  42. // The SCPI module functor-sequencer for command iterator
  43. static xFSeqObj_t seqScpiProcessor;
  44. // -----------------------------------------------------------
  45. // @ctx_ScpiParser
  46. // SCPI parser common context
  47. static sScpiParserContext_t ctx_ScpiParser;
  48. // -----------------------------------------------------------
  49. const sScpiParserStateEntry_t sParserStateMap[] = {
  50. // State: eParserResetState
  51. [eParserResetState] = SCPI_STATE_PARSERRESETSTATE,
  52. // State: SearchForCommandHeader
  53. [eSearchForCommandHeader] = SCPI_STATE_SEARCH4COMMAND,
  54. // State: ProcessCommandHeader
  55. [eProcessCommandHeader] = SCPI_STATE_PROCESSCOMMAND,
  56. // State: ProcessProgramData
  57. [eProcessProgramData] = SCPI_STATE_PROCESSDATA,
  58. // State: EndOfProcessProgramData
  59. [eProcessEndOfProgramData] = SCPI_STATE_ENDOFPROCESSDATA,
  60. // State: HandleError
  61. [eHandleError] = SCPI_STATE_HANDLEERROR,
  62. };
  63. // -----------------------------------------------------------
  64. // @fsq_GetPrivateCtxRef
  65. // Get access to the state's private context by pointer
  66. // @this - the state entry, passed into main state function, or
  67. // ... entry-/leave- routines.
  68. // Returns: the state private context pointer that represented by the
  69. // @ctx_ref field in @sScpiParserStateEntry_t structure.
  70. void * fsq_GetPrivateCtxRef( const struct fFSeqEntry_t * this )
  71. {
  72. // Refer to: @sParserStateMap[], @sScpiParserStateEntry_t
  73. const sScpiParserStateEntry_t * container = (const sScpiParserStateEntry_t *)this;
  74. return container->ctx_ref;
  75. }
  76. // -----------------------------------------------------------
  77. // @fsq_GetStateById_unsafe
  78. // Get the state entry by state identifier @sid
  79. // @sid - the state identifier (eScpiParserStateId_t)
  80. // Returns: the state entry pointer
  81. static const struct fFSeqEntry_t * fsq_GetStateById_unsafe( eScpiParserStateId_t sid )
  82. {
  83. if( sid < cellsof(sParserStateMap) )
  84. return &sParserStateMap[sid].entry;
  85. return NULL;
  86. }
  87. // -----------------------------------------------------------
  88. // @fsq_GetStateById
  89. // Get the state entry by state identifier @sid
  90. // @this - current state pointer
  91. // @sid - the state identifier (eScpiParserStateId_t)
  92. // Returns: the state entry pointer
  93. // Note: the caller must be the state from the same entity
  94. const struct fFSeqEntry_t * fsq_GetStateById( const struct fFSeqEntry_t * this, eScpiParserStateId_t sid )
  95. {
  96. my_assert( sid < cellsof(sParserStateMap) );
  97. if( sid < cellsof(sParserStateMap) )
  98. {
  99. ptrdiff_t range_begin = (ptrdiff_t)&sParserStateMap[0];
  100. ptrdiff_t range_end = (ptrdiff_t)&sParserStateMap[ cellsof(sParserStateMap) - 1 ];
  101. my_assert( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end );
  102. if( (ptrdiff_t)this >= range_begin && (ptrdiff_t)this <= range_end )
  103. return &sParserStateMap[sid].entry;
  104. }
  105. return NULL;
  106. }
  107. // -----------------------------------------------------------
  108. // @fsq_RaiseErrorEx
  109. // Generate internal error event to place it into SCPI Error Log
  110. // @scpiError - SCPI error code
  111. // @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number
  112. // @msgDescription - optional string message with @msgDescLength length
  113. // @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription)
  114. // @msgDescriptionEx - extended optional string message with @msgDescLength length
  115. // @msgDescriptionEndEx - end of extended optional message string, is used to determine the string length (@msgDescriptionEx)
  116. void fsq_RaiseErrorEx( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd,
  117. const char * msgDescriptionEx, const char * msgDescriptionEndEx )
  118. {
  119. ctx_ScpiParser.sExecution.runtimeError = scpiError;
  120. size_t nPos = 0;
  121. size_t nSize = SCPI_ERRORMSG_MAX - 1;
  122. if( NULL != msgHeader )
  123. {
  124. char errNumber[8] = { '\0' };
  125. _snprintf( errNumber, sizeof(errNumber), "%d, ", scpiError );
  126. size_t plen = strlen( errNumber );
  127. if( plen < nSize )
  128. {
  129. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], errNumber );
  130. nSize -= plen;
  131. nPos += plen;
  132. }
  133. plen = strlen( msgHeader );
  134. my_assert( plen < SCPI_ERRORMSG_MAX );
  135. if( plen < nSize )
  136. {
  137. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgHeader );
  138. nSize -= plen;
  139. nPos += plen;
  140. }
  141. }
  142. if( NULL == msgDescription || NULL == msgDescriptionEnd )
  143. {
  144. msgDescription = msgDescriptionEx;
  145. msgDescriptionEnd = msgDescriptionEndEx;
  146. msgDescriptionEx = NULL;
  147. msgDescriptionEndEx = NULL;
  148. }
  149. if( NULL != msgDescription && NULL != msgDescriptionEnd )
  150. {
  151. // remove trailing NL-character
  152. if( scpi_isnl( *(msgDescriptionEnd-1) ) )
  153. {
  154. msgDescriptionEnd--;
  155. }
  156. if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription == 0 )
  157. {
  158. msgDescription = msgDescriptionEx;
  159. msgDescriptionEnd = msgDescriptionEndEx;
  160. msgDescriptionEx = NULL;
  161. msgDescriptionEndEx = NULL;
  162. }
  163. if( (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription > 0 )
  164. {
  165. size_t msgDescLength = (ptrdiff_t)msgDescriptionEnd - (ptrdiff_t)msgDescription;
  166. size_t plen = msgDescLength;
  167. if( NULL != msgHeader )
  168. {
  169. // Default message mode: @msgHeader and @msgDescription
  170. const char prfstr[] = ": '"; plen += strlen(prfstr);
  171. const char psfstr[] = "'"; plen += strlen(psfstr);
  172. if( plen < nSize )
  173. {
  174. // Full message fits to the buffer
  175. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
  176. strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength );
  177. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
  178. }
  179. else
  180. {
  181. const char sfxstr[] = "..."; plen = strlen(sfxstr);
  182. plen += strlen(prfstr);
  183. plen += strlen(psfstr);
  184. // there is free space for a part of description?
  185. if( nSize >= plen )
  186. {
  187. msgDescLength = nSize - plen;
  188. if( msgDescLength > 8 )
  189. {
  190. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
  191. strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescription, msgDescLength );
  192. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr );
  193. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
  194. plen += msgDescLength;
  195. }
  196. else
  197. plen = 0;
  198. }
  199. else
  200. plen = 0;
  201. }
  202. }
  203. else
  204. {
  205. // Custom message mode: Only @msgDescription
  206. if( plen < nSize )
  207. {
  208. strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescription, msgDescLength );
  209. }
  210. else
  211. {
  212. plen = 0;
  213. }
  214. }
  215. nSize -= plen;
  216. nPos += plen;
  217. }
  218. }
  219. if( NULL != msgDescriptionEx && NULL != msgDescriptionEndEx )
  220. {
  221. // remove trailing NL-character
  222. if( scpi_isnl( *(msgDescriptionEndEx-1) ) )
  223. {
  224. msgDescriptionEndEx--;
  225. }
  226. if( (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx > 0 )
  227. {
  228. size_t msgDescLength = (ptrdiff_t)msgDescriptionEndEx - (ptrdiff_t)msgDescriptionEx;
  229. size_t plen = msgDescLength;
  230. if( NULL != msgHeader || NULL != msgDescription )
  231. {
  232. // Default message mode: @msgHeader or @msgDescription already exist
  233. const char prfstr[] = " in '"; plen += strlen(prfstr);
  234. const char psfstr[] = "'"; plen += strlen(psfstr);
  235. if( plen < nSize )
  236. {
  237. // Full message fits to the buffer
  238. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
  239. strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength );
  240. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
  241. }
  242. else
  243. {
  244. const char sfxstr[] = "..."; plen = strlen(sfxstr);
  245. plen += strlen(prfstr);
  246. plen += strlen(psfstr);
  247. // there is free space for a part of description?
  248. if( nSize >= plen )
  249. {
  250. msgDescLength = nSize - plen;
  251. if( msgDescLength > 12 )
  252. {
  253. strcpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], prfstr );
  254. strncat( ctx_ScpiParser.sExecution.xScpiErrorMessage, msgDescriptionEx, msgDescLength );
  255. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, sfxstr );
  256. strcat( ctx_ScpiParser.sExecution.xScpiErrorMessage, psfstr );
  257. plen += msgDescLength;
  258. }
  259. else
  260. plen = 0;
  261. }
  262. else
  263. plen = 0;
  264. }
  265. }
  266. else
  267. {
  268. // Custom message mode: Only @msgDescriptionEx
  269. if( plen < nSize )
  270. {
  271. strncpy( &ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos], msgDescriptionEx, msgDescLength );
  272. }
  273. else
  274. {
  275. plen = 0;
  276. }
  277. }
  278. nSize -= plen;
  279. nPos += plen;
  280. }
  281. }
  282. ctx_ScpiParser.sExecution.xScpiErrorMessage[nPos] = '\0';
  283. }
  284. // -----------------------------------------------------------
  285. // @fsq_RaiseError
  286. // Generate internal error event to place it into SCPI Error Log
  287. // @scpiError - SCPI error code
  288. // @msgHandler - optional null-terminated string prefix, if exist, the function also adds additional prefix with error number
  289. // @msgDescription - optional string message with @msgDescLength length
  290. // @msgDescriptionEnd - end of optional message string, is used to determine the string length (@msgDescription)
  291. void fsq_RaiseError( int scpiError, const char * msgHeader, const char * msgDescription, const char * msgDescriptionEnd )
  292. {
  293. fsq_RaiseErrorEx( scpiError, msgHeader, msgDescription, msgDescriptionEnd, NULL, NULL );
  294. }
  295. // -----------------------------------------------------------
  296. int32_t scpi_WriteChunkOutput( const void * src, size_t length )
  297. {
  298. // This routine may be called even if no output buffer is prepared.
  299. // Check it
  300. if( NULL == ctx_ScpiParser.sRead.pBufferIdx
  301. || 0 == ctx_ScpiParser.sRead.nBufferSize
  302. || NULL == ctx_ScpiParser.sRead.pBuffer )
  303. {
  304. return 0;
  305. }
  306. size_t chunkSize = ((ctx_ScpiParser.sRead.nBufferSize > length)? length : ctx_ScpiParser.sRead.nBufferSize); // min(nBufferSize, length)
  307. if( chunkSize > 0 )
  308. {
  309. memcpy( ctx_ScpiParser.sRead.pBufferIdx, src, chunkSize );
  310. ctx_ScpiParser.sRead.pBufferIdx += chunkSize;
  311. ctx_ScpiParser.sRead.nDataLength += chunkSize;
  312. ctx_ScpiParser.sRead.nTotalLength += chunkSize;
  313. ctx_ScpiParser.sRead.nBufferSize -= chunkSize;
  314. }
  315. return chunkSize;
  316. }
  317. int32_t scpi_WriteCharOutput( uint8_t ch )
  318. {
  319. return scpi_WriteChunkOutput( &ch, sizeof(ch) );
  320. }
  321. // -----------------------------------------------------------
  322. // @scpi_UpdateMessageAvailable
  323. // Updates the message available status, MAV
  324. // "6.1.10.2.1 Message Available Message (MAV)", [1]
  325. void scpi_UpdateMessageAvailable()
  326. {
  327. // Check if .sRead structure is available:
  328. // Note this routine can be called on Read and Write events not only from SCPI-core, but
  329. // by changing Write event to Read event, in this case .sRead structure is initialized.
  330. if( (NULL != ctx_ScpiParser.sRead.pBuffer) && (0 != ctx_ScpiParser.sRead.nBufferSize) )
  331. {
  332. // Message available: only if EOM is reset the MAV is set if is there data in the output buffer.
  333. // Otherwise, if EOM is set, these bytes will be send as soon as possible, so this means there no data to send anymore
  334. // in SCPI core.
  335. bool message_available = (ctx_ScpiParser.sRead.nDataLength > 0) && (!ctx_ScpiParser.sMessage.r_bEndOfMessage);
  336. // Is there data to send to host: set Message-Available indicator
  337. GPIBMachine.fGPIB_set_message_available_bit( &ctx_ScpiParser.sGPIB.registers, message_available );
  338. }
  339. }
  340. // -----------------------------------------------------------
  341. // @GPIB_Init
  342. // Initialize the GPIB machine state to the startup defaults
  343. static void GPIB_Init()
  344. {
  345. GPIBMachine.fGPIB_set_event_status_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 ); // Reset ESE
  346. GPIBMachine.fGPIB_set_service_request_enable_register( &ctx_ScpiParser.sGPIB.registers, 0 ); // Reset SRE
  347. GPIBMachine.fGPIB_set_event_summary_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.ESB
  348. GPIBMachine.fGPIB_set_message_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.MAV
  349. GPIBMachine.fGPIB_set_error_available_enable_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set SRE.EAV
  350. // "11.5.1.1.2 Bit 7 - Power On (PON)", [1]
  351. // "6.1.8.2.1 Power-On Message (pon)", [1]
  352. // "6.3.1.1 IDLE State", [1]
  353. // "The PON (Power-On) Bit", "Service Request Enable Register", [2]
  354. GPIBMachine.fGPIB_set_event_status_register_power_on_state( &ctx_ScpiParser.sGPIB.registers, true ); // Set ESR.PON
  355. // "5.6.1 Control and Operation Definitions", [1]
  356. GPIBMachine.fGPIB_set_event_status_register_user_request_state( &ctx_ScpiParser.sGPIB.registers, false );
  357. // "11.5.1.1.4 Bit 5 - Command ERROR (CME)", [1]
  358. GPIBMachine.fGPIB_set_event_status_register_command_error_state( &ctx_ScpiParser.sGPIB.registers, false );
  359. // "11.5.1.1.5 Bit 4 - Execution ERROR (E)", [1]
  360. GPIBMachine.fGPIB_set_event_status_register_execution_error_state( &ctx_ScpiParser.sGPIB.registers, false );
  361. // "11.5.1.1.6 Bit 3 - Device-Specific ERROR (DDE)", [1]
  362. GPIBMachine.fGPIB_set_event_status_register_device_specific_error_state( &ctx_ScpiParser.sGPIB.registers, false );
  363. // "11.5.1.1.7 Bit 2 - Query ERROR (QYE)", [1]
  364. GPIBMachine.fGPIB_set_event_status_register_query_error_state( &ctx_ScpiParser.sGPIB.registers, false );
  365. // "11.5.1.1.8 Bit 1 - Request Control (RQC)", [1]
  366. GPIBMachine.fGPIB_set_event_status_register_request_control_state( &ctx_ScpiParser.sGPIB.registers, false );
  367. // Set OPC:
  368. // Note, since the device supports only syncrounious commands, the OPC can be set once and have never been reset.
  369. // "11.5.1.1.9 Bit 0 - Operation Complete (OPC)", [1]
  370. // "<...> This event bit is generated in response to the *OPC command. <...>"
  371. GPIBMachine.fGPIB_set_event_status_register_operation_complete_state( &ctx_ScpiParser.sGPIB.registers, false );
  372. // "11.3.3 Service Request Generation", [1]
  373. // Service Request Generation - reset status
  374. GPIBMachine.fGPIB_set_service_request_status( &ctx_ScpiParser.sGPIB.registers, false );
  375. }
  376. // -----------------------------------------------------------
  377. // @fSCPIInit
  378. // Initialize parser
  379. static eScpiStatus_t fSCPIInit()
  380. {
  381. // prepare global context: see @eParserResetState state dispatch function
  382. (void)ctx_ScpiParser;
  383. // construct state machine object, link the first state 'eSearchForCommandHeader'
  384. fSeqConstruct( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState), &ctx_ScpiParser );
  385. // Initialize SCPI Error Log Queue
  386. ctx_ScpiParser.sExecution.errorQueueOverflow = false; // reset overflow indicator
  387. bool errqrc = errq_init( &ctx_ScpiParser.sExecution.xScpiErrorQueue,
  388. ctx_ScpiParser.sExecution.xScpiErrorLogStorage,
  389. sizeof(ctx_ScpiParser.sExecution.xScpiErrorLogStorage) );
  390. my_assert( errqrc ); (void)errqrc;
  391. // "5.8 Device Clear Requirements", [1]
  392. // In Device Clear event: DO NOT clear Error Queue and any bits other than clearing MAV bit in GPIB
  393. // Due to the 'reset_state' is executes on DEVICE CLEAR (INITIATE_CLEAR), it is impossible to
  394. // initialize GPIB in 'reset_state'.
  395. // So, the initialization is placed here:
  396. GPIB_Init();
  397. // startup the SCPI parser state machine
  398. fSeqStartup( &seqScpiProcessor, NULL );
  399. // ... It is requried to execute the ResetState job: initialize and
  400. // ... go next state
  401. fSeqDispatch( &seqScpiProcessor );
  402. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  403. }
  404. // -----------------------------------------------------------
  405. // @fSCPIReset
  406. // Reset parser
  407. static eScpiStatus_t fSCPIReset()
  408. {
  409. // shutdown the SCPI parser state machine
  410. fSeqShutdown( &seqScpiProcessor );
  411. // startup the SCPI parser state machine a reset state
  412. fSeqStartup( &seqScpiProcessor, fsq_GetStateById_unsafe(eParserResetState) );
  413. // process the state machine state:
  414. // ... It is requried to execute the ResetState job: initialize and
  415. // ... go next state
  416. fSeqDispatch( &seqScpiProcessor );
  417. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  418. }
  419. // -----------------------------------------------------------
  420. // @fSCPINewOutTransfer
  421. // New Transfer Event
  422. static eScpiStatus_t fSCPINewOutTransfer()
  423. {
  424. // prepare global context: set the buffer properties
  425. ctx_ScpiParser.sWrite.pData = NULL;
  426. ctx_ScpiParser.sWrite.nDataLength = NULL;
  427. ctx_ScpiParser.sMessage.w_bEndOfMessage = false;
  428. ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // IN-transfer is reset
  429. ctx_ScpiParser.sMessage.bQuery = false;
  430. ctx_ScpiParser.sMessage.pStr = NULL;
  431. ctx_ScpiParser.sMessage.bLeadResponseSep = false;
  432. ctx_ScpiParser.sRead.pBuffer = NULL;
  433. ctx_ScpiParser.sRead.pBufferIdx = NULL;
  434. ctx_ScpiParser.sRead.nDataLength = 0;
  435. ctx_ScpiParser.sRead.nBufferSize = 0;
  436. ctx_ScpiParser.sRead.nTotalLength = 0;
  437. ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
  438. //ctx_ScpiParser.sEvent.bNewTransfer = true; // pass New Transfer indicator
  439. ctx_ScpiParser.sEvent.eCode = eScpiEventRestart; // send RESTART event
  440. // process the state machine state
  441. fSeqDispatch( &seqScpiProcessor );
  442. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  443. }
  444. // -----------------------------------------------------------
  445. // @fSCPIWrite
  446. // Write parser event
  447. static eScpiStatus_t fSCPIWrite( const uint8_t * pData, size_t nDataLen, bool bEndOfMessage, bool bNewTransfer )
  448. {
  449. my_assert( pData );
  450. my_assert( nDataLen );
  451. // Bufferizing input command string:
  452. if( bNewTransfer )
  453. {
  454. // New transfer: cleanup cached input string and replace it with new one
  455. my_assert( nDataLen < sizeof(ctx_ScpiParser.sCache.inputCommand) );
  456. if( nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) )
  457. {
  458. return eScpiStatus_failed; // buffer overflow
  459. }
  460. memcpy( &ctx_ScpiParser.sCache.inputCommand[0], pData, nDataLen );
  461. ctx_ScpiParser.sCache.nBytes = nDataLen;
  462. }
  463. else
  464. {
  465. // Continue transfer: concatinate cached input string with new part
  466. if( ctx_ScpiParser.sCache.nBytes + nDataLen >= sizeof(ctx_ScpiParser.sCache.inputCommand) )
  467. {
  468. return eScpiStatus_failed; // buffer overflow
  469. }
  470. memcpy( &ctx_ScpiParser.sCache.inputCommand[ctx_ScpiParser.sCache.nBytes], pData, nDataLen );
  471. ctx_ScpiParser.sCache.nBytes += nDataLen;
  472. }
  473. // prepare global context: set the buffer properties
  474. ctx_ScpiParser.sWrite.pData = ctx_ScpiParser.sCache.inputCommand;
  475. ctx_ScpiParser.sWrite.nDataLength = ctx_ScpiParser.sCache.nBytes;
  476. ctx_ScpiParser.sMessage.w_bEndOfMessage = bEndOfMessage;
  477. (void)ctx_ScpiParser.sMessage.r_bEndOfMessage;
  478. ctx_ScpiParser.sMessage.bQuery = false;
  479. ctx_ScpiParser.sMessage.pStr = pData;
  480. ctx_ScpiParser.sMessage.pEnd = (pData + nDataLen);
  481. ctx_ScpiParser.sMessage.bLeadResponseSep = false;
  482. ctx_ScpiParser.sRead.pBuffer = NULL;
  483. ctx_ScpiParser.sRead.pBufferIdx = NULL;
  484. ctx_ScpiParser.sRead.nBufferSize = 0;
  485. ctx_ScpiParser.sRead.nDataLength = 0;
  486. ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
  487. (void)ctx_ScpiParser.sRead.nTotalLength;
  488. //ctx_ScpiParser.sEvent.bNewTransfer = bNewTransfer; // pass New Transfer indicator
  489. ctx_ScpiParser.sEvent.eCode = eScpiEventWrite; // send WRITE event
  490. // process the state machine state
  491. fSeqDispatch( &seqScpiProcessor );
  492. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  493. }
  494. // -----------------------------------------------------------
  495. // @fSCPIRead
  496. // Read parser event
  497. // @pBuffer - response buffer pointer
  498. // @nBufferLen - response buffer size
  499. // @pnBytesOut - pointer to the variable to store the value of written bytes to response buffer
  500. // @pEOM - pointer to the variable to store the End-Of-Message indicator. This indicator is set by
  501. // the SCPI library to notify the caller to specify SCPI End-of-message flag in Bulk-IN header that
  502. // is responsible for the stream control. If this flag is set, the Host must generate next READ event
  503. // even if the current buffer is not completely full. It is used together with complex command that
  504. // require several responses. Note: this value is only valid if the return value is not the error code.
  505. static eScpiStatus_t fSCPIRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, bool *pEOM )
  506. {
  507. my_assert( pBuffer );
  508. my_assert( nBufferLen );
  509. my_assert( pnBytesOut );
  510. // prepare global context: set the buffer properties
  511. (void)ctx_ScpiParser.sWrite.pData; // keep value
  512. (void)ctx_ScpiParser.sWrite.nDataLength; // keep value
  513. (void)ctx_ScpiParser.sMessage.w_bEndOfMessage; // keep value
  514. ctx_ScpiParser.sMessage.r_bEndOfMessage = false; // reset by default, need to set to interrupt reading
  515. (void)ctx_ScpiParser.sMessage.bQuery; // keep value
  516. (void)ctx_ScpiParser.sMessage.pStr; // keep value
  517. (void)ctx_ScpiParser.sMessage.pEnd; // keep value
  518. (void)ctx_ScpiParser.sMessage.bLeadResponseSep; // keep value
  519. ctx_ScpiParser.sRead.pBuffer = pBuffer;
  520. ctx_ScpiParser.sRead.pBufferIdx = pBuffer;
  521. ctx_ScpiParser.sRead.nBufferSize = nBufferLen;
  522. ctx_ScpiParser.sRead.nDataLength = 0;
  523. ctx_ScpiParser.sRead.vLastBufferIdx = pBuffer;
  524. (void)ctx_ScpiParser.sRead.nTotalLength;
  525. ctx_ScpiParser.sEvent.eCode = eScpiEventRead; // send READ event
  526. ctx_ScpiParser.sEvent.eStatus = eScpiStatus_failed; // forward set by default
  527. //ctx_ScpiParser.sEvent.bNewTransfer = false;
  528. // process the state machine state
  529. fSeqDispatch( &seqScpiProcessor );
  530. *pnBytesOut = ctx_ScpiParser.sRead.nDataLength; // @nDataLength shall be modified
  531. *pEOM = ctx_ScpiParser.sMessage.r_bEndOfMessage;
  532. #warning SCPI missing EOM bit set if TransferSize is full (return eScpiStatus_need_data)
  533. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  534. }
  535. // -----------------------------------------------------------
  536. // @fSCPINotificationRead
  537. // Read notification data (Interrupt-IN read)
  538. static eScpiStatus_t fSCPINotificationRead( uint8_t * pBuffer, size_t nBufferLen, size_t * pnBytesOut, uint8_t * pSTB )
  539. {
  540. my_assert( pBuffer );
  541. my_assert( nBufferLen );
  542. my_assert( pnBytesOut );
  543. // "11.2.2.1 Reading with a Serial Poll", [1]
  544. // "11.2.2.2 Reading With the *STB? Query", [1]
  545. GPIBMachine.fGPIB_get_status_byte_serial_poll( &ctx_ScpiParser.sGPIB.registers, pSTB );
  546. *pnBytesOut = 0; // no more bytes will be returned in notification, [2] 3.4.2 Interrupt-IN DATA sent due to READ_STATUS_BYTE request
  547. ctx_ScpiParser.sEvent.eStatus = eScpiStatus_success; // forward set by default
  548. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  549. }
  550. // -----------------------------------------------------------
  551. // @fSCPIError
  552. // Error notification from USBTMC level.
  553. // @error - error id, see @SCPILIB_ERROR_* macro group
  554. // @errorCtx - error dependent context, optional;
  555. // Return:
  556. // - eScpiStatus_success: error processed successfully
  557. // - eScpiStatus_failed: unrecoveral error, need to process it on USBTMC level.
  558. static eScpiStatus_t fSCPIError( int32_t error, const void * errorCtx )
  559. {
  560. // shall not be SCPILIB_ERROR_SUCCESS
  561. my_assert( error != SCPILIB_ERROR_SUCCESS );
  562. // prepare global context: set the buffer properties
  563. ctx_ScpiParser.sWrite.pData = NULL;
  564. ctx_ScpiParser.sWrite.nDataLength = 0;
  565. (void)ctx_ScpiParser.sMessage.w_bEndOfMessage;
  566. (void)ctx_ScpiParser.sMessage.r_bEndOfMessage;
  567. (void)ctx_ScpiParser.sMessage.bQuery;
  568. ctx_ScpiParser.sMessage.pStr = NULL;
  569. ctx_ScpiParser.sMessage.pEnd = NULL;
  570. (void)ctx_ScpiParser.sMessage.bLeadResponseSep;
  571. ctx_ScpiParser.sRead.pBuffer = NULL;
  572. ctx_ScpiParser.sRead.pBufferIdx = NULL;
  573. ctx_ScpiParser.sRead.nBufferSize = 0;
  574. ctx_ScpiParser.sRead.nDataLength = 0;
  575. ctx_ScpiParser.sRead.vLastBufferIdx = NULL;
  576. (void)ctx_ScpiParser.sRead.nTotalLength;
  577. ctx_ScpiParser.sEvent.eCode = eScpiEventError; // send ERROR event
  578. ctx_ScpiParser.sEvent.eStatus = eScpiStatus_invalid;
  579. switch( error )
  580. {
  581. case SCPILIB_ERROR_BUFFER_OVERFLOW:
  582. {
  583. fsq_RaiseError( SCPI_ERROR_INBUF_OVERFLOW, SCPI_ERROR_INBUF_OVERFLOW_MSG, NULL, 0 );
  584. }
  585. break;
  586. default: return eScpiStatus_invalid; // invalid error code, interrupt processing
  587. }
  588. // process the state machine state
  589. fSeqDispatch( &seqScpiProcessor );
  590. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  591. }
  592. // -----------------------------------------------------------
  593. // @fSCPIDeInit
  594. // Deinitialize parser
  595. static eScpiStatus_t fSCPIDeInit()
  596. {
  597. // prepare global context:
  598. ctx_ScpiParser.sEvent.eCode = eScpiEventEmpty; // no event occurred
  599. // shutdown the SCPI parser state machine
  600. fSeqShutdown( &seqScpiProcessor );
  601. return (eScpiStatus_t)ctx_ScpiParser.sEvent.eStatus;
  602. }