PIC24 Support Libraries
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 __PIC__
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 PIC, 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(__PIC__) || 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 
175  memset(xferVar, 0, sizeof(xferVar));
176  memset(au8_xferVarWriteable, 0, sizeof(au8_xferVarWriteable));
177 }
178 
180  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
182 }
183 
184 
186  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
187  (u_index != CHAR_RECEIVED_INDEX) && !B_IS_SPEC);
188 }
189 
190 
191 #if !defined(__PIC__) || defined(__DOXYGEN__)
193  return ( (receiveState == STATE_RECV_START) && (receiveError == ERR_NONE) &&
194  (u_index != CHAR_RECEIVED_INDEX) && B_IS_SPEC);
195 }
196 #endif
197 
198 
199 uint getVarIndex(char c_cmd) {
200  return ((unsigned char) c_cmd) >> VAR_SIZE_BITS;
201 }
202 
203 uint getVarLength(char c_cmd) {
204  return (c_cmd & VAR_SIZE_MASK) + 1;
205 }
206 
207 /** Check that the given index is valid. The index is specified by
208  * \ref u_index. On error, the \ref receiveError and \ref receiveState
209  * variables are updated.
210  * \return A pointer to the data corresponding to the index if the index
211  * is valid, or NULL if an error occurred.
212  */
214  uint8_t* pu8_data = NULL;
215 
216  // Check that the index exists
217  if (u_index >= NUM_XFER_VARS) {
220  return NULL;
221  }
222 
223  // Set up to read length bytes in during
224  // subsequent calls.
225  pu8_data = xferVar[u_index].pu8_data;
226  // If this index isn't configured, issue an error and abort
227  if (pu8_data == NULL) {
230  return NULL;
231  }
232 
233  // If this variable is read-only, issue an error and abort
234 #ifdef __PIC__
235  if (!isVarWriteable(u_index)) {
238  return NULL;
239  }
240 #endif
241 
242  // Indicate success by returning a pointer to the data
243  return pu8_data;
244 }
245 
246 void assignBit(uint u_index, BOOL b_bitVal) {
247  // Determine which byte this bit lives in
248  uint u_byteIndex = u_index / 8;
249  // Create a bit mask for this bit
250  uint8_t u8_mask = 1 << (u_index % 8);
251  // Set or clear it
252  if (b_bitVal)
253  au8_xferVarWriteable[u_byteIndex] |= u8_mask;
254  else
255  au8_xferVarWriteable[u_byteIndex] &= ~u8_mask;
256 }
257 
259  // Determine which byte this bit lives in
260  uint u_byteIndex = u_index / 8;
261  // Create a bit mask for this bit
262  uint8_t u8_mask = 1 << (u_index % 8);
263  // Read it
264  return (au8_xferVarWriteable[u_byteIndex] & u8_mask) != 0;
265 }
266 
267 /** Verify that the length of the variable matches the specified length.
268  * Otherwise, issue an error.
269  * On error, \ref receiveError and \ref receiveState are set.
270  * \param u_varLength The length of the variable, in bytes.
271  * \return TRUE on success, FALSE when the length does not match.
272  */
273 static BOOL validateLength(uint u_varLength) {
274  if (xferVar[u_index].u8_size != (u_varLength - 1)) {
277  return FALSE;
278  } else {
279  return TRUE;
280  }
281 }
282 
283 #ifndef __PIC__
284 /// Temporary storage for a variable spec being received, with
285 /// enough additional space to terminate 3 unterminated strings for safety.
286 static uint8_t au8_varSpecData[256 + 3];
287 
288 /// The length of the var spec data
290 
291 /** Parses a received variable spec, stored in \ref au8_varSpecData, assigns
292  * fields in \ref xferVar, and reports any errors.
293  */
294 static void parseVarSpec() {
295  uint u_size;
296  size_t st_len;
297  XFER_VAR* pXferVar = xferVar + u_index;
298  char* psz_s;
299 
300  // Free old memory associated with this variable
301  if (pXferVar->pu8_data != NULL) {
302  free(pXferVar->pu8_data);
303  free(pXferVar->psz_format);
304  free(pXferVar->psz_name);
305  free(pXferVar->psz_desc);
306  }
307 
308  // Determine the size of this variable then allocate it. Note that
309  // the size stored is the actual size minus one!
310  u_size = pXferVar->u8_size = au8_varSpecData[0];
311  u_size++;
312  pXferVar->pu8_data = (uint8_t*) malloc(sizeof(uint8_t)*u_size);
313 
314  // Force all three strings to be null-terminated.
315  ASSERT(u_specLength + 2 < sizeof(au8_varSpecData));
316  au8_varSpecData[u_specLength + 0] = 0;
317  au8_varSpecData[u_specLength + 1] = 0;
318  au8_varSpecData[u_specLength + 2] = 0;
319 
320  // Copy the format string of the variable
321  psz_s = (char*) au8_varSpecData + 1;
322  st_len = strlen(psz_s) + 1;
323  pXferVar->psz_format = (char*) malloc(sizeof(char)*st_len);
324  strcpy(pXferVar->psz_format, psz_s);
325  psz_s += st_len;
326  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
327 
328  // Copy the name string of the variable
329  st_len = strlen(psz_s) + 1;
330  pXferVar->psz_name = (char*) malloc(sizeof(char)*st_len);
331  strcpy(pXferVar->psz_name, psz_s);
332  psz_s += st_len;
333  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
334 
335  // Copy the description string of the variable
336  st_len = strlen(psz_s) + 1;
337  pXferVar->psz_desc = (char*) malloc(sizeof(char)*st_len);
338  strcpy(pXferVar->psz_desc, psz_s);
339  psz_s += st_len;
340  ASSERT(((uint8_t*) psz_s) < au8_varSpecData + sizeof(au8_varSpecData));
341 }
342 #endif
343 
346  // Global state transition: a timeout.
347  // Look for a timeout; they only occur during reception, not
348  // in the start state.
350  // Reset the machine, then signal the error (since errors
351  // are cleared during reset).
354  }
355  return receiveError;
356 }
357 
359  // Output of the command-finding state machine
361  // Bytes of a variable that still need to be read
362  static uint u_varLength;
363  // A pointer which variable data is read into
364  static uint8_t* pu8_data = NULL;
365 #ifndef __PIC__
366  // The last command received
367  static char c_lastCommand;
368 #endif
369 
370  // Global state transition: waiting for a char or command
371  // Run a char through the command state machine
372  cmdOutput = stepCommandFindMachine(c_inChar, &c_outChar);
373  // If we're waiting for another char, do that here instead of
374  // adding a wait case to every receive state below.
375  // If this is the start state (which signals data out is valid),
376  // move out of that start state.
377  switch (cmdOutput) {
378  case OUTPUT_CMD_NONE :
381  }
382  return receiveError;
383 
385  // Report the repeated command, then begin processing
386  // the second command as a (usual) command.
388  cmdOutput = OUTPUT_CMD_CMD;
389  break;
390 
392  // Report the repeated command, then wait
393  // for another character.
395  return receiveError;
396 
397  case OUTPUT_CMD_CMD :
398  // If we're not expecting a command, then signal an
399  // interrupted command then move the state machine to
400  // accept the command.
404  }
405  break;
406 
407  case OUTPUT_CMD_CHAR :
408  // These will be processed by the state machine below.
409  break;
410 
411  default :
412  ASSERT(FALSE);
413  break;
414  }
415 
416  // Process a command or a character using the remainer of the
417  // receive state machine.
418  switch (receiveState) {
419  case STATE_RECV_START:
420  switch (cmdOutput) {
421  case OUTPUT_CMD_CHAR :
422  // c_outChar is already assigned. Stay in this state.
423  // Reset the error on a valid received character, unless this
424  // this character caused a timeout.
425  if (receiveError != ERR_TIMEOUT)
428  break;
429 
430  default :
431  // Commands and wait shouldn't be possible; any other
432  // state is invalid.
433  ASSERT(FALSE);
434  break;
435  }
436  break;
437 
438  case STATE_RECV_CMD_WAIT :
439  switch (cmdOutput) {
440  case OUTPUT_CMD_CHAR :
441  // c_outChar is already assigned.
442  // Signal a character (the command token escaped) was received.
447  break;
448 
449  case OUTPUT_CMD_CMD :
450  // Decode this command
451  switch (c_outChar) {
452  case CMD_LONG_VAR :
454  break;
455 
456  case CMD_SEND_ONLY :
457  case CMD_SEND_RECEIVE_VAR :
458 #ifdef __PIC__
459  // The PIC should never receive a variable spec
462  break;
463 #else
464  // Record this command, for use in the next state.
465  c_lastCommand = c_outChar;
467  break;
468 #endif
469 
470  case ESCAPED_CMD :
471  // Should never reach this state.
472  ASSERT(FALSE);
473  break;
474 
475  default :
476  // It's not one of the special codes, so determine
477  // the variable number and length.
479  u_varLength = getVarLength(c_outChar);
480 #ifndef __PIC__
481  b_isSpec = FALSE;
482 #endif
483  // Validate the index, getting a pointer to the data
484  pu8_data = validateIndex();
485  // If the index is invalid, go no further.
486  if (pu8_data == NULL)
487  break;
488  // Check that the variable length matches
489  if (!validateLength(u_varLength))
490  break;
491  // All checks passed; move to the receive state to
492  // receive bytes.
494  break;
495  }
496  break;
497 
498  default :
499  ASSERT(FALSE);
500  break;
501  }
502  break;
503 
504  case STATE_RECV_READ_BYTES :
505  // Save data to variable
506  *pu8_data++ = c_outChar;
507  // When all data is saved, indicate that
508  // output is available
509  if (--u_varLength == 0) {
512 #ifndef __PIC__
513  if (b_isSpec)
514  parseVarSpec();
515 #endif
516  }
517  break;
518 
519  case STATE_RECV_LONG_INDEX :
520  // Save the index of this variable
521  u_index = c_outChar;
522 #ifndef __PIC__
523  b_isSpec = FALSE;
524 #endif
525  // Validate the index, getting a pointer to the data
526  pu8_data = validateIndex();
527  // If the index is invalid, go no further.
528  if (pu8_data == NULL)
529  break;
530  // Read the variable's length next
532  break;
533 
534 #ifndef __PIC__
535  case STATE_RECV_SPEC_INDEX :
536  // Save the index of this variable
537  u_index = c_outChar;
538  b_isSpec = TRUE;
539  // Point to the var spec temp buffer
540  pu8_data = au8_varSpecData;
541  // Record the type of this variable: send/receive or just send
542  ASSERT( (c_lastCommand == CMD_SEND_ONLY) ||
543  (c_lastCommand == CMD_SEND_RECEIVE_VAR) );
544  assignBit(u_index, c_lastCommand == CMD_SEND_RECEIVE_VAR);
545  // Read the variable's length next
547  break;
548 #endif
549 
551  // Receive and record the length.
552  // Subtle bug: the statement
553  // u_varLength = ((uint) c_outChar) + 1;
554  // doesn't work: C first converts c_outChar to a int, then from that to
555  // a uint. Therefore, if c_outChar < 0, sign extension occurs, which is
556  // incorrect. The syntax below first converts to a uint8_t then grows that
557  // to a uint to guarantee zero extension rather than sign extension.
558  u_varLength = ((uint) ((uint8_t) c_outChar)) + 1;
559  // Validate it only if this isn't a variable spec
560  if (!B_IS_SPEC && !validateLength(u_varLength))
561  break;
562 #ifndef __PIC__
563  else
564  // Otherwise, record the number of bytes in the spec for later processing
565  // in parseVarSpec.
566  u_specLength = u_varLength;
567 #endif
568  // Read the data
570  break;
571 
572  default:
573  ASSERT(FALSE);
574  break;
575  }
576 
577  return receiveError;
578 }
579 //@}
580 
581 
582 /// Strings which provide a user-readable version of
583 /// the error codes in \ref RECEIVE_ERROR.
584 static const char* apsz_errorDesc[NUM_ERROR_CODES] = {
585  "no error",
586  "repeated command",
587  "timeout",
588  "interrupted command",
589  "unspecified index",
590  "index too high",
591  "variable size mismatch",
592  "read only variable",
593  "illegal variable specification",
594 };
595 
596 const char* getReceiveErrorString() {
599 }