FreeEMS  0.2.0-SNAPSHOT-282-g9efc524
commsCore.c
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2008-2014 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
7  * FreeEMS software is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * FreeEMS software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
19  *
20  * We ask that if you make any changes to this file you email them upstream to
21  * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @ingroup communicationsFiles
30  *
31  * @brief Core communications functions.
32  *
33  * This file contains most of the core comms functionality. Currently that is
34  * only for UART serial style communication. It is already too big and needs
35  * to be split up somewhat. This will happen fairly soon during the serial
36  * refactoring and protocol fine tuning.
37  *
38  * @todo TODO function to setup a packet and send it fn(populateBodyFunctionPointer(), header, other, fields, here, and, use, or, not, within){}
39  * @todo TODO factor many things into functions and move the receive delegator to its own file
40  */
41 
42 
43 #define COMMSCORE_C
44 #include "inc/freeEMS.h"
45 #include "inc/flashWrite.h"
46 #include "inc/interrupts.h"
47 #include "inc/utils.h"
48 #include "inc/tableLookup.h"
49 #include "inc/locationIDs.h"
50 #include "inc/blockDetailsLookup.h"
51 #include "inc/decoderInterface.h"
52 #include "inc/commsCore.h"
53 #include "inc/init.h"
54 #include <string.h> /// @todo TODO this is pulling in the system string.h not the m68hc1x version, and functions other than memcpy do not work because they are not in crt1.o or other included-by-default libs
55 #include "decoders/inc/BenchTest.h"
56 
57 
58 /** @brief Populate a basic datalog packet
59  *
60  * Copies various chunks of data to the transmission buffer and truncates to
61  * the configured length. If changing this, update the maxBasicDatalogLength.
62  */
63 unsigned short populateBasicDatalog(){
64  /// @todo TODO setup proper sequence and clock with some sort of differential measurement log to log. insert in front of actual data because these are part of the log itself.
65 
69 
70  unsigned short confSize = 0;
71  unsigned char chunkLimit = TablesB.SmallTablesB.loggingSettings.firstChunk + TablesB.SmallTablesB.loggingSettings.numberOfChunks;
72  unsigned char chunks;
73  for(chunks=TablesB.SmallTablesB.loggingSettings.firstChunk;chunks<chunkLimit;chunks++){
74  unsigned short localSize = TablesB.SmallTablesB.loggingSettings.logChunks[chunks].size;
75  confSize += localSize;
76  if(confSize > 2048){
77  confSize -= localSize;
78  break;
79  }
80  memcpy(TXBufferCurrentPositionHandler, TablesB.SmallTablesB.loggingSettings.logChunks[chunks].address, localSize);
81  TXBufferCurrentPositionHandler += localSize;
82  }
83  // After copying data, otherwise tempClock is NEVER zero and reset detection does NOT work
85  return confSize;
86 }
87 
88 
89 // All of these require some range checking, eg only some registers, and all RAM, not flash, not other regs
90 // TODO pointer for one byte
91 // TODO pointer for one short
92 // TODO function to log generic memory region by location and size ? requires length!
93 // Ranges are :
94 // RAM window
95 // bss/data region
96 // IO registers etc that can't be altered simply by reading from.
97 // NOT :
98 // flash makes no sense
99 // some regs are sensitive
100 // some RAM is unused
101 // serial buffers make no sense
102 // eeprom makes no sense
103 //
104 // 2k of regs max - user beware for now
105 // 12k of RAM max
106 //
107 //init :
108 //logaddr = fixed.addr
109 //loglen = fixed.len
110 //
111 //len = loglen OR 1 OR 2
112 //
113 //check :
114 //if((addr < 0x0800) && (length < (0x0800 - addr))){
115 // // reg space is OK
116 //}else if(((0x1000 < addr) && (addr < 0x4000)) && (length < (0x4000 - addr))){
117 // // RAM space is OK
118 //}else{
119 // // send an error instead
120 //}
121 //
122 //run check at init and set time, not run time or just not check?? maybe its silly to check at all
123 //
124 // /* Just dump the ADC channels as fast as possible */
125 //void populateScopeLogADCAll(){
126 // sampleBlockADC(TXBufferCurrentPositionHandler);
127 // TXBufferCurrentPositionHandler += sizeof(ADCBuffer);
128 //}
129 
130 
131 // what does this mean >> ??? TODO Look at the time stamps and where to write them, also whether to function call these simple blocks or write one function that handles all the logic.
132 
133 
134 /** @brief Finalise a packet and send it
135  *
136  * This functions job is to finalise the main loop part of the packet sending
137  * process. It configures the pos/neg ack header bit, adds the code if neg,
138  * runs a checksum over the packet data and tags it to the end before
139  * configuring the various ISRs that need to send the data out.
140  */
141 void finaliseAndSend(unsigned short errorID){
142 
143  if(errorID != 0){
145  *((unsigned short*)TXBufferCurrentPositionHandler) = errorID;
147  }
148 
149  /* Tag the checksum on the end */
150  *TXBufferCurrentPositionHandler = checksum((unsigned char*)&TXBuffer, ((unsigned short)TXBufferCurrentPositionHandler - (unsigned short)&TXBuffer));
151 
152  /* Send it out on all the channels required. */
153 
154  /* SCI0 - Main serial interface */
156  /* Initiate transmission */
158 
159  /* Note : Order Is Important! */
160  /* TX empty flag is already set, so we must clear it by writing out before enabling the interrupt */
162  }
163  /* CAN0 - Main CAN interface */
165  // just clear up front for now
167  }
168  /* spare2 */
170  // just clear up front for now
172  }
173  /* spare3 */
175  // just clear up front for now
177  }
178  /* spare4 */
180  // just clear up front for now
182  }
183  /* spare5 */
185  // just clear up front for now
187  }
188  /* spare6 */
190  // just clear up front for now
192  }
193  /* spare7 */
195  // just clear up front for now
197  }
198 }
199 
200 
201 /** @brief Decode a packet and respond
202  *
203  * This is the core function that controls which functionality is run when a
204  * packet is received in full by the ISR code and control is passed back to the
205  * main loop code. The vast majority of communications action happens here.
206  */
208  /* Extract and build up the header fields */
209  TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
210 
211  /* Initialised here such that override is possible */
212  TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
213  TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
214 
215  // How big was the packet that we got back
216  unsigned short RXPacketLengthReceived = (unsigned short)RXBufferCurrentPosition - (unsigned short)&RXBuffer;
217 
218  /* Check that the packet is big enough for header,ID,checksum */
219  if(RXPacketLengthReceived < 4){
223  return;
224  }
225 
226  /* Pull out the received checksum and calculate the real one, then check */
227  unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1);
228  unsigned char RXCalculatedChecksum = checksum((unsigned char*)&RXBuffer, RXPacketLengthReceived - 1);
229  if(RXCalculatedChecksum != RXReceivedChecksum){
233  return;
234  }
235 
236  /* Start this off as full packet length and build down to the actual length */
237  RXCalculatedPayloadLength = RXPacketLengthReceived;
238 
239  /* Grab the RX header flags out of the RX buffer */
240  RXBufferCurrentPosition = (unsigned char*)&RXBuffer;
244 
245  /* Flag that we are transmitting! */
247  // SCI0 only for now...
248 
249  /* Load a blank header into the TX buffer ready for masking */
251  *TXHeaderFlags = 0;
253 
254  /* Grab the payload ID for processing and load the return ID */
255  RXHeaderPayloadID = *((unsigned short*)RXBufferCurrentPosition);
256  *((unsigned short*)TXBufferCurrentPositionHandler) = RXHeaderPayloadID + 1;
260 
261  /* Check that the length is sufficient for the fields configured. Packets
262  * that are too long will be caught and rejected on an individual payload
263  * ID basis as the information required to handle that is not available at
264  * this point. Packets that are too short are rejected immediately!
265  */
266  if(((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 7))
267  || ((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXPacketLengthReceived < 6))
268  || ((RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 5))){
271  return;
272  }
273 
274  /* Subtract checksum to get final length */
276 
283  }
284 
285  if(RXHeaderFlags & HEADER_HAS_LENGTH){
286  RXHeaderPayloadLength = *((unsigned short*)RXBufferCurrentPosition);
289  /* Already subtracted one for checksum */
293  return;
294  }
295  }
296 
297  /* Calculate the position of the end of the stored packet for later use as a buffer */
298  void* leftOverBuffer = (void*)((unsigned short)&RXBuffer + RXPacketLengthReceived);
299 
300  unsigned short errorID = 0;
301  /* This is where all the communication logic resides.
302  *
303  * Please Note: Length and its flag should be set by each return packet
304  * type handler if required or desired. If an ack has been requested,
305  * ensure the negative ack flag is set if the operation failed.
306  */
307  switch (RXHeaderPayloadID){
308  // FreeEMS Core Comms Interface cases
310  {
311  if(RXCalculatedPayloadLength != 0){
312  errorID = payloadLengthTypeMismatch;
313  break;
314  }
315 
316  /* This type must have a length field, set that up */
317  *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersion);
320  /* Load the body into place */
321  memcpy((void*)TXBufferCurrentPositionHandler, (void*)&interfaceVersion, sizeof(interfaceVersion));
323  break;
324  }
326  {
327  if(RXCalculatedPayloadLength != 0){
328  errorID = payloadLengthTypeMismatch;
329  break;
330  }
331  /* This type must have a length field, set that up */
332  *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion);
335  /* Load the body into place */
336  memcpy((void*)TXBufferCurrentPositionHandler, (void*)&firmwareVersion, sizeof(firmwareVersion));
338  break;
339  }
341  {
342  if(RXCalculatedPayloadLength != 0){
343  errorID = payloadLengthTypeMismatch;
344  break;
345  }
346  /* Load the size into place */
347  *((unsigned short*)TXBufferCurrentPositionHandler) = RX_BUFFER_SIZE;
349  break;
350  }
352  {
353  /* This type must have a length field, set that up */
354  *((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived;
357  /* Load the body into place */
358  memcpy((void*)TXBufferCurrentPositionHandler, (void*)&RXBuffer, RXPacketLengthReceived);
359  /* Note, there is no overflow check here because the TX buffer is slightly */
360  /* bigger than the RX buffer and there is overflow checking for receives anyway. */
361  TXBufferCurrentPositionHandler += RXPacketLengthReceived;
362  break;
363  }
365  {
366  if(RXCalculatedPayloadLength != 0){
367  errorID = payloadLengthTypeMismatch;
368  }else{ // Perform soft system reset
369  _start();
370  }
371  break;
372  }
374  {
375  if(RXCalculatedPayloadLength != 0){
376  errorID = payloadLengthTypeMismatch;
377  }else{
378  /* This is how the serial monitor does it. */
379  COPCTL = 0x01; /* Arm with shortest time */
380  ARMCOP = 0xFF; /* Write bad value, should cause immediate reset */
381  /* Using _start() only resets the app ignoring the monitor switch. It does not work */
382  /* properly because the location of _start is not the master reset vector location. */
383  }
384  break;
385  }
387  {
388  if(RXCalculatedPayloadLength != 0){
389  errorID = payloadLengthTypeMismatch;
390  }else{
391  init();
392  }
393  break;
394  }
395  // FreeEMS Vanilla Firmware Specific cases
397  {
398  if(RXCalculatedPayloadLength != 0){
399  errorID = payloadLengthTypeMismatch;
400  break;
401  }
402 
403  unsigned short zeroCounter;
404  unsigned char* counterPointer;
405 
406  counterPointer = (unsigned char*) &Counters;
407  for(zeroCounter = 0;zeroCounter < sizeof(Counter);zeroCounter++){
408  *counterPointer = 0;
409  counterPointer++;
410  }
411 
413  counterPointer = (unsigned char*) &Flaggables;
414  for(zeroCounter = 0;zeroCounter < sizeof(Flaggable);zeroCounter++){
415  *counterPointer = 0;
416  counterPointer++;
417  }
418 
420  counterPointer = (unsigned char*) &Flaggables2;
421  for(zeroCounter = 0;zeroCounter < sizeof(Flaggable2);zeroCounter++){
422  *counterPointer = 0;
423  counterPointer++;
424  }
425  break;
426  }
427  case requestBuiltByName:
428  case requestSupportEmail:
429  case requestDecoderName:
433  {
434  if(RXCalculatedPayloadLength != 0){
435  errorID = payloadLengthTypeMismatch;
436  break;
437  }
438 
439  unsigned char* stringToSend = 0;
440  switch (RXHeaderPayloadID) {
441  case requestBuiltByName:
442  stringToSend = (unsigned char*)builtByName;
443  break;
444  case requestSupportEmail:
445  stringToSend = (unsigned char*)supportEmail;
446  break;
447  case requestDecoderName:
448  stringToSend = (unsigned char*)decoderName;
449  break;
451  stringToSend = (unsigned char*)buildTimeAndDate;
452  break;
454  stringToSend = (unsigned char*)compilerVersion;
455  break;
457  stringToSend = (unsigned char*)operatingSystem;
458  break;
459  }
460  /* This type must have a length field, set that up and load the body into place at the same time */
461  *((unsigned short*)TXBufferCurrentPositionHandler) = stringCopy((TXBufferCurrentPositionHandler + 2), stringToSend);
463  // Update with length field and string length.
465  break;
466  }
467  case updateBlockInRAM:
468  {
469  // Subtract six to allow for the locationID, size, offset
471  errorID = payloadLengthTypeMismatch;
472  break;
473  }
474 
475  // Extract the RAM location ID
476  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
478 
479  // Extract the offset to place the data at
480  unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
482 
483  // Extract the size of the data to be stored
484  unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
486 
487  // Look up the memory location details
488  blockDetails details;
489  lookupBlockDetails(locationID, &details);
490 
491  // Don't let anyone write to running variables unless we are running BenchTest firmware!
492  if((details.flags & block_is_read_only) && compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME))){
494  break;
495  }
496 
497  // Subtract six to allow for the locationID, size, offset
498  if((RXCalculatedPayloadLength - 6) != size){
500  break;
501  }
502 
503  // If either of these is zero then this block is not in RAM!
504  if((details.RAMPage == 0) || (details.RAMAddress == 0)){
505  errorID = invalidMemoryActionForID;
506  break;
507  }
508 
509  // Check that size and offset describe a region that is not out of bounds
510  if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
512  break;
513  }
514 
515  // Don't allow sub region manipulation where it does not make sense or is unsafe.
516  if((size != details.size) && !(details.flags & block_is_indexable)){
518  break;
519  }
520 
521  // Save page values for restore
522  unsigned char oldRamPage = RPAGE;
523  // Set the viewable RAM page
524  RPAGE = details.RAMPage;
525 
526  /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
527  //
528  if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
529  void* bufferToCheck;
530 
531  // For sub regions, construct an image for verification
532  if(size != details.size){
533  // Copy data from destination location to buffer
534  memcpy(leftOverBuffer, details.RAMAddress, details.size);
535 
536  // Copy data from rx buffer to buffer over writing old data
537  memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
538 
539  bufferToCheck = leftOverBuffer;
540  }else{
541  bufferToCheck = RXBufferCurrentPosition;
542  }
543 
544  // Verify all tables
545  if(details.flags & block_is_main_table){
546  errorID = validateMainTable((mainTable*)bufferToCheck);
547  }else if(details.flags & block_is_2dus_table){
548  errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
549  }// TODO add other table types here
550 
551  // If the validation failed, report it
552  if(errorID != 0){
553  RPAGE = oldRamPage; // Restore the original RAM page, even when getting an error condition.
554  break;
555  }
556  }
557 
558  // Copy from the RX buffer to the block of RAM
559  memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
560 
561  // Check that the write was successful
562  unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
563 
564  // Restore the original RAM and flash pages
565  RPAGE = oldRamPage;
566 
567  if(index != 0){
568  errorID = MEMORY_WRITE_ERROR;
569  }
570  break;
571  }
572  case updateBlockInFlash:
573  {
574  // Subtract six to allow for the locationID, size, offset
576  errorID = payloadLengthTypeMismatch;
577  break;
578  }
579 
580  // Extract the RAM location ID
581  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
583 
584  // Extract the offset to place the data at
585  unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
587 
588  // Extract the size of the data to be stored
589  unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
591 
592  // Look up the memory location details
593  blockDetails details;
594  lookupBlockDetails(locationID, &details);
595 
596  // Subtract six to allow for the locationID, size, offset
597  if((RXCalculatedPayloadLength - 6) != size){
599  break;
600  }
601 
602  // If either of these is zero then this block is not in flash!
603  if((details.FlashPage == 0) || (details.FlashAddress == 0)){
604  errorID = invalidMemoryActionForID;
605  break;
606  }
607 
608  // Check that size and offset describe a region that is not out of bounds
609  if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
611  break;
612  }
613 
614  // Don't allow sub region manipulation where it does not make sense or is unsafe.
615  if((size != details.size) && !(details.flags & block_is_indexable)){
617  break;
618  }
619 
620  /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
621  //
622  if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
623  void* bufferToCheck;
624 
625  // For sub regions, construct an image for verification
626  if(size != details.size){
627  /* Save page value for restore and set the visible page */
628  unsigned char oldFlashPage = PPAGE;
629  PPAGE = details.FlashPage;
630 
631  // Copy data from destination location to buffer
632  memcpy(leftOverBuffer, details.FlashAddress, details.size);
633 
634  /* Restore the original flash page */
635  PPAGE = oldFlashPage;
636 
637  // Copy data from rx buffer to buffer over writing old data
638  memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
639 
640  bufferToCheck = leftOverBuffer;
641  }else{
642  bufferToCheck = RXBufferCurrentPosition;
643  }
644 
645  // Verify all tables
646  if(details.flags & block_is_main_table){
647  errorID = validateMainTable((mainTable*)bufferToCheck);
648  }else if(details.flags & block_is_2dus_table){
649  errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
650  }// TODO add other table types here
651 
652  // If the validation failed, report it
653  if(errorID != 0){
654  break;
655  }
656  }
657 
658  /* Copy the flash details and populate the RAM details with the buffer location */
659  blockDetails burnDetails;
660  burnDetails.FlashPage = details.FlashPage;
661  burnDetails.FlashAddress = details.FlashAddress + offset;
662  burnDetails.RAMPage = RPAGE;
663  burnDetails.RAMAddress = RXBufferCurrentPosition;
664  burnDetails.size = size;
665 
666  /* Copy from the RX buffer to the block of flash */
667  errorID = writeBlock(&burnDetails, leftOverBuffer);
668  if(errorID != 0){
669  break;
670  }
671 
672  /* If present in RAM, update that too */
673  if((details.RAMPage != 0) && (details.RAMAddress != 0)){
674  /* Save page values for restore */
675  unsigned char oldRamPage = RPAGE;
676  /* Set the viewable RAM page */
677  RPAGE = details.RAMPage;
678 
679  /* Copy from the RX buffer to the block of RAM */
680  memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
681 
682  /* Check that the write was successful */
683  unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
684 
685  /* Restore the original RAM and flash pages */
686  RPAGE = oldRamPage;
687 
688  if(index != 0){
689  errorID = MEMORY_WRITE_ERROR;
690  }
691  }
692 
693  break;
694  }
696  {
697  if(RXCalculatedPayloadLength != 6){
698  errorID = payloadLengthTypeMismatch;
699  break;
700  }
701 
702  // Extract the RAM location ID
703  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
705 
706  // Extract the offset to place the data at
707  unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
709 
710  // Extract the size of the data to be stored
711  unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
713 
714  /* Look up the memory location details */
715  blockDetails details;
716  lookupBlockDetails(locationID, &details);
717 
718  if((details.RAMPage == 0) || (details.RAMAddress == 0)){
719  errorID = invalidMemoryActionForID;
720  break;
721  }
722 
723  // Special behaviour for size of zero which returns the whole block
724  if((size == 0) && (offset == 0)){
725  size = details.size;
726  }
727 
728  // Check that size and offset describe a region that is not out of bounds
729  if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
731  break;
732  }
733 
734  // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
735  if((size != details.size) && !(details.flags & block_is_indexable)){
737  break;
738  }
739 
740  // This type must have a length field, set that up
741  *((unsigned short*)TXBufferCurrentPositionHandler) = size;
744 
745  /* Save page value for restore and set the visible page */
746  unsigned char oldRamPage = RPAGE;
747  RPAGE = details.RAMPage;
748 
749  /* Copy the block of RAM to the TX buffer */
750  memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.RAMAddress + offset), size);
752 
753  /* Restore the original RAM and flash pages */
754  RPAGE = oldRamPage;
755 
756  break;
757  }
759  {
760  if(RXCalculatedPayloadLength != 6){
761  errorID = payloadLengthTypeMismatch;
762  break;
763  }
764 
765  // Extract the RAM location ID
766  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
768 
769  // Extract the offset to place the data at
770  unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
772 
773  // Extract the size of the data to be stored
774  unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
776 
777  /* Look up the memory location details */
778  blockDetails details;
779  lookupBlockDetails(locationID, &details);
780 
781  if((details.FlashPage == 0) || (details.FlashAddress == 0)){
782  errorID = invalidMemoryActionForID;
783  break;
784  }
785 
786  // Special behaviour for size of zero which returns the whole block
787  if((size == 0) && (offset == 0)){
788  size = details.size;
789  }
790 
791  // Check that size and offset describe a region that is not out of bounds
792  if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
794  break;
795  }
796 
797  // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
798  if((size != details.size) && !(details.flags & block_is_indexable)){
800  break;
801  }
802 
803  // This type must have a length field, set that up
804  *((unsigned short*)TXBufferCurrentPositionHandler) = size;
807 
808  /* Save page value for restore and set the visible page */
809  unsigned char oldFlashPage = PPAGE;
810  PPAGE = details.FlashPage;
811 
812  /* Copy the block of flash to the TX buffer */
813  memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.FlashAddress + offset), size);
815 
816  /* Restore the original RAM and flash pages */
817  PPAGE = oldFlashPage;
818 
819  break;
820  }
822  {
823  if(RXCalculatedPayloadLength != 6){
824  errorID = payloadLengthTypeMismatch;
825  break;
826  }
827 
828  // Extract the RAM location ID
829  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
831 
832  // Extract the offset to place the data at
833  unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
835 
836  // Extract the size of the data to be stored
837  unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
839 
840  /* Look up the memory location details */
841  blockDetails details;
842  lookupBlockDetails(locationID, &details);
843 
844  /* Check that all data we need is present */
845  if((details.RAMPage == 0) || (details.RAMAddress == 0) || (details.FlashPage == 0) || (details.FlashAddress == 0)){
846  errorID = invalidMemoryActionForID;
847  break;
848  }
849 
850  // Special behaviour for size of zero which burns the whole block
851  if((size == 0) && (offset == 0)){
852  size = details.size;
853  }
854 
855  // Check that size and offset describe a region that is not out of bounds
856  if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
858  break;
859  }
860 
861  // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
862  if((size != details.size) && !(details.flags & block_is_indexable)){
864  break;
865  }
866 
867 
868  // adjust details block to feed to represent the subsection of ram and flash that we want to burn down.
869  details.RAMAddress += offset;
870  details.FlashAddress += offset;
871  details.size = size;
872 
873  /* Write the block down from RAM to Flash */
874  errorID = writeBlock(&details, leftOverBuffer);
875  break;
876  }
877  case requestDatalogPacket: // Set type through standard configuration methods
878  {
879  if(RXCalculatedPayloadLength != 0){
880  errorID = payloadLengthTypeMismatch;
881  break;
882  }
883 
884  /* Set the length field up */
886  unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
888 
889  /* Fill out the log and send */
890  *localLength = populateBasicDatalog();
891  break;
892  }
893  case setAsyncDatalogType:
894  {
895  if(RXCalculatedPayloadLength != 1){
896  errorID = payloadLengthTypeMismatch;
897  break;
898  }
899 
900  unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition);
901  if(newDatalogType > asyncDatalogLastType){
902  errorID = noSuchAsyncDatalogType;
903  break;
904  }
905 
906  TablesB.SmallTablesB.loggingSettings.datalogStreamType = newDatalogType;
907  break;
908  }
910  {
911  if(RXCalculatedPayloadLength != 6){
912  errorID = payloadLengthTypeMismatch;
913  break;
914  }
915 
916  unsigned short length = *((unsigned short*)RXBufferCurrentPosition);
918  // Make sure the buffer can handle the block
919  if(length > TX_MAX_PAYLOAD_SIZE){
920  errorID = requestedLengthTooLarge;
921  break;
922  }
923 
924  void* address = (void*) *((unsigned short*)RXBufferCurrentPosition);
926  // Ensure we don't try to read past the end of the address space
927  if(((unsigned short)address) <= ((0xFFFF - length) + 1)){
928  // TODO Possibly check and limit ranges
929  errorID = requestedAddressDisallowed;
930  break;
931  }
932 
933  unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition);
935  // Ensure RAM page is valid. Being too high is not possible.
936  if(RAMPage < RPAGE_MIN){
937  errorID = requestedRAMPageInvalid;
938  break;
939  }
940 
941  unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition);
943  // Ensure Flash page is valid. Being too high is not possible.
944  if(FlashPage < PPAGE_MIN){
945  errorID = requestedFlashPageInvalid;
946  break;
947  }
948 
949  /* This type must have a length field, set that up */
950  *((unsigned short*)TXBufferCurrentPositionHandler) = length + 6;
953 
954  /* Put the request payload into the reply */
955  *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address;
957  *((unsigned short*)TXBufferCurrentPositionHandler) = length;
959  *((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage;
961  *((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage;
963 
964  /* Load the body into place */
965  memcpy((void*)TXBufferCurrentPositionHandler, address, length);
967 
968  break;
969  }
971  {
972  if(RXCalculatedPayloadLength != 3){
973  errorID = payloadLengthTypeMismatch;
974  break;
975  }
976 
977  // Extract the type of list that we want
978  unsigned char listType = *((unsigned char*)RXBufferCurrentPosition);
980 
981  // Extract the mask for the qualities that we want
982  unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition);
984 
985  // This type must have a length field, set that up
986  unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler;
989 
990  // Zero the counter before we start, woops!
991  *listLength = 0;
992 
993  unsigned long locationID;
994  blockDetails details;
995  for(locationID = 0;locationID < 65536;locationID++){
996  unsigned short locationIDDoesntExist;
997  locationIDDoesntExist = lookupBlockDetails((unsigned short)locationID, &details);
998 
999  if(!locationIDDoesntExist){
1000  if((listType == 0x00) || // get all
1001  ((listType == 0x01) && (details.flags & blockDetailsMask)) || // get OR of bits
1002  ((listType == 0x02) && (!(~(details.flags) & blockDetailsMask)))){ // get AND of bits
1003  *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short)locationID;
1004  TXBufferCurrentPositionHandler += 2;
1005  *listLength += 2;
1006  }
1007  }
1008  }
1009 
1010  break;
1011  }
1013  {
1014  if(RXCalculatedPayloadLength != 2){
1015  errorID = payloadLengthTypeMismatch;
1016  break;
1017  }
1018 
1019  // Extract the RAM location ID
1020  unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
1022 
1023  // This type must have a length field, set that up
1024  *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(blockDetails);
1027 
1028  // Write straight to output buffer to save time/code
1030 
1031  if(errorID != 0){
1032  break;
1033  }
1034 
1035  // Adjust TX buffer position if successful
1037 
1038  break;
1039  }
1041  {
1042  /*
1043  * The idea here is to call this function with arguments, and data
1044  * and have the result sent back for comparison with an expected
1045  * result that isn't divulged to the firmware.
1046  *
1047  * It is intended that all testable functions be callable through
1048  * this mechanism and that any number of test executions can be
1049  * performed by an external suite using different parameters and
1050  * data sets and matching expected results.
1051  *
1052  * The usual error mechanism shall be used to indicate some sort of
1053  * either internal or test failure and returned errors shall be
1054  * suitably descriptive to allow diagnosis and fixing of issues.
1055  */
1056 
1057  // Must at least have test ID
1058  if(RXCalculatedPayloadLength < 2){
1059  errorID = payloadLengthTypeMismatch;
1060  break;
1061  }
1062 
1063  // grab unit test ID from payload
1064  unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition);
1066 
1067  switch(unitTestID){
1068  case testEmptyTest:
1069  {
1070  // Must be only the ID
1071  if(RXCalculatedPayloadLength != 2){
1073  break;
1074  }
1075 
1076  *((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID;
1078 
1079  break;
1080  }
1081  case testTwoDTableUSLookup:
1082  {
1083  // ID + Value + Table
1084  if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){
1086  break;
1087  }
1088 
1089  unsigned short Value = *((unsigned short*)RXBufferCurrentPosition);
1091 
1094 
1095  unsigned short result = lookupTwoDTableUS(Table, Value);
1096 
1097  *((unsigned short*)TXBufferCurrentPositionHandler) = result;
1099 
1100  break;
1101  }
1102  // http://issues.freeems.org/view.php?id=156
1103  //
1104  /// TODO @todo test all things listed below:
1105  // lookupPagedMainTableCellValue - pass this RPAGE so that it remains unchanged
1106  // validateMainTable
1107  // validateTwoDTable
1108  // set table values - leave this till last, currently unused by mtx, likely to be removed anyway
1109  // generateDerivedVars - convert to pointers, remove headers, privatise a lot of data!
1110  // calculateFuelAndIgnition - ditto
1111  // scheduling algorithm - ditto
1112  // safeAdd
1113  // safeTrim
1114  // safeScale
1115  // sleep (milliseconds)
1116  // sleepMicro (microseconds)
1117  // checksum
1118  // stringCopy
1119  // compare
1120  // utils that can't be checked: sampleLoopADC sampleBlockADC sampleEachADC - can check for how long each takes! adjustPWM (test only anyway), resetToNonRunningState and setupPagedRAM (would interfere with functioning of device)
1121  // init code may be able to be partially checked
1122  // most other code at this stage is ISR code, flash writing code, or could interfere with the running of the engine
1123  // more testable code will appear with time, such as the HAL layer, and most accessory functions.
1124  default:
1125  {
1126  errorID = noSuchUnitTestID;
1127  }
1128 
1129  // each case:
1130  // checks length, fails if wrong
1131  // parses data into args
1132  // calls function on data/args
1133  // assembles response OR sets error
1134  // breaks
1135  }
1136  break;
1137  }
1139  {
1140  // see TODO on include at top and modify this line appropriately
1141  if(!(compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME)))){
1142  if(RXCalculatedPayloadLength < 1){
1143  errorID = payloadLengthTypeMismatch;
1144  break;
1145  }
1146 
1147  unsigned char localTestMode = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1149  if(localTestMode > TEST_MODE_BUMP_UP_CYCLES){
1150  errorID = unimplementedTestMode;
1151  break;
1152  }else if((localTestMode == TEST_MODE_STOP) && (RXCalculatedPayloadLength == 1)){
1153  if(!(coreStatusA & BENCH_TEST_ON)){
1154  errorID = benchTestNotRunningToStop;
1155  break;
1156  }
1157 
1158  // Ensure we succeed at stopping it as quickly as possible.
1159  ATOMIC_START();
1160  // Stash mid-test details for return
1161  *((unsigned short*)TXBufferCurrentPositionHandler) = testNumberOfCycles; // Save and return the remaining cycle count
1163  *((unsigned char*)TXBufferCurrentPositionHandler) = KeyUserDebugs.currentEvent; // Save the current event for the ultra-fussy
1165  // Setup the test to stop ASAP
1166  KeyUserDebugs.currentEvent = testEventsPerCycle - 1; // Gets incremented then compared with testEventsPerCycle
1167  testNumberOfCycles = 1; // Gets decremented then compared with zero
1168  ATOMIC_END();
1169 
1170  break;
1171  }else if((localTestMode == TEST_MODE_BUMP_UP_CYCLES) && (RXCalculatedPayloadLength == 2)){
1172  if(!(coreStatusA & BENCH_TEST_ON)){
1173  errorID = benchTestNotRunningToBump;
1174  break;
1175  }
1176 
1177  // Get bump value from payload
1178  unsigned char bumpCycles = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1180 
1181  if(bumpCycles == 0){
1182  errorID = bumpingByZeroMakesNoSense;
1183  break;
1184  }
1185 
1186  // Bump count by value from payload
1187  testNumberOfCycles += bumpCycles;
1188  // Given that this function is only for situations when A it's getting near to
1189  // zero and B the user is watching, not checking for overflow is reasonable.
1190 
1191  *((unsigned char*)TXBufferCurrentPositionHandler) = bumpCycles; // Return the bump size for achaelogical purposes
1193 
1194  break;
1195  }else if((localTestMode == TEST_MODE_ITERATIONS) && (RXCalculatedPayloadLength == 24)){
1196  testMode = localTestMode;
1197  // do nothing to fall through, or move other code into here
1198  }else{
1199  errorID = packetSizeWrongForTestMode;
1200  break;
1201  }
1202 
1203  if(coreStatusA & BENCH_TEST_ON){
1204  errorID = benchTestAlreadyRunning;
1205  break;
1206  }
1207 
1208  // Parse the values and return all but the test packet type
1209 
1210  testEventsPerCycle = *((unsigned char*)RXBufferCurrentPosition); //100; // @ 10ms = 1s
1214  if(testEventsPerCycle == 0){
1215  errorID = invalidEventsPerCycle;
1216  break;
1217  }
1218 
1219  testNumberOfCycles = *((unsigned short*)RXBufferCurrentPosition); //20; // @ 1s = 20s
1221  *((unsigned short*)TXBufferCurrentPositionHandler) = testNumberOfCycles;
1223  if(testNumberOfCycles == 0){
1224  errorID = invalidNumberOfCycles;
1225  break;
1226  }
1227 
1228  testTicksPerEvent = *((unsigned short*)RXBufferCurrentPosition); //12500; // @ 0.8us = 10ms
1230  *((unsigned short*)TXBufferCurrentPositionHandler) = testTicksPerEvent;
1233  errorID = tooShortOfAnEventPeriod;
1234  break;
1235  }
1236 
1237  // Pluck the arrays out of the packet for the loop below
1238  unsigned char* testEventNumbers = RXBufferCurrentPosition;
1240  unsigned short* testPulseWidths = (unsigned short*)RXBufferCurrentPosition;
1242  memcpy((void*)TXBufferCurrentPositionHandler, (void*)testEventNumbers, 18);
1244 
1245  // Reset the clock for reading timeout
1246  Clocks.timeoutADCreadingClock = 0; // make this optional, such that we can use real inputs to determine pw and/or dwell.
1247 
1248  // Validate and transfer the per-channel data
1249  unsigned char channel;
1250  unsigned char configuredChannels = 6;
1251  for(channel = 0;channel < 6;channel++){
1252  if(testPulseWidths[channel] > ectSwitchOnCodeTime){ // See next block for warning.
1253  // use as-is
1255  outputEventPulseWidthsMath[channel] = testPulseWidths[channel];
1256  outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1257  }else if(testPulseWidths[channel] > 3){
1258  // less than the code time, and not special, error!
1259  errorID = tooShortOfAPulseWidthToTest;
1260  // Warning, PWs close to this could be slightly longer than requested, that will change in later revisions.
1261  break;
1262  }else if(testPulseWidths[channel] == 3){
1263  testMode++; // Dirty hack to avoid dealing with Dave for the time being.
1264  testNumberOfMissing = channel;
1265  }else if(testPulseWidths[channel] == 2){
1266  // use the dwell from the core maths and input vars.
1269  outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1270  }else if(testPulseWidths[channel] == 1){
1271  // use the reference pulse width from the core maths and input vars.
1274  outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1275  }else{ // is zero
1276  // Set this channel to zero for and therefore off, don't set this channel.
1277  outputEventInputEventNumbers[channel] = 0xFF; // Off.
1278  configuredChannels--;
1279  }
1280  }
1281 
1282  if(configuredChannels == 0){
1283  errorID = noChannelsConfiguredToTest;
1284  break;
1285  }
1286 
1287  if(errorID == 0){
1288  // Let the first iteration roll it over to zero.
1289  KeyUserDebugs.currentEvent = 0xFF; // Needs to be here in case of multiple runs, init is not sufficient
1290 
1292  if(testEventsPerCycle <= 127){
1293  testEventsPerCycle *= 2;
1294  }else{
1296  break;
1297  }
1298 
1299  // Store the time per event in RPM such that it can be updated dynamically
1301 
1302  // The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode!
1303 
1304  // Un-schedule anything that got scheduled
1305  outputEventInputEventNumbers[2] = 0xFF;
1306  outputEventInputEventNumbers[3] = 0xFF;
1307  outputEventInputEventNumbers[4] = 0xFF;
1308  outputEventInputEventNumbers[5] = 0xFF;
1310  errorID = unimplementedTestMode;
1311  break;
1312  }
1313 
1314  // Trigger decoder interrupt to fire thus starting the loop!
1315  TIE = 0x01; // The ISR does the rest!
1316 
1317  // Nothing went wrong, now set flag.
1319  }else{
1320  break;
1321  }
1322 
1323 
1324 /* http://issues.freeems.org/view.php?id=155
1325  *
1326  * The following block has been left in, as I still do not know why it won't work as intended:
1327  *
1328  * - It should fire all 6 output pins with a 52ms duration pulse, exactly once.
1329  * - The SAME code run from anywhere else (pre main loop, in main loop, in rtc, in decoder) works fine, just not here in commsCore.c
1330  * - The interrupts run, but the pin doesn't change state, despite the registers being configured correctly
1331  *
1332  * I've tried quite a bit:
1333  *
1334  * - Moving this code around
1335  * - Checking memory definitions
1336  * - Completely rewriting the output ISR
1337  * - Adding significant debug to output ISR
1338  * - Checking for register contents in output ISR
1339  * - Checking for key things modified in this file
1340  * - General head scratching and confused searching
1341  */
1342 
1343 // outputEventPinNumbers[0] = 0; // 1 ign
1344 // outputEventPinNumbers[1] = 1; // 2 ign
1345 // outputEventPinNumbers[2] = 2; // 3 ign/1 fuel
1346 // outputEventPinNumbers[3] = 3; // 4 ign/2 fuel
1347 // outputEventPinNumbers[4] = 4; // 3 fuel
1348 // outputEventPinNumbers[5] = 5; // 4 fuel
1349 // outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
1350 // outputEventDelayFinalPeriod[1] = decoderMaxCodeTime;
1351 // outputEventDelayFinalPeriod[2] = decoderMaxCodeTime;
1352 // outputEventDelayFinalPeriod[3] = decoderMaxCodeTime;
1353 // outputEventDelayFinalPeriod[4] = decoderMaxCodeTime;
1354 // outputEventDelayFinalPeriod[5] = decoderMaxCodeTime;
1355 // outputEventPulseWidthsMath[0] = SHORTMAX;
1356 // outputEventPulseWidthsMath[1] = SHORTMAX;
1357 // outputEventPulseWidthsMath[2] = SHORTMAX;
1358 // outputEventPulseWidthsMath[3] = SHORTMAX;
1359 // outputEventPulseWidthsMath[4] = SHORTMAX;
1360 // outputEventPulseWidthsMath[5] = SHORTMAX;
1361 //
1362 // unsigned short edgeTimeStamp = TCNT;
1363 // // call sched output with args
1364 // LongTime timeStamp;
1365 // /* Install the low word */
1366 // timeStamp.timeShorts[1] = edgeTimeStamp;
1367 // /* Find out what our timer value means and put it in the high word */
1368 // if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
1369 // timeStamp.timeShorts[0] = timerExtensionClock + 1;
1370 // }else{
1371 // timeStamp.timeShorts[0] = timerExtensionClock;
1372 // }
1373 //
1374 // schedulePortTPin(0, timeStamp);
1375 // schedulePortTPin(1, timeStamp);
1376 // schedulePortTPin(2, timeStamp);
1377 // schedulePortTPin(3, timeStamp);
1378 // schedulePortTPin(4, timeStamp);
1379 // schedulePortTPin(5, timeStamp);
1380 //
1381 // sleep(1000);
1382  }else{
1383  errorID = thisIsNotTheBenchTestDecoder;
1384  }
1385  break;
1386  }
1387  default:
1388  {
1389  if((RXHeaderPayloadID % 2) == 1){
1390  errorID = invalidPayloadID;
1391  }else{
1392  errorID = unrecognisedPayloadID;
1393  }
1394  break;
1395  }
1396  }
1397 
1398  // Always reply, if errorID is zero it's just an ack.
1399  finaliseAndSend(errorID);
1400 
1401  /* Switch reception back on now that we are done with the received data */
1403 }
1404 
1405 
1406 /* This function should be period limited to about 10 seconds internally (or by scheduler) */
1407 //void checkCountersAndSendErrors(){
1408  // compare time stamps with current time stamps and execute if old enough. (if no scheduler)
1409 
1410  // compare counters with counters cache (from last time) sending an error packet when they differ
1411 
1412  // copy counters to counters cache for next time
1413 
1414  // send errors with busy wait on the basis that all errors should be taken care of and not be sent in fairly short order?
1415 
1416  // or send with isr but just busy wait for it to finish before sending the next?
1417 
1418  // debug messages, busy wait or isr or both, perhaps busy wait till able to send, lock sending (need semaphore for this as well as sending one?) and initiate send, then carry on? investigate timeframes for sends of smallish 100byte packets.
1419 
1420  // need to figure out how to queue received packets for processing when we are currently sending stuff out.
1421 
1422  // above notes don't belong here really.
1423 //}
1424