// Reset ignorePacket Flag if more than 20ms // have passed and no byte was received void oblifySync() { if ((currentMillis - lastReceivedByteMillis) > 15 && recPos != 0) { inByte = escape; processSerialEvent(); resetReceiveState(0); lastReceivedByteMillis = currentMillis; #ifndef OBLIFY Serial.println("Timeout! Reset ignorePacket"); #endif } }
void parseSerial() { if (hIndex >= 8) // Error { hIndex = 0; Serial.println("Serial Input Overflow"); resetReceiveState(0); } if (inByte != ';') headerData[hIndex++] = inByte; else { headerData[hIndex] = 0; // close string hIndex = 0; if (recPos == 0) // Type { // Header inByte = 0x0; processSerialEvent(); inByte = 0xb; processSerialEvent(); inByte = 0xf; processSerialEvent(); inByte = 0x5; processSerialEvent(); // type uint16_t ttype = (uint16_t)strtoul(headerData,0,10); //Serial.print("Serial Type: "); //Serial.println(ttype); inByte = lowByte(ttype); processSerialEvent(); inByte = highByte(ttype); processSerialEvent(); } else // Flags { uint8_t fflags = (uint8_t)strtoul(headerData,0,10); //Serial.print("Serial Flags: "); //Serial.println(fflags); inByte = fflags; processSerialEvent(); // Server ID inByte = 255; processSerialEvent(); inByte = 255; processSerialEvent(); inByte = 0; processSerialEvent(); //Dest inByte = 0; processSerialEvent(); } } }
/** @brief Serial Communication Interface 0 ISR * * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting * appropriately. Its functions are to send raw bytes out over the wire from a * buffer and to receive bytes from the wire un-escape them, checksum them and * store them in a buffer. * * @todo TODO Move this code into an include file much like the fuel interrupts such that it can be used for multiple UART SCI devices without duplication. * @todo TODO Fix the init code such that this doesn't run at boot without a serail device attached. Clear buffer maybe? or flag clearing/isr enabling ordering? */ void SCI0ISR(){ // OK before flag reading because cleared when SCI0DRL accessed (R or W) DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT4, PORTB); /* Read the flags register */ unsigned char flags = SCI0SR1; /* Note: Combined with reading or writing the data register this also clears the flags. */ /* If either of these flags is set, we need to read the data to clear the flag */ if(flags & (SCISR1_RX_REGISTER_FULL | SCISR1_RX_OVERRUN)){ /* Grab the received byte from the register to clear the flag, whether we want the data or not */ unsigned char rawByte = SCI0DRL; /* If the RX interrupt is enabled do something useful */ if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){ if(flags & (SCISR1_RX_NOISE | SCISR1_RX_FRAMING | SCISR1_RX_PARITY | SCISR1_RX_OVERRUN)){ /* If there is noise on the receive line record it */ if(flags & SCISR1_RX_NOISE){ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_NOISE_ERRORS_OFFSET); KeyUserDebugs.serialHardwareErrors++; } /* If a framing error occurs record it */ if(flags & SCISR1_RX_FRAMING){ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_FRAMING_ERRORS_OFFSET); KeyUserDebugs.serialHardwareErrors++; } /* If a parity error occurs record it */ if(flags & SCISR1_RX_PARITY){ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PARITY_ERRORS_OFFSET); KeyUserDebugs.serialHardwareErrors++; } /* If an overrun occurs record it */ if(flags & SCISR1_RX_OVERRUN){ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_OVERRUN_ERRORS_OFFSET); KeyUserDebugs.serialOverrunErrors++; } resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); }else{ // Process the received data /* Look for a start byte to indicate a new packet */ if(rawByte == START_BYTE){ /* If another interface is using it (Note, clear flag, not normal) */ if(RXBufferContentSourceID & COM_CLEAR_SCI0_INTERFACE_ID){ /* Turn off our reception */ SCI0CR2 &= SCICR2_RX_ISR_DISABLE; }else{ /* If we are using it */ if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ /* Increment the counter */ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_STARTS_INSIDE_A_PACKET_OFFSET); KeyUserDebugs.serialAndCommsCodeErrors++; } /* Reset to us using it unless someone else was */ resetReceiveState(COM_SET_SCI0_INTERFACE_ID); } }else if((unsigned short)RXBufferCurrentPosition >= ((unsigned short)&RXBuffer + RX_BUFFER_SIZE)){ /* Buffer was full, record and reset */ FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_OVER_LENGTH_OFFSET); KeyUserDebugs.serialAndCommsCodeErrors++; resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); }else if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ if(RXStateFlags & RX_SCI_ESCAPED_NEXT){ /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */ RXStateFlags &= RX_SCI_NOT_ESCAPED_NEXT; if(rawByte == ESCAPED_ESCAPE_BYTE){ *RXBufferCurrentPosition++ = ESCAPE_BYTE; }else if(rawByte == ESCAPED_START_BYTE){ *RXBufferCurrentPosition++ = START_BYTE; }else if(rawByte == ESCAPED_STOP_BYTE){ *RXBufferCurrentPosition++ = STOP_BYTE; }else{ /* Otherwise reset and record as data is bad */ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_ESCAPE_PAIR_MISMATCHES_OFFSET); KeyUserDebugs.serialAndCommsCodeErrors++; } }else if(rawByte == ESCAPE_BYTE){ /* Drop the escape and set the flag to indicate that the next byte should be un-escaped. */ RXStateFlags |= RX_SCI_ESCAPED_NEXT; }else if(rawByte == STOP_BYTE){ /* Turn off reception */ SCI0CR2 &= SCICR2_RX_ISR_DISABLE; RXStateFlags |= RX_READY_TO_PROCESS; }else{ *RXBufferCurrentPosition++ = rawByte; } } /* ELSE: Do nothing : drop the byte */ } } } /* If the TX interrupt is enabled check the register empty flag. */ if((SCI0CR2 & SCICR2_TX_ISR_ENABLE) && (flags & SCISR1_TX_REGISTER_EMPTY)){ /* Get the byte to be sent from the buffer */ unsigned char rawValue = *TXBufferCurrentPositionSCI0; if(TXBufferCurrentPositionSCI0 <= TXBufferCurrentPositionHandler){ if(TXByteEscaped == 0){ /* If the raw value needs to be escaped */ if(rawValue == ESCAPE_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_ESCAPE_BYTE; }else if(rawValue == START_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_START_BYTE; }else if(rawValue == STOP_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_STOP_BYTE; }else{ /* Otherwise just send it */ SCI0DRL = rawValue; TXBufferCurrentPositionSCI0++; } }else{ SCI0DRL = TXByteEscaped; TXBufferCurrentPositionSCI0++; TXByteEscaped = 0; } }else{ /* Length is zero */ if(coreStatusA & BIT7){ /* Turn off transmission interrupt */ SCI0CR2 &= (SCICR2_TX_ISR_DISABLE & SCICR2_TX_DISABLE); /* Clear the TX in progress flag */ TXBufferInUseFlags &= COM_CLEAR_SCI0_INTERFACE_ID; coreStatusA &= NBIT7; }else{ coreStatusA |= BIT7; /* Send the stop byte */ SCI0DRL = STOP_BYTE; } } } DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT4, PORTB); }
void processSerialEvent() { // Read the received byte: switch(recPos) { case 0: // HEADER : {0x00,0x0b,0x0f,0x05}; case 1: case 2: case 3: if (Header[recPos] != inByte) { #ifndef OBLIFY Serial.println("Invalid Header"); #endif if (inByte == escape) { resetReceiveState(0); recPos = 1; } else resetReceiveState(1); return; } #ifndef OBLIFY if (recPos == 3) Serial.println("Header OK"); #endif break; case 4: // TYPE r_type = inByte; break; case 5: r_type = UNSIGNED_INT16(inByte,r_type); #ifndef OBLIFY Serial.print("Received Type: "); Serial.println(r_type); #endif if (r_type > PACKET_ID_COUNT) { #ifndef OBLIFY Serial.println("Invalid Packet Type"); #endif resetReceiveState(1); return; } else if (packethandler[r_type] == 0) { #ifndef OBLIFY Serial.println("Handler not implemented"); #endif resetReceiveState(1); return; } break; case 6: // FLAG r_flags = inByte; #ifndef OBLIFY Serial.print("Received Flags: "); Serial.println(r_flags); #endif break; case 7: // SERVER ID case 8: if (inByte != 255) { #ifndef OBLIFY Serial.println("Not from Server"); #endif resetReceiveState(1); return; } break; case 9: destinationID = inByte; break; case 10: destinationID = UNSIGNED_INT16(inByte,destinationID); #ifndef OBLIFY Serial.print("Received Destination ID: "); Serial.println(destinationID); #endif if (destinationID != 0 && destinationID != deviceID) { #ifndef OBLIFY Serial.println("Not for Device"); #endif resetReceiveState(1); } break; default: // PAYLOAD #ifndef OBLIFY if (recPos == 11) Serial.println("Reading Payload"); #endif if (recBufferIndex >= REC_BUFF_SIZE) { #ifndef OBLIFY Serial.println("Receive Buffer Overflow"); #endif resetReceiveState(inByte == escape ? 0 : 1); return; } if (inByte != escape && recBufferIndex < REC_BUFF_SIZE-1) { if (recBufferIndex < REC_BUFF_SIZE - 2) recBuffer[recBufferIndex++] = inByte; #ifndef OBLIFY Serial.print((char)inByte); #endif } else { recBuffer[recBufferIndex] = '\0'; #ifndef OBLIFY Serial.println("\ndone"); #endif packethandler[r_type](); resetReceiveState( inByte == escape ? 0 : 1 ); return; } break; } // switch ++recPos; }
/** @brief Serial Communication Interface 0 ISR * * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting * appropriately. Its functions are to send raw bytes out over the wire from a * buffer and to receive bytes from the wire un-escape them, checksum them and * store them in a buffer. * * @author Fred Cooke * * @todo TODO Move this code into an include file much like the fuel interrupts such that it can be used for multiple UART SCI devices without duplication. * @todo TODO Remove the debug code that uses the IO ports to light LEDs during specific actions. */ void SCI0ISR(){ /* Read the flags register */ unsigned char flags = SCI0SR1; /* Note: Combined with reading or writing the data register this also clears the flags. */ /* Start counting */ unsigned short start = TCNT; /* If the RX interrupt is enabled check RX related flags */ if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){ /* Grab the received byte from the register */ unsigned char rawByte = SCI0DRL; //PORTB |= BIT0; PORTB = ONES; /* Record error conditions always */ unsigned char resetOnError = 0; /* If there is noise on the receive line record it */ if(flags & SCISR1_RX_NOISE){ Counters.serialNoiseErrors++; resetOnError++; }/* If an overrun occurs record it */ if(flags & SCISR1_RX_OVERRUN){ Counters.serialOverrunErrors++; resetOnError++; }/* If a framing error occurs record it */ if(flags & SCISR1_RX_FRAMING){ Counters.serialFramingErrors++; resetOnError++; }/* If a parity error occurs record it */ if(flags & SCISR1_RX_PARITY){ Counters.serialParityErrors++; resetOnError++; } /* Drop out because of error flags */ if(resetOnError){ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); PORTB |= BIT1; return; } /* If there is data waiting to be received */ if(flags & SCISR1_RX_REGISTER_FULL){ PORTB |= BIT2; /* Look for a start bresetReceiveStateyte to indicate a new packet */ if(rawByte == START_BYTE){ PORTM ^= BIT3; /* If another interface is using it (Note, clear flag, not normal) */ if(RXBufferContentSourceID & COM_CLEAR_SCI0_INTERFACE_ID){ /* Turn off our reception */ SCI0CR2 &= SCICR2_RX_DISABLE; SCI0CR2 &= SCICR2_RX_ISR_DISABLE; PORTB |= BIT4; }else{ PORTB |= BIT5; /* If we are using it */ if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ /* Increment the counter */ Counters.serialStartsInsideAPacket++; } /* Reset to us using it unless someone else was */ resetReceiveState(COM_SET_SCI0_INTERFACE_ID); } }else if(RXPacketLengthReceived >= RX_BUFFER_SIZE){ /* Buffer was full, record and reset */ Counters.serialPacketsOverLength++; resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); PORTB |= BIT6; }else if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ if(RXStateFlags & RX_SCI_ESCAPED_NEXT){ PORTB |= BIT7; /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */ RXStateFlags &= RX_SCI_NOT_ESCAPED_NEXT; if(rawByte == ESCAPED_ESCAPE_BYTE){ /* Store and checksum escape byte */ receiveAndIncrement(ESCAPE_BYTE); }else if(rawByte == ESCAPED_START_BYTE){ /* Store and checksum start byte */ receiveAndIncrement(START_BYTE); }else if(rawByte == ESCAPED_STOP_BYTE){ /* Store and checksum stop byte */ receiveAndIncrement(STOP_BYTE); }else{ /* Otherwise reset and record as data is bad */ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); Counters.serialEscapePairMismatches++; } }else if(rawByte == ESCAPE_BYTE){ PORTA |= BIT0; /* Set flag to indicate that the next byte should be un-escaped. */ RXStateFlags |= RX_SCI_ESCAPED_NEXT; }else if(rawByte == STOP_BYTE){ PORTM ^= BIT4; /* Turn off reception */ SCI0CR2 &= SCICR2_RX_DISABLE; SCI0CR2 &= SCICR2_RX_ISR_DISABLE; /* Bring the checksum back to where it should be */ unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1); RXCalculatedChecksum -= RXReceivedChecksum; /* Check that the checksum matches and that the packet is big enough for header,ID,checksum */ if(RXPacketLengthReceived < 4){ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); Counters.commsPacketsUnderMinLength++; }else if(RXCalculatedChecksum == RXReceivedChecksum){ /* If it's OK set process flag */ RXStateFlags |= RX_READY_TO_PROCESS; PORTA |= BIT2; }else{ PORTA |= BIT3; /* Otherwise reset the state and record it */ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); Counters.commsChecksumMismatches++; } }else{ PORTM ^= BIT5; /* If it isn't special process it! */ receiveAndIncrement(rawByte); } }else{ /* Do nothing : drop the byte */ PORTA |= BIT5; } } } /* If the TX interrupt is enabled check the register empty flag. */ if((SCI0CR2 & SCICR2_TX_ISR_ENABLE) && (flags & SCISR1_TX_REGISTER_EMPTY)){ /* Get the byte to be sent from the buffer */ unsigned char rawValue = *TXBufferCurrentPositionSCI0; if(TXPacketLengthToSendSCI0 > 0){ if(TXByteEscaped == 0){ /* If the raw value needs to be escaped */ if(rawValue == ESCAPE_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_ESCAPE_BYTE; }else if(rawValue == START_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_START_BYTE; }else if(rawValue == STOP_BYTE){ SCI0DRL = ESCAPE_BYTE; TXByteEscaped = ESCAPED_STOP_BYTE; }else{ /* Otherwise just send it */ sendAndIncrement(rawValue); } }else{ sendAndIncrement(TXByteEscaped); TXByteEscaped = 0; } }else{ /* Length is zero */ /* Turn off transmission interrupt */ SCI0CR2 &= SCICR2_TX_ISR_DISABLE; /* Send the stop byte */ SCI0DRL = STOP_BYTE; while(!(SCI0SR1 & 0x80)){/* Wait for ever until able to send then move on */} SCI0DRL = STOP_BYTE; // nasty hack that works... means at least one and most 2 stops are sent so stuff works, but is messy... there must be a better way. /* Clear the TX in progress flag */ // TXBufferInUseFlags &= COM_CLEAR_SCI0_INTERFACE_ID; } } /* Record how long the operation took */ RuntimeVars.serialISRRuntime = TCNT - start; }
/** @brief 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. */ void decodePacketAndRespond(){ /* 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){ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_UNDER_LENGTH_OFFSET); KeyUserDebugs.serialAndCommsCodeErrors++; 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){ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_CHECKSUM_MISMATCHES_OFFSET); KeyUserDebugs.serialAndCommsCodeErrors++; 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; RXHeaderFlags = *RXBufferCurrentPosition; RXBufferCurrentPosition++; RXCalculatedPayloadLength--; /* Flag that we are transmitting! */ TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID; // SCI0 only for now... /* Load a blank header into the TX buffer ready for masking */ TXHeaderFlags = TXBufferCurrentPositionHandler; *TXHeaderFlags = 0; TXBufferCurrentPositionHandler++; /* Grab the payload ID for processing and load the return ID */ RXHeaderPayloadID = *((unsigned short*)RXBufferCurrentPosition); *((unsigned short*)TXBufferCurrentPositionHandler) = RXHeaderPayloadID + 1; RXBufferCurrentPosition += 2; TXBufferCurrentPositionHandler += 2; RXCalculatedPayloadLength -= 2; /* 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))){ finaliseAndSend(packetTooShortForSpecifiedFields); resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); return; } /* Subtract checksum to get final length */ RXCalculatedPayloadLength--; if(RXHeaderFlags & HEADER_HAS_SEQUENCE){ *TXBufferCurrentPositionHandler = *RXBufferCurrentPosition; RXBufferCurrentPosition++; TXBufferCurrentPositionHandler++; RXCalculatedPayloadLength--; *TXHeaderFlags |= HEADER_HAS_SEQUENCE; } if(RXHeaderFlags & HEADER_HAS_LENGTH){ RXHeaderPayloadLength = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; RXCalculatedPayloadLength -= 2; /* Already subtracted one for checksum */ if(RXHeaderPayloadLength != RXCalculatedPayloadLength){ finaliseAndSend(payloadLengthHeaderMismatch); resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 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. */ switch (RXHeaderPayloadID){ // FreeEMS Core Comms Interface cases case requestInterfaceVersion: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersion); *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, (void*)&interfaceVersion, sizeof(interfaceVersion)); TXBufferCurrentPositionHandler += sizeof(interfaceVersion); break; } case requestFirmwareVersion: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion); *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, (void*)&firmwareVersion, sizeof(firmwareVersion)); TXBufferCurrentPositionHandler += sizeof(firmwareVersion); break; } case requestMaxPacketSize: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* Load the size into place */ *((unsigned short*)TXBufferCurrentPositionHandler) = RX_BUFFER_SIZE; TXBufferCurrentPositionHandler += 2; break; } case requestEchoPacketReturn: { /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* 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; } case requestSoftSystemReset: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; }else{ // Perform soft system reset _start(); } break; } case requestHardSystemReset: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; }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; } case requestReInitOfSystem: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; }else{ init(); } break; } // FreeEMS Vanilla Firmware Specific cases case clearCountersAndFlagsToZero: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } unsigned short zeroCounter; unsigned char* counterPointer = (unsigned char*) &Counters; for(zeroCounter = 0;zeroCounter < sizeof(Counter);zeroCounter++){ *counterPointer = 0; counterPointer++; } KeyUserDebugs.flaggableFlags = 0; unsigned char* flaggablePointer = (unsigned char*) &Flaggables; for(zeroCounter = 0;zeroCounter < sizeof(Flaggable);zeroCounter++){ *flaggablePointer = 0; flaggablePointer++; } break; } case requestDecoderName: case requestFirmwareBuildDate: case requestCompilerVersion: case requestOperatingSystem: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } unsigned char* stringToSend = 0; switch (RXHeaderPayloadID) { case requestDecoderName: stringToSend = (unsigned char*)decoderName; break; case requestFirmwareBuildDate: stringToSend = (unsigned char*)buildTimeAndDate; break; case requestCompilerVersion: stringToSend = (unsigned char*)compilerVersion; break; case requestOperatingSystem: 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); *TXHeaderFlags |= HEADER_HAS_LENGTH; // Update with length field and string length. TXBufferCurrentPositionHandler += 2 + *((unsigned short*)TXBufferCurrentPositionHandler); break; } case updateBlockInRAM: { // Subtract six to allow for the locationID, size, offset if(RXCalculatedPayloadLength < 7){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // 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))){ errorID = attemptToWriteToReadOnlyBlock; break; } // Subtract six to allow for the locationID, size, offset if((RXCalculatedPayloadLength - 6) != size){ errorID = payloadNotEqualToSpecifiedValue; break; } // If either of these is zero then this block is not in RAM! if((details.RAMPage == 0) || (details.RAMAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = uncheckedTableManipulationNotAllowed; 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; } case updateBlockInFlash: { // Subtract six to allow for the locationID, size, offset if(RXCalculatedPayloadLength < 7){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Look up the memory location details blockDetails details; lookupBlockDetails(locationID, &details); // Subtract six to allow for the locationID, size, offset if((RXCalculatedPayloadLength - 6) != size){ errorID = payloadNotEqualToSpecifiedValue; break; } // If either of these is zero then this block is not in flash! if((details.FlashPage == 0) || (details.FlashAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = uncheckedTableManipulationNotAllowed; 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.RAMAddress = RXBufferCurrentPosition; 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; } case retrieveBlockFromRAM: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); if((details.RAMPage == 0) || (details.RAMAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; break; } // This type must have a length field, set that up *((unsigned short*)TXBufferCurrentPositionHandler) = size; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* 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); TXBufferCurrentPositionHandler += size; /* Restore the original RAM and flash pages */ RPAGE = oldRamPage; break; } case retrieveBlockFromFlash: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); if((details.FlashPage == 0) || (details.FlashAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; break; } // This type must have a length field, set that up *((unsigned short*)TXBufferCurrentPositionHandler) = size; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* 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); TXBufferCurrentPositionHandler += size; /* Restore the original RAM and flash pages */ PPAGE = oldFlashPage; break; } case burnBlockFromRamToFlash: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* 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)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; 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 { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* Set the length field up */ *TXHeaderFlags |= HEADER_HAS_LENGTH; unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler; TXBufferCurrentPositionHandler += 2; /* Fill out the log and send */ *localLength = populateBasicDatalog(); break; } case setAsyncDatalogType: { if(RXCalculatedPayloadLength != 1){ errorID = payloadLengthTypeMismatch; break; } unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition); if(newDatalogType > asyncDatalogLastType){ errorID = noSuchAsyncDatalogType; break; } TablesB.SmallTablesB.loggingSettings.datalogStreamType = newDatalogType; break; } case retrieveArbitraryMemory: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } unsigned short length = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Make sure the buffer can handle the block if(length > TX_MAX_PAYLOAD_SIZE){ errorID = requestedLengthTooLarge; break; } void* address = (void*) *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // 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 errorID = requestedAddressDisallowed; break; } unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Ensure RAM page is valid. Being too high is not possible. if(RAMPage < RPAGE_MIN){ errorID = requestedRAMPageInvalid; break; } unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Ensure Flash page is valid. Being too high is not possible. if(FlashPage < PPAGE_MIN){ errorID = requestedFlashPageInvalid; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = length + 6; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Put the request payload into the reply */ *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address; TXBufferCurrentPositionHandler += 2; *((unsigned short*)TXBufferCurrentPositionHandler) = length; TXBufferCurrentPositionHandler += 2; *((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage; TXBufferCurrentPositionHandler++; *((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage; TXBufferCurrentPositionHandler++; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, address, length); TXBufferCurrentPositionHandler += length; break; } case retrieveListOfLocationIDs: { if(RXCalculatedPayloadLength != 3){ errorID = payloadLengthTypeMismatch; break; } // Extract the type of list that we want unsigned char listType = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Extract the mask for the qualities that we want unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // This type must have a length field, set that up unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; // 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; } case retrieveLocationIDDetails: { if(RXCalculatedPayloadLength != 2){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // This type must have a length field, set that up *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(blockDetails); *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; // Write straight to output buffer to save time/code errorID = lookupBlockDetails(locationID, (blockDetails*)TXBufferCurrentPositionHandler); if(errorID != 0){ break; } // Adjust TX buffer position if successful TXBufferCurrentPositionHandler += sizeof(blockDetails); break; } case requestUnitTestOverSerial: { /* * 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 if(RXCalculatedPayloadLength < 2){ errorID = payloadLengthTypeMismatch; break; } // grab unit test ID from payload unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; switch(unitTestID){ case testEmptyTest: { // Must be only the ID if(RXCalculatedPayloadLength != 2){ errorID = payloadShorterThanRequiredForTest; break; } *((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID; TXBufferCurrentPositionHandler +=2; break; } case testTwoDTableUSLookup: { // ID + Value + Table if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){ errorID = payloadShorterThanRequiredForTest; break; } unsigned short Value = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; twoDTableUS* Table = ((twoDTableUS*)RXBufferCurrentPosition); RXBufferCurrentPosition += sizeof(twoDTableUS); unsigned short result = lookupTwoDTableUS(Table, Value); *((unsigned short*)TXBufferCurrentPositionHandler) = result; TXBufferCurrentPositionHandler +=2; 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; } case startBenchTestSequence: { // 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)))){ if(RXCalculatedPayloadLength < 1){ errorID = payloadLengthTypeMismatch; break; } unsigned char localTestMode = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now. RXBufferCurrentPosition++; if(localTestMode > TEST_MODE_BUMP_UP_CYCLES){ errorID = unimplementedTestMode; break; }else if((localTestMode == TEST_MODE_STOP) && (RXCalculatedPayloadLength == 1)){ if(!(coreStatusA & BENCH_TEST_ON)){ errorID = benchTestNotRunningToStop; break; } // Ensure we succeed at stopping it as quickly as possible. ATOMIC_START(); KeyUserDebugs.currentEvent = testEventsPerCycle - 1; // Gets incremented then compared with testEventsPerCycle testNumberOfCycles = 1; // Gets decremented then compared with zero ATOMIC_END(); // eventually save and return where it got to break; }else if((localTestMode == TEST_MODE_BUMP_UP_CYCLES) && (RXCalculatedPayloadLength == 2)){ if(!(coreStatusA & BENCH_TEST_ON)){ errorID = benchTestNotRunningToBump; break; } // Get bump value from payload unsigned char bumpCycles = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now. RXBufferCurrentPosition++; if(bumpCycles == 0){ errorID = bumpingByZeroMakesNoSense; 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. break; }else if((localTestMode == TEST_MODE_ITERATIONS) && (RXCalculatedPayloadLength == 24)){ testMode = localTestMode; // do nothing to fall through, or move other code into here }else{ errorID = packetSizeWrongForTestMode; break; } if(coreStatusA & BENCH_TEST_ON){ errorID = benchTestAlreadyRunning; break; } testEventsPerCycle = *((unsigned char*)RXBufferCurrentPosition); //100; // @ 10ms = 1s RXBufferCurrentPosition++; if(testEventsPerCycle == 0){ errorID = invalidEventsPerCycle; break; } testNumberOfCycles = *((unsigned short*)RXBufferCurrentPosition); //20; // @ 1s = 20s RXBufferCurrentPosition += 2; if(testNumberOfCycles == 0){ errorID = invalidNumberOfCycles; break; } testTicksPerEvent = *((unsigned short*)RXBufferCurrentPosition); //12500; // @ 0.8us = 10ms RXBufferCurrentPosition += 2; if(testTicksPerEvent < decoderMaxCodeTime){ errorID = tooShortOfAnEventPeriod; break; } // Pluck the arrays out of the packet for the loop below unsigned char* testEventNumbers = RXBufferCurrentPosition; RXBufferCurrentPosition += 6; unsigned short* testPulseWidths = (unsigned short*)RXBufferCurrentPosition; RXBufferCurrentPosition += 12; // 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] > injectorSwitchOnCodeTime){ // See next block for warning. // use as-is outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime; outputEventPulseWidthsMath[channel] = testPulseWidths[channel]; outputEventInputEventNumbers[channel] = testEventNumbers[channel]; }else if(testPulseWidths[channel] > 3){ // less than the code time, and not special, error! errorID = tooShortOfAPulseWidthToTest; // 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. testNumberOfMissing = channel; }else if(testPulseWidths[channel] == 2){ // use the dwell from the core maths and input vars. outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime; outputEventPulseWidthsMath[channel] = DerivedVars->Dwell; outputEventInputEventNumbers[channel] = testEventNumbers[channel]; }else if(testPulseWidths[channel] == 1){ // use the reference pulse width from the core maths and input vars. outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime; outputEventPulseWidthsMath[channel] = DerivedVars->RefPW; 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){ errorID = noChannelsConfiguredToTest; 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(testMode == TEST_MODE_DODGY_MISSING_TOOTH){ if(testEventsPerCycle <= 127){ testEventsPerCycle *= 2; }else{ errorID = tooManyEventsPerCycleMissingTth; break; } // Store the time per event in RPM such that it can be updated dynamically CoreVars->RPM = testTicksPerEvent; // The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode! // Un-schedule anything that got scheduled outputEventInputEventNumbers[2] = 0xFF; outputEventInputEventNumbers[3] = 0xFF; outputEventInputEventNumbers[4] = 0xFF; outputEventInputEventNumbers[5] = 0xFF; }else if(testMode > TEST_MODE_DODGY_MISSING_TOOTH){ errorID = unimplementedTestMode; break; } // Trigger decoder interrupt to fire thus starting the loop! TIE = 0x01; // The ISR does the rest! // Nothing went wrong, now set flag. coreStatusA |= BENCH_TEST_ON; }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{ errorID = thisIsNotTheBenchTestDecoder; } break; } default: { if((RXHeaderPayloadID % 2) == 1){ errorID = invalidPayloadID; }else{ errorID = unrecognisedPayloadID; } 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 */ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); }
/** @brief 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. * * @author Fred Cooke */ void decodePacketAndRespond(){ /* Extract and build up the header fields */ RXBufferCurrentPosition = (unsigned char*)&RXBuffer; TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer; /* Initialised here such that override is possible */ TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer; TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer; /* 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 */ RXHeaderFlags = *RXBufferCurrentPosition; RXBufferCurrentPosition++; RXCalculatedPayloadLength--; /* Flag that we are transmitting! */ TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID; // SCI0 only for now... /* Load a blank header into the TX buffer ready for masking */ unsigned char* TXHeaderFlags = TXBufferCurrentPositionHandler; *TXHeaderFlags = 0; TXBufferCurrentPositionHandler++; /* Grab the payload ID for processing and load the return ID */ RXHeaderPayloadID = *((unsigned short*)RXBufferCurrentPosition); *((unsigned short*)TXBufferCurrentPositionHandler) = RXHeaderPayloadID + 1; RXBufferCurrentPosition += 2; TXBufferCurrentPositionHandler += 2; RXCalculatedPayloadLength -= 2; /* 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))){ finaliseAndSend(packetTooShortForSpecifiedFields); resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); return; } /* Subtract checksum to get final length */ RXCalculatedPayloadLength--; if(RXHeaderFlags & HEADER_HAS_SEQUENCE){ *TXBufferCurrentPositionHandler = *RXBufferCurrentPosition; RXBufferCurrentPosition++; TXBufferCurrentPositionHandler++; RXCalculatedPayloadLength--; *TXHeaderFlags |= HEADER_HAS_SEQUENCE; } if(RXHeaderFlags & HEADER_HAS_LENGTH){ RXHeaderPayloadLength = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; RXCalculatedPayloadLength -= 2; /* Already subtracted one for checksum */ if(RXHeaderPayloadLength != RXCalculatedPayloadLength){ finaliseAndSend(payloadLengthHeaderMismatch); resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 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 it's 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. */ switch (RXHeaderPayloadID){ case requestInterfaceVersion: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersionAndType); *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, (void*)&interfaceVersionAndType, sizeof(interfaceVersionAndType)); TXBufferCurrentPositionHandler += sizeof(interfaceVersionAndType); break; } case requestFirmwareVersion: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion); *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, (void*)&firmwareVersion, sizeof(firmwareVersion)); TXBufferCurrentPositionHandler += sizeof(firmwareVersion); break; } case requestMaxPacketSize: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* Load the size into place */ *((unsigned short*)TXBufferCurrentPositionHandler) = RX_BUFFER_SIZE; TXBufferCurrentPositionHandler += 2; break; } case requestEchoPacketReturn: { /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* 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; } case requestSoftSystemReset: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* Perform soft system reset */ _start(); } case requestHardSystemReset: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } /* 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. */ } case requestReInitOfSystem: { if(RXCalculatedPayloadLength != 0){ errorID = payloadLengthTypeMismatch; break; } init(); break; } case updateBlockInRAM: { // Subtract six to allow for the locationID, size, offset if(RXCalculatedPayloadLength < 7){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Look up the memory location details blockDetails details; lookupBlockDetails(locationID, &details); // Subtract six to allow for the locationID, size, offset if((RXCalculatedPayloadLength - 6) != size){ errorID = payloadNotEqualToSpecifiedValue; break; } // If either of these is zero then this block is not in RAM! if((details.RAMPage == 0) || (details.RAMAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = uncheckedTableManipulationNotAllowed; 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){ 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; } case updateBlockInFlash: { // Subtract six to allow for the locationID, size, offset if(RXCalculatedPayloadLength < 7){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Look up the memory location details blockDetails details; lookupBlockDetails(locationID, &details); // Subtract six to allow for the locationID, size, offset if((RXCalculatedPayloadLength - 6) != size){ errorID = payloadNotEqualToSpecifiedValue; break; } // If either of these is zero then this block is not in flash! if((details.FlashPage == 0) || (details.FlashAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = uncheckedTableManipulationNotAllowed; 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.RAMAddress = RXBufferCurrentPosition; 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; } case retrieveBlockFromRAM: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); if((details.RAMPage == 0) || (details.RAMAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; break; } /* 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); TXBufferCurrentPositionHandler += size; /* Restore the original RAM and flash pages */ RPAGE = oldRamPage; break; } case retrieveBlockFromFlash: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); if((details.FlashPage == 0) || (details.FlashAddress == 0)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; break; } /* 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); TXBufferCurrentPositionHandler += size; /* Restore the original RAM and flash pages */ PPAGE = oldFlashPage; break; } case burnBlockFromRamToFlash: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the offset to place the data at unsigned short offset = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Extract the size of the data to be stored unsigned short size = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* 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)){ errorID = invalidMemoryActionForID; 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))){ errorID = invalidSizeOffsetCombination; 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)){ errorID = doesNotMakeSenseToRetrievePartially; 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 adjustMainTableCell: { if(RXCalculatedPayloadLength != 8){ errorID = payloadLengthTypeMismatch; break; } /* Extract the flash location ID from the received data */ unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); /* Check the ID to ensure it is a main table */ if(!(details.flags & block_is_main_table)){ errorID = invalidIDForMainTableAction; break; } /* Extract the cell value and coordinates */ unsigned short RPMIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; unsigned short LoadIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; unsigned short cellValue = *((unsigned short*)RXBufferCurrentPosition); /* Attempt to set the value */ errorID = setPagedMainTableCellValue(details.RAMPage, details.RAMAddress, RPMIndex, LoadIndex, cellValue); break; } case adjustMainTableRPMAxis: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } /* Extract the flash location ID from the received data */ unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); /* Check the ID to ensure it is a main table */ if(!(details.flags & block_is_main_table)){ errorID = invalidIDForMainTableAction; break; } /* Extract the cell value and coordinates */ unsigned short RPMIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; unsigned short RPMValue = *((unsigned short*)RXBufferCurrentPosition); /* Attempt to set the value */ errorID = setPagedMainTableRPMValue(details.RAMPage, details.RAMAddress, RPMIndex, RPMValue); break; } case adjustMainTableLoadAxis: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } /* Extract the flash location ID from the received data */ unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); /* Check the ID to ensure it is a main table */ if(!(details.flags & block_is_main_table)){ errorID = invalidIDForMainTableAction; break; } /* Extract the cell value and coordinates */ unsigned short LoadIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; unsigned short LoadValue = *((unsigned short*)RXBufferCurrentPosition); /* Attempt to set the value */ errorID = setPagedMainTableLoadValue(details.RAMPage, details.RAMAddress, LoadIndex, LoadValue); break; } case adjust2dTableAxis: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } /* Extract the flash location ID from the received data */ unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); /* Check the ID to ensure it is a 2d table */ if(!(details.flags & block_is_2dus_table)){ errorID = invalidIDForTwoDTableAction; break; } /* Extract the cell value and coordinates */ unsigned short axisIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; unsigned short axisValue = *((unsigned short*)RXBufferCurrentPosition); /* Attempt to set the value */ errorID = setPagedTwoDTableAxisValue(details.RAMPage, details.RAMAddress, axisIndex, axisValue); break; } case adjust2dTableCell: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } /* Extract the flash location ID from the received data */ unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; /* Look up the memory location details */ blockDetails details; lookupBlockDetails(locationID, &details); /* Check the ID to ensure it is a 2d table */ if(!(details.flags & block_is_2dus_table)){ errorID = invalidIDForTwoDTableAction; break; } /* Extract the cell value and coordinates */ unsigned short cellIndex = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition -= 2; unsigned short cellValue = *((unsigned short*)RXBufferCurrentPosition); /* Attempt to set the value */ errorID = setPagedTwoDTableCellValue(details.RAMPage, details.RAMAddress, cellIndex, cellValue); break; } case requestBasicDatalog: { if((RXCalculatedPayloadLength > 2) || (RXCalculatedPayloadLength == 1)){ errorID = payloadLengthTypeMismatch; break; }else if(RXCalculatedPayloadLength == 2){ unsigned short newConfiguredLength = *((unsigned short*)RXBufferCurrentPosition); if(newConfiguredLength > maxBasicDatalogLength){ errorID = datalogLengthExceedsMax; break; }else{ configuredBasicDatalogLength = newConfiguredLength; } }// fall through to use existing configured length /* Set the length field up */ *TXHeaderFlags |= HEADER_HAS_LENGTH; *(unsigned short*)TXBufferCurrentPositionHandler = configuredBasicDatalogLength; TXBufferCurrentPositionHandler += 2; /* Fill out the log and send */ populateBasicDatalog(); break; } case requestConfigurableDatalog: { /// perform function TODO @todo REWORK review this errorID = unimplementedFunction; break; } case setAsyncDatalogType: { if(RXCalculatedPayloadLength != 1){ errorID = payloadLengthTypeMismatch; break; } unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition); if(newDatalogType > 0x01){ errorID = noSuchAsyncDatalogType; break; } TablesB.SmallTablesB.datalogStreamType = newDatalogType; break; } case forwardPacketOverCAN: { /// perform function TODO @todo REWORK review this errorID = unimplementedFunction; break; } case forwardPacketOverOtherUART: { /// perform function TODO @todo REWORK review this errorID = unimplementedFunction; break; } case retrieveArbitraryMemory: { if(RXCalculatedPayloadLength != 6){ errorID = payloadLengthTypeMismatch; break; } unsigned short length = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Make sure the buffer can handle the block if(length > TX_MAX_PAYLOAD_SIZE){ errorID = requestedLengthTooLarge; break; } void* address = (void*) *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // 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 errorID = requestedAddressDisallowed; break; } unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Ensure RAM page is valid. Being too high is not possible. if(RAMPage < RPAGE_MIN){ errorID = requestedRAMPageInvalid; break; } unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Ensure Flash page is valid. Being too high is not possible. if(FlashPage < PPAGE_MIN){ errorID = requestedFlashPageInvalid; break; } /* This type must have a length field, set that up */ *((unsigned short*)TXBufferCurrentPositionHandler) = length + 6; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; /* Put the request payload into the reply */ *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address; TXBufferCurrentPositionHandler += 2; *((unsigned short*)TXBufferCurrentPositionHandler) = length; TXBufferCurrentPositionHandler += 2; *((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage; TXBufferCurrentPositionHandler++; *((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage; TXBufferCurrentPositionHandler++; /* Load the body into place */ memcpy((void*)TXBufferCurrentPositionHandler, address, length); TXBufferCurrentPositionHandler += length; break; } case retrieveListOfLocationIDs: { if(RXCalculatedPayloadLength != 3){ errorID = payloadLengthTypeMismatch; break; } // Extract the type of list that we want unsigned char listType = *((unsigned char*)RXBufferCurrentPosition); RXBufferCurrentPosition++; // Extract the mask for the qualities that we want unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // This type must have a length field, set that up unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler; *TXHeaderFlags |= HEADER_HAS_LENGTH; TXBufferCurrentPositionHandler += 2; // 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; } case retrieveLocationIDDetails: { if(RXCalculatedPayloadLength != 2){ errorID = payloadLengthTypeMismatch; break; } // Extract the RAM location ID unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; // Write straight to output buffer to save time/code errorID = lookupBlockDetails(locationID, (blockDetails*)TXBufferCurrentPositionHandler); if(errorID != 0){ break; } // Adjust TX buffer position if successful TXBufferCurrentPositionHandler += sizeof(blockDetails); break; } case requestUnitTestOverSerial: { /* * 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 if(RXCalculatedPayloadLength < 2){ errorID = payloadLengthTypeMismatch; break; } // grab unit test ID from payload unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; switch(unitTestID){ case emptyTest: { // Must be only the ID if(RXCalculatedPayloadLength != 2){ errorID = payloadShorterThanRequiredForTest; break; } *((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID; TXBufferCurrentPositionHandler +=2; break; } case twoDTableUSLookup: { // ID + Value + Table if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){ errorID = payloadShorterThanRequiredForTest; break; } unsigned short Value = *((unsigned short*)RXBufferCurrentPosition); RXBufferCurrentPosition += 2; twoDTableUS* Table = ((twoDTableUS*)RXBufferCurrentPosition); RXBufferCurrentPosition += sizeof(twoDTableUS); unsigned short result = lookupTwoDTableUS(Table, Value); *((unsigned short*)TXBufferCurrentPositionHandler) = result; TXBufferCurrentPositionHandler +=2; break; } 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; } default: { if((RXHeaderPayloadID % 2) == 1){ errorID = invalidPayloadID; }else{ errorID = unrecognisedPayloadID; } } } // 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 */ resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); PORTK |= BIT0; }