FreeEMS  0.2.0-SNAPSHOT-285-g028e24c
Macros | Functions
commsCore.c File Reference

Core communications functions. More...

#include "inc/freeEMS.h"
#include "inc/flashWrite.h"
#include "inc/interrupts.h"
#include "inc/utils.h"
#include "inc/tableLookup.h"
#include "inc/locationIDs.h"
#include "inc/blockDetailsLookup.h"
#include "inc/decoderInterface.h"
#include "inc/commsCore.h"
#include "inc/init.h"
#include <string.h>
#include "decoders/inc/BenchTest.h"
Include dependency graph for commsCore.c:

Go to the source code of this file.

Macros

#define COMMSCORE_C

Functions

unsigned short populateBasicDatalog ()
 Populate a basic datalog packet.
void finaliseAndSend (unsigned short errorID)
 Finalise a packet and send it.
void decodePacketAndRespond ()
 Decode a packet and respond.

Detailed Description

Core communications functions.

This file contains most of the core comms functionality. Currently that is only for UART serial style communication. It is already too big and needs to be split up somewhat. This will happen fairly soon during the serial refactoring and protocol fine tuning.

Todo:

TODO function to setup a packet and send it fn(populateBodyFunctionPointer(), header, other, fields, here, and, use, or, not, within){}

TODO factor many things into functions and move the receive delegator to its own file

Definition in file commsCore.c.

Macro Definition Documentation

#define COMMSCORE_C

Definition at line 43 of file commsCore.c.

Function Documentation

unsigned short populateBasicDatalog ( void  )

Populate a basic datalog packet.

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

Copies various chunks of data to the transmission buffer and truncates to the configured length. If changing this, update the maxBasicDatalogLength.

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.

Definition at line 63 of file commsCore.c.

References KeyUserDebug::clockIn8thsOfAMilli, KeyUserDebug::clockInMilliSeconds, Clocks, KeyUserDebug::coreStatusA, coreStatusA, KeyUserDebugs, Clock::realTimeClockMain, Clock::realTimeClockMillis, KeyUserDebug::tempClock, and TXBufferCurrentPositionHandler.

Referenced by decodePacketAndRespond(), and main().

{
/// @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.
unsigned short confSize = 0;
unsigned char chunkLimit = TablesB.SmallTablesB.loggingSettings.firstChunk + TablesB.SmallTablesB.loggingSettings.numberOfChunks;
unsigned char chunks;
for(chunks=TablesB.SmallTablesB.loggingSettings.firstChunk;chunks<chunkLimit;chunks++){
unsigned short localSize = TablesB.SmallTablesB.loggingSettings.logChunks[chunks].size;
confSize += localSize;
if(confSize > 2048){
confSize -= localSize;
break;
}
memcpy(TXBufferCurrentPositionHandler, TablesB.SmallTablesB.loggingSettings.logChunks[chunks].address, localSize);
}
// After copying data, otherwise tempClock is NEVER zero and reset detection does NOT work
return confSize;
}

Here is the caller graph for this function:

void finaliseAndSend ( unsigned short  errorID)

Finalise a packet and send it.

This functions job is to finalise the main loop part of the packet sending process. It configures the pos/neg ack header bit, adds the code if neg, runs a checksum over the packet data and tags it to the end before configuring the various ISRs that need to send the data out.

Definition at line 141 of file commsCore.c.

References checksum(), COM_CLEAR_CAN0_INTERFACE_ID, COM_CLEAR_SPARE2_INTERFACE_ID, COM_CLEAR_SPARE3_INTERFACE_ID, COM_CLEAR_SPARE4_INTERFACE_ID, COM_CLEAR_SPARE5_INTERFACE_ID, COM_CLEAR_SPARE6_INTERFACE_ID, COM_CLEAR_SPARE7_INTERFACE_ID, COM_SET_CAN0_INTERFACE_ID, COM_SET_SCI0_INTERFACE_ID, COM_SET_SPARE2_INTERFACE_ID, COM_SET_SPARE3_INTERFACE_ID, COM_SET_SPARE4_INTERFACE_ID, COM_SET_SPARE5_INTERFACE_ID, COM_SET_SPARE6_INTERFACE_ID, COM_SET_SPARE7_INTERFACE_ID, HEADER_IS_NACK, SCI0CR2, SCI0DRL, SCICR2_TX_ENABLE, SCICR2_TX_ISR_ENABLE, START_BYTE, TXBufferCurrentPositionHandler, TXBufferInUseFlags, and TXHeaderFlags.

Referenced by decodePacketAndRespond(), and main().

{
if(errorID != 0){
*((unsigned short*)TXBufferCurrentPositionHandler) = errorID;
}
/* Tag the checksum on the end */
*TXBufferCurrentPositionHandler = checksum((unsigned char*)&TXBuffer, ((unsigned short)TXBufferCurrentPositionHandler - (unsigned short)&TXBuffer));
/* Send it out on all the channels required. */
/* SCI0 - Main serial interface */
/* Initiate transmission */
/* Note : Order Is Important! */
/* TX empty flag is already set, so we must clear it by writing out before enabling the interrupt */
}
/* CAN0 - Main CAN interface */
// just clear up front for now
}
/* spare2 */
// just clear up front for now
}
/* spare3 */
// just clear up front for now
}
/* spare4 */
// just clear up front for now
}
/* spare5 */
// just clear up front for now
}
/* spare6 */
// just clear up front for now
}
/* spare7 */
// just clear up front for now
}
}

Here is the call graph for this function:

Here is the caller graph for this function:

void decodePacketAndRespond ( void  )

Decode a packet and respond.

This is the core function that controls which functionality is run when a packet is received in full by the ISR code and control is passed back to the main loop code. The vast majority of communications action happens here.

TODO

Todo:
factor this out into validation delegation function once the number of types increases somewhat

TODO

Todo:
factor this out into validation delegation function once the number of types increases somewhat

TODO

Todo:
test all things listed below:

Definition at line 207 of file commsCore.c.

References _start(), ARMCOP, asyncDatalogLastType, ATOMIC_END, ATOMIC_START, attemptToWriteToReadOnlyBlock, BENCH_TEST_NAME, BENCH_TEST_ON, benchTestAlreadyRunning, benchTestNotRunningToBump, benchTestNotRunningToStop, block_is_2dus_table, block_is_indexable, block_is_main_table, block_is_read_only, buildTimeAndDate, builtByName, bumpingByZeroMakesNoSense, burnBlockFromRamToFlash, checksum(), CLEAR_ALL_SOURCE_ID_FLAGS, clearCountersAndFlagsToZero, Clocks, COM_SET_SCI0_INTERFACE_ID, compare(), compilerVersion, COPCTL, coreStatusA, CoreVars, Counters, KeyUserDebug::currentEvent, decoderMaxCodeTime, decoderName, DerivedVars, doesNotMakeSenseToRetrievePartially, DerivedVar::Dwell, ectSwitchOnCodeTime, finaliseAndSend(), firmwareVersion, FLAG_AND_INC_FLAGGABLE, FLAG_SERIAL_CHECKSUM_MISMATCHES_OFFSET, FLAG_SERIAL_PACKETS_UNDER_LENGTH_OFFSET, KeyUserDebug::flaggableFlags, KeyUserDebug::flaggableFlags2, Flaggables, Flaggables2, blockDetails::flags, blockDetails::FlashAddress, blockDetails::FlashPage, HEADER_HAS_LENGTH, HEADER_HAS_SEQUENCE, init(), interfaceVersion, invalidEventsPerCycle, invalidMemoryActionForID, invalidNumberOfCycles, invalidPayloadID, invalidSizeOffsetCombination, KeyUserDebugs, lookupBlockDetails(), lookupTwoDTableUS(), MEMORY_WRITE_ERROR, noChannelsConfiguredToTest, noSuchAsyncDatalogType, noSuchUnitTestID, operatingSystem, outputEventDelayFinalPeriod, outputEventInputEventNumbers, outputEventPulseWidthsMath, packetSizeWrongForTestMode, packetTooShortForSpecifiedFields, payloadLengthHeaderMismatch, payloadLengthTypeMismatch, payloadNotEqualToSpecifiedValue, payloadShorterThanRequiredForTest, populateBasicDatalog(), PPAGE, PPAGE_MIN, blockDetails::RAMAddress, blockDetails::RAMPage, DerivedVar::RefPW, requestBuiltByName, requestCompilerVersion, requestDatalogPacket, requestDecoderName, requestEchoPacketReturn, requestedAddressDisallowed, requestedFlashPageInvalid, requestedLengthTooLarge, requestedRAMPageInvalid, requestFirmwareBuildDate, requestFirmwareVersion, requestHardSystemReset, requestInterfaceVersion, requestMaxPacketSize, requestOperatingSystem, requestReInitOfSystem, requestSoftSystemReset, requestSupportEmail, requestUnitTestOverSerial, resetReceiveState(), retrieveArbitraryMemory, retrieveBlockFromFlash, retrieveBlockFromRAM, retrieveListOfLocationIDs, retrieveLocationIDDetails, RPAGE, RPAGE_MIN, CoreVar::RPM, RX_BUFFER_SIZE, RXBufferCurrentPosition, RXCalculatedPayloadLength, RXHeaderFlags, RXHeaderPayloadID, RXHeaderPayloadLength, KeyUserDebug::serialAndCommsCodeErrors, setAsyncDatalogType, blockDetails::size, startBenchTestSequence, stringCopy(), supportEmail, TEST_MODE_BUMP_UP_CYCLES, TEST_MODE_DODGY_MISSING_TOOTH, TEST_MODE_ITERATIONS, TEST_MODE_STOP, testEmptyTest, testEventsPerCycle, testMode, testNumberOfCycles, testNumberOfMissing, testTicksPerEvent, testTwoDTableUSLookup, thisIsNotTheBenchTestDecoder, TIE, Clock::timeoutADCreadingClock, tooManyEventsPerCycleMissingTth, tooShortOfAnEventPeriod, tooShortOfAPulseWidthToTest, TX_MAX_PAYLOAD_SIZE, TXBufferCurrentPositionCAN0, TXBufferCurrentPositionHandler, TXBufferCurrentPositionSCI0, TXBufferInUseFlags, TXHeaderFlags, uncheckedTableManipulationNotAllowed, unimplementedTestMode, unrecognisedPayloadID, updateBlockInFlash, updateBlockInRAM, validateMainTable(), validateTwoDTable(), and writeBlock().

Referenced by main().

{
/* Extract and build up the header fields */
TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
/* Initialised here such that override is possible */
TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
// How big was the packet that we got back
unsigned short RXPacketLengthReceived = (unsigned short)RXBufferCurrentPosition - (unsigned short)&RXBuffer;
/* Check that the packet is big enough for header,ID,checksum */
if(RXPacketLengthReceived < 4){
return;
}
/* Pull out the received checksum and calculate the real one, then check */
unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1);
unsigned char RXCalculatedChecksum = checksum((unsigned char*)&RXBuffer, RXPacketLengthReceived - 1);
if(RXCalculatedChecksum != RXReceivedChecksum){
return;
}
/* Start this off as full packet length and build down to the actual length */
RXCalculatedPayloadLength = RXPacketLengthReceived;
/* Grab the RX header flags out of the RX buffer */
RXBufferCurrentPosition = (unsigned char*)&RXBuffer;
/* Flag that we are transmitting! */
// SCI0 only for now...
/* Load a blank header into the TX buffer ready for masking */
/* Grab the payload ID for processing and load the return ID */
/* Check that the length is sufficient for the fields configured. Packets
* that are too long will be caught and rejected on an individual payload
* ID basis as the information required to handle that is not available at
* this point. Packets that are too short are rejected immediately!
*/
if(((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 7))
|| ((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXPacketLengthReceived < 6))
|| ((RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 5))){
return;
}
/* Subtract checksum to get final length */
}
if(RXHeaderFlags & HEADER_HAS_LENGTH){
/* Already subtracted one for checksum */
return;
}
}
/* Calculate the position of the end of the stored packet for later use as a buffer */
void* leftOverBuffer = (void*)((unsigned short)&RXBuffer + RXPacketLengthReceived);
unsigned short errorID = 0;
/* This is where all the communication logic resides.
*
* Please Note: Length and its flag should be set by each return packet
* type handler if required or desired. If an ack has been requested,
* ensure the negative ack flag is set if the operation failed.
*/
// FreeEMS Core Comms Interface cases
{
break;
}
/* This type must have a length field, set that up */
*((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersion);
/* Load the body into place */
break;
}
{
break;
}
/* This type must have a length field, set that up */
*((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion);
/* Load the body into place */
break;
}
{
break;
}
/* Load the size into place */
break;
}
{
/* This type must have a length field, set that up */
*((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived;
/* Load the body into place */
memcpy((void*)TXBufferCurrentPositionHandler, (void*)&RXBuffer, RXPacketLengthReceived);
/* Note, there is no overflow check here because the TX buffer is slightly */
/* bigger than the RX buffer and there is overflow checking for receives anyway. */
TXBufferCurrentPositionHandler += RXPacketLengthReceived;
break;
}
{
}else{ // Perform soft system reset
_start();
}
break;
}
{
}else{
/* This is how the serial monitor does it. */
COPCTL = 0x01; /* Arm with shortest time */
ARMCOP = 0xFF; /* Write bad value, should cause immediate reset */
/* Using _start() only resets the app ignoring the monitor switch. It does not work */
/* properly because the location of _start is not the master reset vector location. */
}
break;
}
{
}else{
init();
}
break;
}
// FreeEMS Vanilla Firmware Specific cases
{
break;
}
unsigned short zeroCounter;
unsigned char* counterPointer;
counterPointer = (unsigned char*) &Counters;
for(zeroCounter = 0;zeroCounter < sizeof(Counter);zeroCounter++){
*counterPointer = 0;
counterPointer++;
}
counterPointer = (unsigned char*) &Flaggables;
for(zeroCounter = 0;zeroCounter < sizeof(Flaggable);zeroCounter++){
*counterPointer = 0;
counterPointer++;
}
counterPointer = (unsigned char*) &Flaggables2;
for(zeroCounter = 0;zeroCounter < sizeof(Flaggable2);zeroCounter++){
*counterPointer = 0;
counterPointer++;
}
break;
}
{
break;
}
unsigned char* stringToSend = 0;
switch (RXHeaderPayloadID) {
stringToSend = (unsigned char*)builtByName;
break;
stringToSend = (unsigned char*)supportEmail;
break;
stringToSend = (unsigned char*)decoderName;
break;
stringToSend = (unsigned char*)buildTimeAndDate;
break;
stringToSend = (unsigned char*)compilerVersion;
break;
stringToSend = (unsigned char*)operatingSystem;
break;
}
/* This type must have a length field, set that up and load the body into place at the same time */
*((unsigned short*)TXBufferCurrentPositionHandler) = stringCopy((TXBufferCurrentPositionHandler + 2), stringToSend);
// Update with length field and string length.
break;
}
{
// Subtract six to allow for the locationID, size, offset
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// Extract the offset to place the data at
unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
// Extract the size of the data to be stored
unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
// Look up the memory location details
blockDetails details;
lookupBlockDetails(locationID, &details);
// Don't let anyone write to running variables unless we are running BenchTest firmware!
if((details.flags & block_is_read_only) && compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME))){
break;
}
// Subtract six to allow for the locationID, size, offset
if((RXCalculatedPayloadLength - 6) != size){
break;
}
// If either of these is zero then this block is not in RAM!
if((details.RAMPage == 0) || (details.RAMAddress == 0)){
break;
}
// Check that size and offset describe a region that is not out of bounds
if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
break;
}
// Don't allow sub region manipulation where it does not make sense or is unsafe.
if((size != details.size) && !(details.flags & block_is_indexable)){
break;
}
// Save page values for restore
unsigned char oldRamPage = RPAGE;
// Set the viewable RAM page
RPAGE = details.RAMPage;
/// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
//
if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
void* bufferToCheck;
// For sub regions, construct an image for verification
if(size != details.size){
// Copy data from destination location to buffer
memcpy(leftOverBuffer, details.RAMAddress, details.size);
// Copy data from rx buffer to buffer over writing old data
memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
bufferToCheck = leftOverBuffer;
}else{
bufferToCheck = RXBufferCurrentPosition;
}
// Verify all tables
if(details.flags & block_is_main_table){
errorID = validateMainTable((mainTable*)bufferToCheck);
}else if(details.flags & block_is_2dus_table){
errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
}// TODO add other table types here
// If the validation failed, report it
if(errorID != 0){
RPAGE = oldRamPage; // Restore the original RAM page, even when getting an error condition.
break;
}
}
// Copy from the RX buffer to the block of RAM
memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
// Check that the write was successful
unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
// Restore the original RAM and flash pages
RPAGE = oldRamPage;
if(index != 0){
errorID = MEMORY_WRITE_ERROR;
}
break;
}
{
// Subtract six to allow for the locationID, size, offset
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// Extract the offset to place the data at
unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
// Extract the size of the data to be stored
unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
// Look up the memory location details
blockDetails details;
lookupBlockDetails(locationID, &details);
// Subtract six to allow for the locationID, size, offset
if((RXCalculatedPayloadLength - 6) != size){
break;
}
// If either of these is zero then this block is not in flash!
if((details.FlashPage == 0) || (details.FlashAddress == 0)){
break;
}
// Check that size and offset describe a region that is not out of bounds
if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
break;
}
// Don't allow sub region manipulation where it does not make sense or is unsafe.
if((size != details.size) && !(details.flags & block_is_indexable)){
break;
}
/// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
//
if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
void* bufferToCheck;
// For sub regions, construct an image for verification
if(size != details.size){
/* Save page value for restore and set the visible page */
unsigned char oldFlashPage = PPAGE;
PPAGE = details.FlashPage;
// Copy data from destination location to buffer
memcpy(leftOverBuffer, details.FlashAddress, details.size);
/* Restore the original flash page */
PPAGE = oldFlashPage;
// Copy data from rx buffer to buffer over writing old data
memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
bufferToCheck = leftOverBuffer;
}else{
bufferToCheck = RXBufferCurrentPosition;
}
// Verify all tables
if(details.flags & block_is_main_table){
errorID = validateMainTable((mainTable*)bufferToCheck);
}else if(details.flags & block_is_2dus_table){
errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
}// TODO add other table types here
// If the validation failed, report it
if(errorID != 0){
break;
}
}
/* Copy the flash details and populate the RAM details with the buffer location */
blockDetails burnDetails;
burnDetails.FlashPage = details.FlashPage;
burnDetails.FlashAddress = details.FlashAddress + offset;
burnDetails.RAMPage = RPAGE;
burnDetails.size = size;
/* Copy from the RX buffer to the block of flash */
errorID = writeBlock(&burnDetails, leftOverBuffer);
if(errorID != 0){
break;
}
/* If present in RAM, update that too */
if((details.RAMPage != 0) && (details.RAMAddress != 0)){
/* Save page values for restore */
unsigned char oldRamPage = RPAGE;
/* Set the viewable RAM page */
RPAGE = details.RAMPage;
/* Copy from the RX buffer to the block of RAM */
memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
/* Check that the write was successful */
unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
/* Restore the original RAM and flash pages */
RPAGE = oldRamPage;
if(index != 0){
errorID = MEMORY_WRITE_ERROR;
}
}
break;
}
{
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// Extract the offset to place the data at
unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
// Extract the size of the data to be stored
unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
/* Look up the memory location details */
blockDetails details;
lookupBlockDetails(locationID, &details);
if((details.RAMPage == 0) || (details.RAMAddress == 0)){
break;
}
// Special behaviour for size of zero which returns the whole block
if((size == 0) && (offset == 0)){
size = details.size;
}
// Check that size and offset describe a region that is not out of bounds
if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
break;
}
// Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
if((size != details.size) && !(details.flags & block_is_indexable)){
break;
}
// This type must have a length field, set that up
*((unsigned short*)TXBufferCurrentPositionHandler) = size;
/* Save page value for restore and set the visible page */
unsigned char oldRamPage = RPAGE;
RPAGE = details.RAMPage;
/* Copy the block of RAM to the TX buffer */
memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.RAMAddress + offset), size);
/* Restore the original RAM and flash pages */
RPAGE = oldRamPage;
break;
}
{
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// Extract the offset to place the data at
unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
// Extract the size of the data to be stored
unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
/* Look up the memory location details */
blockDetails details;
lookupBlockDetails(locationID, &details);
if((details.FlashPage == 0) || (details.FlashAddress == 0)){
break;
}
// Special behaviour for size of zero which returns the whole block
if((size == 0) && (offset == 0)){
size = details.size;
}
// Check that size and offset describe a region that is not out of bounds
if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
break;
}
// Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
if((size != details.size) && !(details.flags & block_is_indexable)){
break;
}
// This type must have a length field, set that up
*((unsigned short*)TXBufferCurrentPositionHandler) = size;
/* Save page value for restore and set the visible page */
unsigned char oldFlashPage = PPAGE;
PPAGE = details.FlashPage;
/* Copy the block of flash to the TX buffer */
memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.FlashAddress + offset), size);
/* Restore the original RAM and flash pages */
PPAGE = oldFlashPage;
break;
}
{
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// Extract the offset to place the data at
unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
// Extract the size of the data to be stored
unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
/* Look up the memory location details */
blockDetails details;
lookupBlockDetails(locationID, &details);
/* Check that all data we need is present */
if((details.RAMPage == 0) || (details.RAMAddress == 0) || (details.FlashPage == 0) || (details.FlashAddress == 0)){
break;
}
// Special behaviour for size of zero which burns the whole block
if((size == 0) && (offset == 0)){
size = details.size;
}
// Check that size and offset describe a region that is not out of bounds
if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
break;
}
// Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
if((size != details.size) && !(details.flags & block_is_indexable)){
break;
}
// adjust details block to feed to represent the subsection of ram and flash that we want to burn down.
details.RAMAddress += offset;
details.FlashAddress += offset;
details.size = size;
/* Write the block down from RAM to Flash */
errorID = writeBlock(&details, leftOverBuffer);
break;
}
case requestDatalogPacket: // Set type through standard configuration methods
{
break;
}
/* Set the length field up */
unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
/* Fill out the log and send */
*localLength = populateBasicDatalog();
break;
}
{
break;
}
unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition);
if(newDatalogType > asyncDatalogLastType){
break;
}
TablesB.SmallTablesB.loggingSettings.datalogStreamType = newDatalogType;
break;
}
{
break;
}
unsigned short length = *((unsigned short*)RXBufferCurrentPosition);
// Make sure the buffer can handle the block
if(length > TX_MAX_PAYLOAD_SIZE){
break;
}
void* address = (void*) *((unsigned short*)RXBufferCurrentPosition);
// Ensure we don't try to read past the end of the address space
if(((unsigned short)address) <= ((0xFFFF - length) + 1)){
// TODO Possibly check and limit ranges
break;
}
unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition);
// Ensure RAM page is valid. Being too high is not possible.
if(RAMPage < RPAGE_MIN){
break;
}
unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition);
// Ensure Flash page is valid. Being too high is not possible.
if(FlashPage < PPAGE_MIN){
break;
}
/* This type must have a length field, set that up */
*((unsigned short*)TXBufferCurrentPositionHandler) = length + 6;
/* Put the request payload into the reply */
*((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address;
*((unsigned short*)TXBufferCurrentPositionHandler) = length;
*((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage;
*((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage;
/* Load the body into place */
memcpy((void*)TXBufferCurrentPositionHandler, address, length);
break;
}
{
break;
}
// Extract the type of list that we want
unsigned char listType = *((unsigned char*)RXBufferCurrentPosition);
// Extract the mask for the qualities that we want
unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition);
// This type must have a length field, set that up
unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler;
// Zero the counter before we start, woops!
*listLength = 0;
unsigned long locationID;
blockDetails details;
for(locationID = 0;locationID < 65536;locationID++){
unsigned short locationIDDoesntExist;
locationIDDoesntExist = lookupBlockDetails((unsigned short)locationID, &details);
if(!locationIDDoesntExist){
if((listType == 0x00) || // get all
((listType == 0x01) && (details.flags & blockDetailsMask)) || // get OR of bits
((listType == 0x02) && (!(~(details.flags) & blockDetailsMask)))){ // get AND of bits
*((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short)locationID;
TXBufferCurrentPositionHandler += 2;
*listLength += 2;
}
}
}
break;
}
{
break;
}
// Extract the RAM location ID
unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
// This type must have a length field, set that up
*((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(blockDetails);
// Write straight to output buffer to save time/code
if(errorID != 0){
break;
}
// Adjust TX buffer position if successful
break;
}
{
/*
* The idea here is to call this function with arguments, and data
* and have the result sent back for comparison with an expected
* result that isn't divulged to the firmware.
*
* It is intended that all testable functions be callable through
* this mechanism and that any number of test executions can be
* performed by an external suite using different parameters and
* data sets and matching expected results.
*
* The usual error mechanism shall be used to indicate some sort of
* either internal or test failure and returned errors shall be
* suitably descriptive to allow diagnosis and fixing of issues.
*/
// Must at least have test ID
break;
}
// grab unit test ID from payload
unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition);
switch(unitTestID){
{
// Must be only the ID
break;
}
*((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID;
break;
}
{
// ID + Value + Table
if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){
break;
}
unsigned short Value = *((unsigned short*)RXBufferCurrentPosition);
unsigned short result = lookupTwoDTableUS(Table, Value);
*((unsigned short*)TXBufferCurrentPositionHandler) = result;
break;
}
// http://issues.freeems.org/view.php?id=156
//
/// TODO @todo test all things listed below:
// lookupPagedMainTableCellValue - pass this RPAGE so that it remains unchanged
// validateMainTable
// validateTwoDTable
// set table values - leave this till last, currently unused by mtx, likely to be removed anyway
// generateDerivedVars - convert to pointers, remove headers, privatise a lot of data!
// calculateFuelAndIgnition - ditto
// scheduling algorithm - ditto
// safeAdd
// safeTrim
// safeScale
// sleep (milliseconds)
// sleepMicro (microseconds)
// checksum
// stringCopy
// compare
// 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)
// init code may be able to be partially checked
// most other code at this stage is ISR code, flash writing code, or could interfere with the running of the engine
// more testable code will appear with time, such as the HAL layer, and most accessory functions.
default:
{
errorID = noSuchUnitTestID;
}
// each case:
// checks length, fails if wrong
// parses data into args
// calls function on data/args
// assembles response OR sets error
// breaks
}
break;
}
{
// see TODO on include at top and modify this line appropriately
if(!(compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME)))){
break;
}
unsigned char localTestMode = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
if(localTestMode > TEST_MODE_BUMP_UP_CYCLES){
break;
}else if((localTestMode == TEST_MODE_STOP) && (RXCalculatedPayloadLength == 1)){
break;
}
// Ensure we succeed at stopping it as quickly as possible.
// Stash mid-test details for return
*((unsigned short*)TXBufferCurrentPositionHandler) = testNumberOfCycles; // Save and return the remaining cycle count
*((unsigned char*)TXBufferCurrentPositionHandler) = KeyUserDebugs.currentEvent; // Save the current event for the ultra-fussy
// Setup the test to stop ASAP
KeyUserDebugs.currentEvent = testEventsPerCycle - 1; // Gets incremented then compared with testEventsPerCycle
testNumberOfCycles = 1; // Gets decremented then compared with zero
break;
}else if((localTestMode == TEST_MODE_BUMP_UP_CYCLES) && (RXCalculatedPayloadLength == 2)){
break;
}
// Get bump value from payload
unsigned char bumpCycles = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
if(bumpCycles == 0){
break;
}
// Bump count by value from payload
testNumberOfCycles += bumpCycles;
// Given that this function is only for situations when A it's getting near to
// zero and B the user is watching, not checking for overflow is reasonable.
*((unsigned char*)TXBufferCurrentPositionHandler) = bumpCycles; // Return the bump size for achaelogical purposes
break;
}else if((localTestMode == TEST_MODE_ITERATIONS) && (RXCalculatedPayloadLength == 24)){
testMode = localTestMode;
// do nothing to fall through, or move other code into here
}else{
break;
}
if(coreStatusA & BENCH_TEST_ON){
break;
}
// Parse the values and return all but the test packet type
testEventsPerCycle = *((unsigned char*)RXBufferCurrentPosition); //100; // @ 10ms = 1s
break;
}
testNumberOfCycles = *((unsigned short*)RXBufferCurrentPosition); //20; // @ 1s = 20s
break;
}
testTicksPerEvent = *((unsigned short*)RXBufferCurrentPosition); //12500; // @ 0.8us = 10ms
break;
}
// Pluck the arrays out of the packet for the loop below
unsigned char* testEventNumbers = RXBufferCurrentPosition;
unsigned short* testPulseWidths = (unsigned short*)RXBufferCurrentPosition;
memcpy((void*)TXBufferCurrentPositionHandler, (void*)testEventNumbers, 18);
// Reset the clock for reading timeout
Clocks.timeoutADCreadingClock = 0; // make this optional, such that we can use real inputs to determine pw and/or dwell.
// Validate and transfer the per-channel data
unsigned char channel;
unsigned char configuredChannels = 6;
for(channel = 0;channel < 6;channel++){
if(testPulseWidths[channel] > ectSwitchOnCodeTime){ // See next block for warning.
// use as-is
outputEventPulseWidthsMath[channel] = testPulseWidths[channel];
outputEventInputEventNumbers[channel] = testEventNumbers[channel];
}else if(testPulseWidths[channel] > 3){
// less than the code time, and not special, error!
// Warning, PWs close to this could be slightly longer than requested, that will change in later revisions.
break;
}else if(testPulseWidths[channel] == 3){
testMode++; // Dirty hack to avoid dealing with Dave for the time being.
}else if(testPulseWidths[channel] == 2){
// use the dwell from the core maths and input vars.
outputEventInputEventNumbers[channel] = testEventNumbers[channel];
}else if(testPulseWidths[channel] == 1){
// use the reference pulse width from the core maths and input vars.
outputEventInputEventNumbers[channel] = testEventNumbers[channel];
}else{ // is zero
// Set this channel to zero for and therefore off, don't set this channel.
outputEventInputEventNumbers[channel] = 0xFF; // Off.
configuredChannels--;
}
}
if(configuredChannels == 0){
break;
}
if(errorID == 0){
// Let the first iteration roll it over to zero.
KeyUserDebugs.currentEvent = 0xFF; // Needs to be here in case of multiple runs, init is not sufficient
if(testEventsPerCycle <= 127){
}else{
break;
}
// Store the time per event in RPM such that it can be updated dynamically
// The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode!
// Un-schedule anything that got scheduled
break;
}
// Trigger decoder interrupt to fire thus starting the loop!
TIE = 0x01; // The ISR does the rest!
// Nothing went wrong, now set flag.
}else{
break;
}
/* http://issues.freeems.org/view.php?id=155
*
* The following block has been left in, as I still do not know why it won't work as intended:
*
* - It should fire all 6 output pins with a 52ms duration pulse, exactly once.
* - 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
* - The interrupts run, but the pin doesn't change state, despite the registers being configured correctly
*
* I've tried quite a bit:
*
* - Moving this code around
* - Checking memory definitions
* - Completely rewriting the output ISR
* - Adding significant debug to output ISR
* - Checking for register contents in output ISR
* - Checking for key things modified in this file
* - General head scratching and confused searching
*/
// outputEventPinNumbers[0] = 0; // 1 ign
// outputEventPinNumbers[1] = 1; // 2 ign
// outputEventPinNumbers[2] = 2; // 3 ign/1 fuel
// outputEventPinNumbers[3] = 3; // 4 ign/2 fuel
// outputEventPinNumbers[4] = 4; // 3 fuel
// outputEventPinNumbers[5] = 5; // 4 fuel
// outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
// outputEventDelayFinalPeriod[1] = decoderMaxCodeTime;
// outputEventDelayFinalPeriod[2] = decoderMaxCodeTime;
// outputEventDelayFinalPeriod[3] = decoderMaxCodeTime;
// outputEventDelayFinalPeriod[4] = decoderMaxCodeTime;
// outputEventDelayFinalPeriod[5] = decoderMaxCodeTime;
// outputEventPulseWidthsMath[0] = SHORTMAX;
// outputEventPulseWidthsMath[1] = SHORTMAX;
// outputEventPulseWidthsMath[2] = SHORTMAX;
// outputEventPulseWidthsMath[3] = SHORTMAX;
// outputEventPulseWidthsMath[4] = SHORTMAX;
// outputEventPulseWidthsMath[5] = SHORTMAX;
//
// unsigned short edgeTimeStamp = TCNT;
// // call sched output with args
// LongTime timeStamp;
// /* Install the low word */
// timeStamp.timeShorts[1] = edgeTimeStamp;
// /* Find out what our timer value means and put it in the high word */
// if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
// timeStamp.timeShorts[0] = timerExtensionClock + 1;
// }else{
// timeStamp.timeShorts[0] = timerExtensionClock;
// }
//
// schedulePortTPin(0, timeStamp);
// schedulePortTPin(1, timeStamp);
// schedulePortTPin(2, timeStamp);
// schedulePortTPin(3, timeStamp);
// schedulePortTPin(4, timeStamp);
// schedulePortTPin(5, timeStamp);
//
// sleep(1000);
}else{
}
break;
}
default:
{
if((RXHeaderPayloadID % 2) == 1){
errorID = invalidPayloadID;
}else{
}
break;
}
}
// Always reply, if errorID is zero it's just an ack.
finaliseAndSend(errorID);
/* Switch reception back on now that we are done with the received data */
}

Here is the call graph for this function:

Here is the caller graph for this function: