36#ifdef _OPENLCB_BOOTLOADER_HXX_
37#error Bootloader.hxx should be included only once.
41#define _OPENLCB_BOOTLOADER_HXX_
80 struct can_frame input_frame;
81 struct can_frame output_frame;
82 unsigned input_frame_full : 1;
83 unsigned output_frame_full : 1;
84 unsigned request_reset : 1;
85#ifdef BOOTLOADER_STREAM
86 unsigned stream_pending : 1;
87 unsigned stream_open : 1;
88 unsigned stream_proceed_pending : 1;
90#ifdef BOOTLOADER_DATAGRAM
91 unsigned incoming_datagram_pending : 1;
92 unsigned datagram_write_pending : 1;
95 unsigned datagram_output_pending : 1;
97 unsigned datagram_reply_waiting : 1;
104 uint8_t datagram_dlc;
105 uint8_t datagram_offset;
106 uint8_t datagram_payload[14];
111#ifdef BOOTLOADER_STREAM
113 uint8_t stream_src_id;
115 uint16_t stream_buffer_size;
118 int stream_buffer_remaining;
122 uintptr_t write_buffer_offset;
124 unsigned write_buffer_index;
133#ifdef BOOTLOADER_STREAM
134#ifndef WRITE_BUFFER_SIZE
138#define WRITE_BUFFER_SIZE 1024
143#define WRITE_BUFFER_SIZE 64
150#define FLASH_SPACE (MemoryConfigDefs::SPACE_FIRMWARE)
152#define STREAM_ID 0x5A
166#ifdef BOOTLOADER_STREAM
168#define PIP_REPLY_VALUE \
169 (Defs::DATAGRAM | Defs::STREAM | Defs::MEMORY_CONFIGURATION | \
170 Defs::FIRMWARE_UPGRADE_ACTIVE)
173#define PIP_REPLY_VALUE \
174 (Defs::DATAGRAM | Defs::MEMORY_CONFIGURATION | \
175 Defs::FIRMWARE_UPGRADE_ACTIVE)
192 CLR_CAN_FRAME_RTR(
state_.output_frame);
193 CLR_CAN_FRAME_ERR(
state_.output_frame);
194 SET_CAN_FRAME_EFF(
state_.output_frame);
195 state_.output_frame.can_dlc = 0;
196 state_.output_frame_full = 1;
209 SET_CAN_FRAME_ID_EFF(
state_.output_frame,
id);
220 GET_CAN_FRAME_ID_EFF(
state_.input_frame)))
226 SET_CAN_FRAME_ID_EFF(
state_.output_frame,
id);
227 state_.output_frame.can_dlc = 2;
228 state_.output_frame.data[0] = (alias >> 8) & 0xf;
229 state_.output_frame.data[1] = alias & 0xff;
236 for (
int i = 5; i >= 0; --i)
238 state_.output_frame.data[i] = node_id & 0xff;
241 state_.output_frame.can_dlc = 6;
252 if (
state_.input_frame.can_dlc != 6)
255 for (
int i = 5; i >= 0; --i)
257 if (
state_.input_frame.data[i] != (node_id & 0xff))
268 state_.output_frame.can_dlc = 4;
269 state_.output_frame.data[2] = error_code >> 8;
270 state_.output_frame.data[3] = error_code & 0xff;
279 state_.input_frame_full = 0;
289 return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
300 state_.datagram_payload[1] |= 0x08;
301 state_.datagram_payload[
state_.datagram_dlc++] = error_code >> 8;
302 state_.datagram_payload[
state_.datagram_dlc++] = error_code & 0xff;
320 const void *flash_min;
321 const void *flash_max;
324 if (
state_.write_buffer_offset >=
325 ((uintptr_t)flash_max - (uintptr_t)flash_min))
329 state_.write_buffer_offset += (uintptr_t)flash_min;
338 const void *address =
339 reinterpret_cast<const void *
>(
state_.write_buffer_offset);
340 const void *page_start =
nullptr;
341 uint32_t page_length_bytes = 0;
343 if (page_start == address)
349 state_.write_buffer_index);
350 state_.write_buffer_offset +=
state_.write_buffer_index;
351 state_.write_buffer_index = 0;
358 uint8_t command =
state_.input_frame.data[1];
363 if (
state_.input_frame.can_dlc < 3 ||
382 if (
state_.input_frame.can_dlc < 3 ||
403 state_.input_frame_full = 0;
406#ifdef BOOTLOADER_DATAGRAM
411 if (
state_.incoming_datagram_pending)
414 if (src ==
state_.write_src_alias)
424 if (
state_.input_frame.can_dlc < 8)
437 state_.write_buffer_offset =
445 state_.incoming_datagram_pending = 1;
446 state_.datagram_write_pending = 1;
451 state_.write_buffer_index = 1;
461 state_.input_frame_full = 0;
465#ifdef BOOTLOADER_STREAM
468 if (
state_.datagram_output_pending ||
state_.stream_open)
475 if (
state_.input_frame.can_dlc < 7)
484 state_.input_frame_full = 0;
488 memcpy(
state_.datagram_payload,
state_.input_frame.data,
489 state_.input_frame.can_dlc - 1);
491 state_.datagram_output_pending = 1;
492 state_.output_frame.data[
state_.output_frame.can_dlc++] =
496 state_.datagram_offset = 0;
503 state_.stream_pending = 1;
506 state_.write_buffer_index = 0;
507 state_.write_buffer_offset =
532 case Defs::MTI_PROTOCOL_SUPPORT_INQUIRY:
534 if (
state_.output_frame_full)
540 state_.output_frame.can_dlc = 8;
544 case Defs::MTI_VERIFY_NODE_ID_ADDRESSED:
546 if (
state_.output_frame_full)
553 state_.input_frame_full = 0;
556 case Defs::MTI_DATAGRAM_OK:
558 if (
state_.datagram_reply_waiting)
560 state_.datagram_reply_waiting = 0;
561 state_.datagram_output_pending = 0;
565#ifdef BOOTLOADER_STREAM
566 case Defs::MTI_STREAM_INITIATE_REQUEST:
568 if (
state_.output_frame_full)
573 if (
state_.input_frame.can_dlc < 7)
580 state_.output_frame.can_dlc = 8;
581 state_.output_frame.data[6] =
state_.input_frame.data[6];
583 if (!
state_.stream_pending ||
587 state_.output_frame.data[2] = 0;
588 state_.output_frame.data[3] = 0;
589 state_.output_frame.data[4] =
592 state_.output_frame.data[5] = 0b00100000;
596 uint16_t proposed_size = (
state_.input_frame.data[2] << 8) |
597 state_.input_frame.data[3];
598 uint16_t final_size = WRITE_BUFFER_SIZE;
599 while (final_size > proposed_size)
603 state_.stream_buffer_size = final_size;
604 state_.stream_buffer_remaining =
state_.stream_buffer_size;
605 state_.output_frame.data[2] =
state_.stream_buffer_size >> 8;
606 state_.output_frame.data[3] =
state_.stream_buffer_size & 0xff;
607 state_.output_frame.data[4] = 0x80;
608 state_.output_frame.data[5] = 0x00;
610 state_.stream_pending = 0;
615 case Defs::MTI_STREAM_COMPLETE:
623 if (
state_.output_frame_full)
633 state_.input_frame_full = 0;
642 case Defs::MTI_VERIFY_NODE_ID_GLOBAL:
644 if (
state_.output_frame_full)
651 state_.input_frame_full = 0;
658 state_.input_frame_full = 0;
663#ifdef BOOTLOADER_STREAM
668 state_.stream_pending = 0;
669 state_.stream_proceed_pending = 0;
671 state_.write_src_alias = 0;
672 state_.stream_buffer_size = 0;
673 state_.stream_buffer_remaining = 0;
674 state_.write_buffer_index = 0;
675 state_.write_buffer_offset = 0;
678void handle_stream_data()
683 if (
state_.output_frame_full)
689 state_.input_frame_full = 0;
692 int len =
state_.input_frame.can_dlc - 1;
693 if (WRITE_BUFFER_SIZE <
state_.write_buffer_index + len)
695 if (
state_.output_frame_full)
701 state_.input_frame_full = 0;
704 state_.input_frame_full = 0;
706 &
state_.input_frame.data[1], len);
707 state_.write_buffer_index += len;
708 state_.stream_buffer_remaining -= len;
709 if (
state_.stream_buffer_remaining <= 0)
711 state_.stream_proceed_pending = 1;
712 state_.stream_buffer_remaining +=
state_.stream_buffer_size;
714 if (
state_.write_buffer_index >= WRITE_BUFFER_SIZE)
722 if (!
state_.stream_open ||
729 if (
state_.output_frame_full)
735 state_.input_frame_full = 0;
745 if (
state_.write_buffer_index)
750 state_.input_frame_full = 0;
758 if (IS_CAN_FRAME_ERR(
state_.input_frame) ||
759 IS_CAN_FRAME_RTR(
state_.input_frame) ||
760 !IS_CAN_FRAME_EFF(
state_.input_frame))
762 state_.input_frame_full = 0;
765 uint32_t can_id = GET_CAN_FRAME_ID_EFF(
state_.input_frame);
766 int dlc =
state_.input_frame.can_dlc;
770 state_.input_frame_full = 0;
773 else if ((can_id & 0xfff) ==
state_.alias)
779 if (
state_.output_frame_full)
793 state_.input_frame_full = 0;
801 if (
state_.output_frame_full)
811 state_.input_frame_full = 0;
814 else if ((can_id >> 12) == (0x1A000 |
state_.alias) ||
815 (can_id >> 12) == (0x1B000 |
state_.alias))
821 if (
state_.output_frame_full)
835#ifdef BOOTLOADER_DATAGRAM
836 else if ((can_id >> 12) == (0x1C000 |
state_.alias) ||
837 (can_id >> 12) == (0x1D000 |
state_.alias))
839 if (!
state_.incoming_datagram_pending ||
842 if (
state_.output_frame_full)
852 state_.write_buffer_index +=
state_.input_frame.can_dlc;
859 state_.incoming_datagram_pending = 0;
860 state_.datagram_write_pending = 0;
863 state_.input_frame_full = 0;
867#ifdef BOOTLOADER_STREAM
868 else if ((can_id >> 12) == (0x1F000 |
state_.alias) && dlc > 1)
870 return handle_stream_data();
873 else if ((can_id >> 24) == 0x19)
877 if (Defs::get_mti_address(mti) &&
state_.input_frame.can_dlc >= 2 &&
878 (
state_.input_frame.data[0] & 0xf) == ((
state_.alias >> 8) & 0xf) &&
883 else if (!Defs::get_mti_address(mti))
889 state_.input_frame_full = 0;
897 switch (
state_.init_state)
909 state_.alias = (node & 0xfff) ^ ((node >> 12) & 0xfff) ^
910 ((node >> 24) & 0xfff) ^ ((node >> 36) & 0xfff);
918 (
state_.alias + ((uint16_t(node)) & 0xffe) + 1759) &
971 case SEND_NMRANET_INIT:
992 if (!
state_.datagram_offset)
994 if (
state_.datagram_dlc <= 8)
1003 else if (
state_.datagram_dlc -
state_.datagram_offset <= 8)
1013 SET_CAN_FRAME_ID_EFF(
state_.output_frame,
id);
1014 int len = std::min(
state_.datagram_dlc -
state_.datagram_offset, 8);
1015 memcpy(
state_.output_frame.data,
1016 &
state_.datagram_payload[
state_.datagram_offset], len);
1017 state_.datagram_offset += len;
1018 state_.output_frame.can_dlc = len;
1021 state_.datagram_reply_waiting = 1;
1034 if (!request && csum_ok)
1057 state_.input_frame_full = 1;
1060 (
state_.input_frame_full ||
state_.output_frame_full ||
1061 state_.init_state != INITIALIZED ||
1062 (
state_.datagram_output_pending
1072#ifdef BOOTLOADER_LOOP_HOOK
1073 BOOTLOADER_LOOP_HOOK();
1077 state_.output_frame_full = 0;
1085 if (
state_.input_frame_full)
1089 if (
state_.init_state != INITIALIZED && !
state_.output_frame_full)
1093#ifdef BOOTLOADER_STREAM
1094 if (
state_.stream_proceed_pending && !
state_.output_frame_full)
1097 Defs::MTI_STREAM_PROCEED,
state_.write_src_alias);
1098 state_.stream_proceed_pending = 0;
1099 state_.output_frame.data[
state_.output_frame.can_dlc++] =
1102 state_.output_frame.data[
state_.output_frame.can_dlc++] = 0;
1103 state_.output_frame.data[
state_.output_frame.can_dlc++] = 0;
1106 if (
state_.datagram_output_pending && !
state_.datagram_reply_waiting &&
1107 !
state_.output_frame_full)
bool check_application_checksum()
void set_can_frame_global(Defs::MTI mti)
Sets up the outgoing frame buffer for a global OpenLCB packet.
void bootloader_entry()
Main entry point for MCU-based bootloaders. Never returns.
void reset_stream_state()
Clears out the stream state in state_.
void setup_can_frame()
Prepares the outgoing frame buffer for a frame to be sent.
void handle_addressed_message(Defs::MTI mti)
Handles incoming addressed message (non-datagram).
void reject_datagram()
Kills the input frame and sends back a datagram rejected error message with permanent error.
void handle_send_datagram()
Fills outgoing frame from the pending datagram send buffer.
volatile unsigned g_bootloader_busy
1 if the bootloader is active, 0 if the bootloader is not active.
void set_error_code(uint16_t error_code)
Sets output frame dlc to 4; adds the given error code to bytes 2 and 3.
void handle_init()
Initialization state machine.
bool normalize_write_buffer_offset()
Translates from the logical address space of the OpenLCB memory config protocol memory space to the p...
void handle_global_message(Defs::MTI mti)
Handles incoming global message.
uint32_t load_uint32_be(const uint8_t *ptr)
Loads an unaligned 32-bit value that is network-endian.
bool bootloader_loop()
Called repeatedly in an infinite loop to run the bootloader.
void set_can_frame_addressed(Defs::MTI mti, NodeAlias alias=CanDefs::get_src(GET_CAN_FRAME_ID_EFF(state_.input_frame)))
Sets the outgoing CAN frame to addressed, destination taken from the source field of the incoming mes...
void add_memory_config_error_response(uint16_t error_code)
turns an already prepared memory config response datagram into an error response.
#define PIP_REPLY_VALUE
Protocol support bitmask that the bootloader should export.
void set_can_frame_nodeid()
Adds the node id ad the data payload of the outgoing can frame.
void init_flash_write_buffer()
Clears out the flash write buffer with all 0xFF values.
void handle_stream_complete()
Handles incoming stream data complete message.
#define FLASH_SPACE
Which OpenLCB Memory Config Space number should the bootloader export.
void flush_flash_buffer()
Writes the flash write buffer into flash, and clears it out for continuing the bootloading process.
void handle_input_frame()
Handles an incoming CAN frame.
void handle_memory_config_frame()
Decodes the memory config protocol's incoming data.
bool is_can_frame_nodeid()
Checks whether the incoming frame contains the current (bootloader) node's node_id as the data payloa...
bool bootloader_init()
Called once before starting the bootloader loop.
#define STREAM_ID
local stream ID.
static const uint64_t PIP_REPLY
We manually convert to big-endian to store this value in .rodata.
bool try_send_can_frame(const struct can_frame &frame)
Callback from the bootloader to transmit a single CAN frame.
void get_flash_boundaries(const void **flash_min, const void **flash_max, const struct app_header **app_header)
Callback from the bootloader to retrieve flash boundaries.
void bootloader_reboot(void)
Callback from the bootloader when a reboot should be triggered.
void application_entry(void)
Callback from the bootloader for entering the application.
void erase_flash_page(const void *address)
Callback from the bootloader to erase a flash page.
void get_flash_page_info(const void *address, const void **page_start, uint32_t *page_length_bytes)
Callback from the bootloader to retrieve flash page information.
uint16_t flash_complete(void)
Callback from the bootloader to indicate that the full firmware file has been received.
void bootloader_hw_init(void)
Callback from the bootloader to configure and start the TWAI hardware.
bool read_can_frame(struct can_frame *frame)
Callback from the bootloader to read a single CAN frame.
uint16_t nmranet_alias(void)
Callback from the bootloader to obtain the pre-defined alias to use.
uint64_t nmranet_nodeid(void)
Callback from the bootloader to obtain the node-id to use.
void write_flash(const void *address, const void *data, uint32_t size_bytes)
Callback from the bootloader to write to flash.
void bootloader_led(enum BootloaderLed led, bool value)
Sets status LEDs.
void bootloader_hw_set_to_safe(void)
Initializes the hardware to a safe state of the outputs.
bool request_bootloader(void)
See OSMutexLock in os/OS.hxx.
Lightweight locking class for protecting small critical sections.
InitState
State machine states for initializing the bootloader node.
BootloaderState state_
Global state variables.
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
uint8_t g_write_buffer[WRITE_BUFFER_SIZE]
Write buffer; the OpenLCB protocol engine collects the incoming bytes into this buffer and repeatedly...
Internal state of the bootloader stack.
static bool is_cid_frame(uint32_t can_id)
Tests if the incoming frame is a CID frame.
static CanFrameType get_can_frame_type(uint32_t can_id)
Get the CAN frame type field value of the CAN ID.
@ NMRANET_MSG
normal NMRAnet message
@ CONTROL_MSG
CAN control frame message.
static ControlField get_control_field(uint32_t can_id)
Get the control field of a can control frame.
static void control_init(struct can_frame &frame, NodeAlias src, uint16_t field, int sequence)
Initialize a control frame CAN ID and set DLC to 0.
static FrameType get_frame_type(uint32_t can_id)
Get the frame type field value of the CAN ID.
static CanMTI get_mti(uint32_t can_id)
Get the MTI field value of the CAN ID.
static void set_datagram_fields(uint32_t *can_id, NodeAlias src, NodeAlias dst, CanFrameType can_type)
Set all the CAN ID fields for datagram or stream message.
@ AMD_FRAME
Alias Map Definition frame.
@ AME_FRAME
Alias Mapping Enquiry.
@ RID_FRAME
Reserve ID Frame.
static Priority get_priority(uint32_t can_id)
Get the priority field value of the CAN ID.
CanFrameType
CAN Frame Types.
@ DATAGRAM_MIDDLE_FRAME
middle frame of multi-frame datagram
@ DATAGRAM_FINAL_FRAME
last frame of multi-frame datagram
@ DATAGRAM_FIRST_FRAME
first frame of multi-frame datagram
@ GLOBAL_ADDRESSED
most CAN frame types fall in this category
@ DATAGRAM_ONE_FRAME
a single frame datagram
@ NORMAL_PRIORITY
normal priority CAN message
static NodeAlias get_src(uint32_t can_id)
Get the source field value of the CAN ID.
static void set_fields(uint32_t *can_id, NodeAlias src, Defs::MTI mti, CanFrameType can_type, FrameType type, Priority priority)
Set all the CAN ID fields.
@ REPLY_PENDING
A reply is pending.
@ INVALID_ARGUMENTS
NON_STANDARD Invalid or unparseable arguments.
@ UNIMPLEMENTED
NON_STANDARD The feature or command requested is not implemented by the target node.
@ OUT_OF_ORDER
Out of order error occurred.
@ BUFFER_UNAVAILABLE
Buffer unavailable error occurred.
@ CONFIGURATION
configuration message
MTI
Known Message type indicators.
@ COMMAND_RESET
reset node to its power on state
@ COMMAND_UNFREEZE
unfreeze operation of node
@ COMMAND_FREEZE
freeze operation of node
@ COMMAND_WRITE
command to write data to address space
@ COMMAND_WRITE_STREAM
command to write data using a stream
@ COMMAND_WRITE_REPLY
reply to write data to address space
@ COMMAND_ENTER_BOOTLOADER
reset node in bootloader mode
@ SPACE_FIRMWARE
firmware upgrade space