PIC24 Support Libraries
dataXferImpl.c
Go to the documentation of this file.
1 #include "dataXferImpl.h"
2 #include <string.h>
3 
4 /** \file
5  * \brief Implementation of the \ref index "uC data transfer protocol".
6  */
7 
8 /// \name Command-finding state machine
9 //@{
10 
11 /// The current state of the command-finding state machine.
13 
14 /// The current output of the command-finding state machine
16 
20 }
21 
23  switch (cmdState) {
24  case STATE_CMD_START :
25  if (c_inChar != CMD_TOKEN) {
26  // This is just a character, not a command.
27  *c_outChar = c_inChar;
29  } else {
32  }
33  break;
34 
35  // We're waiting for a command or an escaped command.
36  case STATE_CMD_WAIT1 :
37  switch (c_inChar) {
38  case CMD_TOKEN :
39  // The sequence CMD_TOKEN CMD_TOKEN.
41  break;
42 
43  case ESCAPED_CMD :
44  // This was an escaped CMD_TOKEN char.
45  *c_outChar = CMD_TOKEN;
48  break;
49 
50  default :
51  // This is a command
52  *c_outChar = c_inChar;
55  break;
56  }
57  break;
58 
59  case STATE_CMD_WAIT2 :
60  switch (c_inChar) {
61  case ESCAPED_CMD :
62  // The sequence CMD_TOKEN CMD_TOKEN ESCAPED_CMD, which
63  // is the command CMD_TOKEN
66  *c_outChar = CMD_TOKEN;
67  break;
68 
69  case CMD_TOKEN :
70  // The sequence CMD_TOKEN CMD_TOKEN CMD_TOKEN, which
71  // is a repeated command (the first CMD_TOKEN) followed
72  // by CMD_TOKEN CMD_TOKEN (so remain in this state to
73  // decode it based on the next incoming char).
75  break;
76 
77  default :
78  // The sequence CMD_TOKEN CMD_TOKEN c,
79  // which is a repeated command c.
80  *c_outChar = c_inChar;
83  break;
84 
85  }
86  break;
87 
88  default:
89  // Shouldn't happen
90  ASSERT(FALSE);
91  break;
92  }
93 
94  return cmdOutput;
95 }
96 //@}
97 
98 
99 
102 
103 
104 /// \name Receive state machine
105 //@{
106 
107 /// Current state of the receive state machine.
109 
110 /// A character output by the receive state machine. See
111 /// \ref stepReceiveMachine for more information.
112 static char c_outChar;
113 
114 /// The index of a data item; updated by the receive state
115 /// machine, or \ref CHAR_RECEIVED_INDEX if a character was received.
116 /// See \ref stepReceiveMachine for more information.
117 static uint u_index;
118 
119 /// An error code produced by the receive
120 /// state machine. See \ref stepReceiveMachine for more information.
122 
123 #ifndef MICROCONTROLLER
124 /// True if the data just received by the receive state machine
125 /// was a specification. See \ref stepReceiveMachine for more information.
126 static BOOL b_isSpec;
127 #define B_IS_SPEC b_isSpec
128 #else
129 // For the microcontroller, we never have a spec.
130 #define B_IS_SPEC 0
131 #endif
132 
134  return receiveState;
135 }
136 
138  return c_outChar;
139 }
140 
142  return u_index;
143 }
144 
145 
149  return re;
150 }
151 
152 
153 #if !defined(MICROCONTROLLER) || defined(__DOXYGEN__)
155  return b_isSpec;
156 }
157 #endif
158 
159 
163  // Since this state machine depends on the command-finding machine
164  // reset it also.
166 }
167 
168 
171 }
172 
173 #ifndef MICROCONTROLLER
174 /** Free memory associted with a variable.
175  *
176  * \param u_index A value from 0-\ref NUM_XFER_VARS, unique for each var.
177  */
178 static void freeVariable(uint u_index) {
179  if (xferVar[u_index].pu8_data != NULL) {
180  free(xferVar[u_index].pu8_data);
181  free(xferVar[u_index].psz_format);
182  free(xferVar[u_index].psz_name);
183  free(xferVar[u_index].psz_desc);
184  }
185 }
186 #endif
187 
189 #ifndef MICROCONTROLLER
190  // Free all memory allocated for receive variables.
191  uint u_index;
192  for (u_index = 0; u_index < NUM_XFER_VARS; ++u_index) {
193  freeVariable(u_index);
194  }
195 #endif
196  memset(xferVar, 0, sizeof(xferVar));
197  memset(au8_xferVarWriteable, 0, sizeof(au8_xferVarWriteable));
198 }
199 
201  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
203 }
204 
205 
207  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
208  (u_index != CHAR_RECEIVED_INDEX) && !B_IS_SPEC);
209 }
210 
211 
212 #if !defined(MICROCONTROLLER) || defined(__DOXYGEN__)
214  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
215  (u_index != CHAR_RECEIVED_INDEX) && B_IS_SPEC);
216 }
217 #endif
218 
219 
220 uint getVarIndex(char c_cmd) {
221  return ((unsigned char) c_cmd) >> VAR_SIZE_BITS;
222 }
223 
224 uint getVarLength(char c_cmd) {
225  return (c_cmd & VAR_SIZE_MASK) + 1;
226 }
227 
228 /** Check that the given index is valid. The index is specified by
229  * \ref u_index. On error, the \ref receiveError and \ref receiveState
230  * variables are updated.
231  * \return A pointer to the data corresponding to the index if the index
232  * is valid, or NULL if an error occurred.
233  */
235  uint8_t* pu8_data = NULL;
236 
237  // Check that the index exists
238  if (u_index >= NUM_XFER_VARS) {
241  return NULL;
242  }
243 
244  // Set up to read length bytes in during
245  // subsequent calls.
246  pu8_data = xferVar[u_index].pu8_data;
247  // If this index isn't configured, issue an error and abort
248  if (pu8_data == NULL) {
251  return NULL;
252  }
253 
254  // If this variable is read-only, issue an error and abort
255 #ifdef MICROCONTROLLER
256  if (!isVarWriteable(u_index)) {
259  return NULL;
260  }
261 #endif
262 
263  // Indicate success by returning a pointer to the data
264  return pu8_data;
265 }
266 
267 void assignBit(uint u_index, BOOL b_bitVal) {
268  // Determine which byte this bit lives in
269  uint u_byteIndex = u_index / 8;
270  // Create a bit mask for this bit
271  uint8_t u8_mask = 1 << (u_index % 8);
272  // Set or clear it
273  if (b_bitVal)
274  au8_xferVarWriteable[u_byteIndex] |= u8_mask;
275  else
276  au8_xferVarWriteable[u_byteIndex] &= ~u8_mask;
277 }
278 
280  // Determine which byte this bit lives in
281  uint u_byteIndex = u_index / 8;
282  // Create a bit mask for this bit
283  uint8_t u8_mask = 1 << (u_index % 8);
284  // Read it
285  return (au8_xferVarWriteable[u_byteIndex] & u8_mask) != 0;
286 }
287 
288 /** Verify that the length of the variable matches the specified length.
289  * Otherwise, issue an error.
290  * On error, \ref receiveError and \ref receiveState are set.
291  * \param u_varLength The length of the variable, in bytes.
292  * \return TRUE on success, FALSE when the length does not match.
293  */
294 static BOOL validateLength(uint u_varLength) {
295  if (xferVar[u_index].u8_size != (u_varLength - 1)) {
298  return FALSE;
299  } else {
300  return TRUE;
301  }
302 }
303 
304 #ifndef MICROCONTROLLER
305 /// Temporary storage for a variable spec being received, with
306 /// enough additional space to terminate 3 unterminated strings for safety.
307 static uint8_t au8_varSpecData[(UINT8_MAX + 1) + 3];
308 
309 /// The length of the var spec data
311 
312 /** Parses a received variable spec, stored in \ref au8_varSpecData, assigns
313  * fields in \ref xferVar, and reports any errors.
314  */
315 static void parseVarSpec() {
316  uint u_size;
317  size_t st_len;
318  XFER_VAR* pXferVar = xferVar + u_index;
319  char* psz_s;
320 
321  // Free old memory associated with this variable
322  freeVariable(u_index);
323 
324  // Determine the size of this variable then allocate it. Note that
325  // the size stored is the actual size minus one!
326  u_size = pXferVar->u8_size = au8_varSpecData[0];
327  u_size++;
328  pXferVar->pu8_data = (uint8_t*) malloc(sizeof(uint8_t)*u_size);
329 
330  // Force all three strings to be null-terminated.
331  ASSERT(u_specLength + 2 < sizeof(au8_varSpecData));
332  au8_varSpecData[u_specLength + 0] = 0;
333  au8_varSpecData[u_specLength + 1] = 0;
334  au8_varSpecData[u_specLength + 2] = 0;
335 
336  // Copy the format string of the variable
337  psz_s = (char*) au8_varSpecData + 1;
338  st_len = strlen(psz_s) + 1;
339  pXferVar->psz_format = (char*) malloc(sizeof(char)*st_len);
340  strcpy(pXferVar->psz_format, psz_s);
341  psz_s += st_len;
342  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
343 
344  // Copy the name string of the variable
345  st_len = strlen(psz_s) + 1;
346  pXferVar->psz_name = (char*) malloc(sizeof(char)*st_len);
347  strcpy(pXferVar->psz_name, psz_s);
348  psz_s += st_len;
349  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
350 
351  // Copy the description string of the variable
352  st_len = strlen(psz_s) + 1;
353  pXferVar->psz_desc = (char*) malloc(sizeof(char)*st_len);
354  strcpy(pXferVar->psz_desc, psz_s);
355  psz_s += st_len;
356  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
357 }
358 #endif
359 
362  // Global state transition: a timeout.
363  // Look for a timeout; they only occur during reception, not
364  // in the start state.
366  // Reset the machine, then signal the error (since errors
367  // are cleared during reset).
370  }
371  return receiveError;
372 }
373 
375  // Output of the command-finding state machine
377  // Bytes of a variable that still need to be read
378  static uint u_varLength;
379  // A pointer which variable data is read into
380  static uint8_t* pu8_data = NULL;
381 #ifndef MICROCONTROLLER
382  // The last command received
383  static char c_lastCommand;
384 #endif
385 
386  // Global state transition: waiting for a char or command
387  // Run a char through the command state machine
388  cmdOutput = stepCommandFindMachine(c_inChar, &c_outChar);
389  // If we're waiting for another char, do that here instead of
390  // adding a wait case to every receive state below.
391  // If this is the start state (which signals data out is valid),
392  // move out of that start state.
393  switch (cmdOutput) {
394  case OUTPUT_CMD_NONE :
397  }
398  return receiveError;
399 
401  // Report the repeated command, then begin processing
402  // the second command as a (usual) command.
404  cmdOutput = OUTPUT_CMD_CMD;
405  break;
406 
408  // Report the repeated command, then wait
409  // for another character.
411  return receiveError;
412 
413  case OUTPUT_CMD_CMD :
414  // If we're not expecting a command, then signal an
415  // interrupted command then move the state machine to
416  // accept the command.
420  }
421  break;
422 
423  case OUTPUT_CMD_CHAR :
424  // These will be processed by the state machine below.
425  break;
426 
427  default :
428  ASSERT(FALSE);
429  break;
430  }
431 
432  // Process a command or a character using the remainer of the
433  // receive state machine.
434  switch (receiveState) {
435  case STATE_RECV_START:
436  switch (cmdOutput) {
437  case OUTPUT_CMD_CHAR :
438  // c_outChar is already assigned. Stay in this state.
439  // Reset the error on a valid received character, unless this
440  // this character caused a timeout.
441  if (receiveError != ERR_TIMEOUT)
444  break;
445 
446  default :
447  // Commands and wait shouldn't be possible; any other
448  // state is invalid.
449  ASSERT(FALSE);
450  break;
451  }
452  break;
453 
454  case STATE_RECV_CMD_WAIT :
455  switch (cmdOutput) {
456  case OUTPUT_CMD_CHAR :
457  // c_outChar is already assigned.
458  // Signal a character (the command token escaped) was received.
463  break;
464 
465  case OUTPUT_CMD_CMD :
466  // Decode this command
467  switch (c_outChar) {
468  case CMD_LONG_VAR :
470  break;
471 
472  case CMD_SEND_ONLY :
473  case CMD_SEND_RECEIVE_VAR :
474 #ifdef MICROCONTROLLER
475  // The microcontroller should never receive a variable spec
478  break;
479 #else
480  // Record this command, for use in the next state.
481  c_lastCommand = c_outChar;
483  break;
484 #endif
485 
486  case ESCAPED_CMD :
487  // Should never reach this state.
488  ASSERT(FALSE);
489  break;
490 
491  default :
492  // It's not one of the special codes, so determine
493  // the variable number and length.
495  u_varLength = getVarLength(c_outChar);
496 #ifndef MICROCONTROLLER
497  b_isSpec = FALSE;
498 #endif
499  // Validate the index, getting a pointer to the data
500  pu8_data = validateIndex();
501  // If the index is invalid, go no further.
502  if (pu8_data == NULL)
503  break;
504  // Check that the variable length matches
505  if (!validateLength(u_varLength))
506  break;
507  // All checks passed; move to the receive state to
508  // receive bytes.
510  break;
511  }
512  break;
513 
514  default :
515  ASSERT(FALSE);
516  break;
517  }
518  break;
519 
520  case STATE_RECV_READ_BYTES :
521  // Save data to variable
522  *pu8_data++ = c_outChar;
523  // When all data is saved, indicate that
524  // output is available
525  if (--u_varLength == 0) {
528 #ifndef MICROCONTROLLER
529  if (b_isSpec)
530  parseVarSpec();
531 #endif
532  }
533  break;
534 
535  case STATE_RECV_LONG_INDEX :
536  // Save the index of this variable
537  u_index = c_outChar;
538 #ifndef MICROCONTROLLER
539  b_isSpec = FALSE;
540 #endif
541  // Validate the index, getting a pointer to the data
542  pu8_data = validateIndex();
543  // If the index is invalid, go no further.
544  if (pu8_data == NULL)
545  break;
546  // Read the variable's length next
548  break;
549 
550 #ifndef MICROCONTROLLER
551  case STATE_RECV_SPEC_INDEX :
552  // Save the index of this variable
553  u_index = c_outChar;
554  b_isSpec = TRUE;
555  // Point to the var spec temp buffer
556  pu8_data = au8_varSpecData;
557  // Record the type of this variable: send/receive or just send
558  ASSERT( (c_lastCommand == CMD_SEND_ONLY) ||
559  (c_lastCommand == CMD_SEND_RECEIVE_VAR) );
560  assignBit(u_index, c_lastCommand == CMD_SEND_RECEIVE_VAR);
561  // Read the variable's length next
563  break;
564 #endif
565 
567  // Receive and record the length.
568  // Subtle bug: the statement
569  // u_varLength = ((uint) c_outChar) + 1;
570  // doesn't work: C first converts c_outChar to a int, then from that to
571  // a uint. Therefore, if c_outChar < 0, sign extension occurs, which is
572  // incorrect. The syntax below first converts to a uint8_t then grows that
573  // to a uint to guarantee zero extension rather than sign extension.
574  u_varLength = ((uint) ((uint8_t) c_outChar)) + 1;
575  // Validate it only if this isn't a variable spec
576  if (!B_IS_SPEC && !validateLength(u_varLength))
577  break;
578 #ifndef MICROCONTROLLER
579  else
580  // Otherwise, record the number of bytes in the spec for later processing
581  // in parseVarSpec.
582  u_specLength = u_varLength;
583 #endif
584  // Read the data
586  break;
587 
588  default:
589  ASSERT(FALSE);
590  break;
591  }
592 
593  return receiveError;
594 }
595 //@}
596 
597 
598 /// Strings which provide a user-readable version of
599 /// the error codes in \ref RECEIVE_ERROR.
600 static const char* apsz_errorDesc[NUM_ERROR_CODES] = {
601  "no error",
602  "repeated command",
603  "timeout",
604  "interrupted command",
605  "unspecified index",
606  "index too high",
607  "variable size mismatch",
608  "read only variable",
609  "illegal variable specification",
610 };
611 
612 const char* getReceiveErrorString() {
615 }
The machine is waiting for an additional character; c_outChar is not valid.
Definition: dataXferImpl.h:242
RECEIVE_ERROR getReceiveMachineError()
Definition: dataXferImpl.c:146
The machine is waiting for another character; c_outChar is not valid.
Definition: dataXferImpl.h:240
void resetCommandFindMachine()
Definition: dataXferImpl.c:17
void clearReceiveMachineError()
Definition: dataXferImpl.c:169
#define VAR_SIZE_MASK
A mask which removes all but the variable size bits in the varBits field.
Definition: dataXferImpl.h:228
Reading the variable index for a long var command.
Definition: dataXferImpl.h:370
static uint u_specLength
The length of the var spec data.
Definition: dataXferImpl.c:310
unsigned int uint
An abbreviation for an unsigned integer.
Definition: dataXferImpl.h:190
void resetReceiveMachine()
Definition: dataXferImpl.c:160
#define NUM_XFER_VARS
Definition: dataXferImpl.h:335
A repeated command was received; c_outChar contains the command.
Definition: dataXferImpl.h:255
A character was received; c_outChar contains the character.
Definition: dataXferImpl.h:251
Reading the variable length for a long/specification command.
Definition: dataXferImpl.h:372
static BOOL validateLength(uint u_varLength)
Definition: dataXferImpl.c:294
CMD_STATE
Definition: dataXferImpl.h:236
#define CMD_SEND_RECEIVE_VAR
Definition: dataXferImpl.h:221
static void freeVariable(uint u_index)
Definition: dataXferImpl.c:178
#define VAR_SIZE_BITS
Definition: dataXferImpl.h:225
char * psz_name
Definition: dataXferImpl.h:324
static RECEIVE_ERROR receiveError
Definition: dataXferImpl.c:121
BOOL
Definition: all_generic.h:402
RECEIVE_STATE getReceiveMachineState()
Definition: dataXferImpl.c:133
static RECEIVE_STATE receiveState
Current state of the receive state machine.
Definition: dataXferImpl.c:108
static char c_outChar
Definition: dataXferImpl.c:112
static uint8_t * validateIndex()
Definition: dataXferImpl.c:234
const char * getReceiveErrorString()
Returns an error string matching the last error code.
Definition: dataXferImpl.c:612
CMD_OUTPUT
Definition: dataXferImpl.h:247
RECEIVE_STATE
Definition: dataXferImpl.h:362
static uint8_t au8_varSpecData[(UINT8_MAX+1)+3]
Definition: dataXferImpl.c:307
char * psz_desc
Description of this variable. PC only.
Definition: dataXferImpl.h:326
Reading data bytes in from a command.
Definition: dataXferImpl.h:368
uint8_t au8_xferVarWriteable[NUM_XFER_VARS/8+((NUM_XFER_VARS % 8) > 0)]
Definition: dataXferImpl.c:101
static CMD_STATE cmdState
The current state of the command-finding state machine.
Definition: dataXferImpl.c:12
At the start of the machine.
Definition: dataXferImpl.h:364
uint getReceiveMachineIndex()
Definition: dataXferImpl.c:141
XFER_VAR xferVar[NUM_XFER_VARS]
A table to hold the state of transfer variables.
Definition: dataXferImpl.c:100
static uint u_index
Definition: dataXferImpl.c:117
CMD_OUTPUT stepCommandFindMachine(char c_inChar, char *c_outChar)
Definition: dataXferImpl.c:22
void clearReceiveStruct()
Definition: dataXferImpl.c:188
#define CMD_TOKEN
Definition: dataXferImpl.h:205
BOOL isReceiveMachineData()
Definition: dataXferImpl.c:206
char * psz_format
printf format string to use in displaying the variable. PC only.
Definition: dataXferImpl.h:321
The microcontroller is sent a variable specification.
Definition: dataXferImpl.h:408
BOOL isReceiveMachineChar()
Definition: dataXferImpl.c:200
The state machine produced no output, but is waiting for additional input.
Definition: dataXferImpl.h:249
void assignBit(uint u_index, BOOL b_bitVal)
Definition: dataXferImpl.c:267
The destination variable is read-only.
Definition: dataXferImpl.h:406
static void parseVarSpec()
Definition: dataXferImpl.c:315
RECEIVE_ERROR
Definition: dataXferImpl.h:381
Reading the variable index for a specification command.
Definition: dataXferImpl.h:374
uint8_t * pu8_data
Definition: dataXferImpl.h:316
RECEIVE_ERROR stepReceiveMachine(char c_inChar)
Definition: dataXferImpl.c:374
#define ASSERT(test)
A command was received; c_outChar contains the command.
Definition: dataXferImpl.h:253
#define CHAR_RECEIVED_INDEX
Definition: dataXferImpl.h:358
Waiting for a command or escaped CMD_TOKEN.
Definition: dataXferImpl.h:366
BOOL isReceiveMachineSpec()
Definition: dataXferImpl.c:213
RECEIVE_ERROR notifyOfTimeout()
Definition: dataXferImpl.c:361
static CMD_OUTPUT cmdOutput
The current output of the command-finding state machine.
Definition: dataXferImpl.c:15
static BOOL b_isSpec
Definition: dataXferImpl.c:126
#define NUM_ERROR_CODES
Number of error codes in the RECEIVE_ERROR enum.
Definition: dataXferImpl.h:412
BOOL getReceiveMachineIsSpec()
Definition: dataXferImpl.c:154
#define ESCAPED_CMD
Definition: dataXferImpl.h:209
unsigned char uint8_t
An abbreviation for an 8-bit unsigned integer.
Definition: dataXferImpl.h:194
Routines which implement the uC comm protocol.
uint8_t u8_size
Size of data in bytes - 1: 0 = 1 byte, etc.
Definition: dataXferImpl.h:318
static const char * apsz_errorDesc[NUM_ERROR_CODES]
Definition: dataXferImpl.c:600
#define CMD_LONG_VAR
Definition: dataXferImpl.h:213
char getReceiveMachineOutChar()
Definition: dataXferImpl.c:137
#define CMD_SEND_ONLY
Definition: dataXferImpl.h:217
The machine is in its starting state.
Definition: dataXferImpl.h:238
BOOL isVarWriteable(uint u_index)
Definition: dataXferImpl.c:279
uint getVarIndex(char c_cmd)
Definition: dataXferImpl.c:220
uint getVarLength(char c_cmd)
Definition: dataXferImpl.c:224