Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Bootloader.hxx
Go to the documentation of this file.
1
36#ifdef _OPENLCB_BOOTLOADER_HXX_
37#error Bootloader.hxx should be included only once.
38#endif
39
41#define _OPENLCB_BOOTLOADER_HXX_
42
43#include <string.h>
44#include <unistd.h>
45
46#ifdef ESP_PLATFORM
47#include "bootloader_hal.h"
48#else
50#endif
51#include "openlcb/Defs.hxx"
52#include "openlcb/CanDefs.hxx"
56#include "can_frame.h"
57
58namespace openlcb
59{
60
64{
65 PICK_ALIAS = 0,
66 SEND_CID_7,
67 SEND_CID_6,
68 SEND_CID_5,
69 SEND_CID_4,
70 WAIT_RID,
71 SEND_RID,
72 SEND_AMD,
73 SEND_NMRANET_INIT,
74 INITIALIZED,
75};
76
79{
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;
89#endif
90#ifdef BOOTLOADER_DATAGRAM
91 unsigned incoming_datagram_pending : 1;
92 unsigned datagram_write_pending : 1;
93#endif
94 // 1 if the datagram buffer is busy
95 unsigned datagram_output_pending : 1;
96 // 1 if we are waiting for an incoming reply to a sent datagram
97 unsigned datagram_reply_waiting : 1;
98
99 NodeAlias alias;
100 InitState init_state;
101
102 // response datagram
103 NodeAlias datagram_dst;
104 uint8_t datagram_dlc;
105 uint8_t datagram_offset;
106 uint8_t datagram_payload[14];
107
108 // Node that is sending us the stream of data.
109 NodeAlias write_src_alias;
110
111#ifdef BOOTLOADER_STREAM
112 // stream source ID of incoming data.
113 uint8_t stream_src_id;
114 // What's the total length of the negotiated stream buffer.
115 uint16_t stream_buffer_size;
116 // How many bytes are left of the strem buffer before a continue needs to
117 // be sent.
118 int stream_buffer_remaining;
119#endif
120
121 // Offset of the beginning of the write buffer.
122 uintptr_t write_buffer_offset;
123 // Offset inside the write buffer for the next incoming data.
124 unsigned write_buffer_index;
125};
126
130
131//#define WRITE_BUFFER_SIZE 1024
132
133#ifdef BOOTLOADER_STREAM
134#ifndef WRITE_BUFFER_SIZE
138#define WRITE_BUFFER_SIZE 1024
139#endif
140#else
143#define WRITE_BUFFER_SIZE 64
144#endif
147uint8_t g_write_buffer[WRITE_BUFFER_SIZE];
148
150#define FLASH_SPACE (MemoryConfigDefs::SPACE_FIRMWARE)
152#define STREAM_ID 0x5A
153}
154using namespace openlcb;
155
156extern "C" {
157
160extern volatile unsigned g_bootloader_busy;
161volatile unsigned g_bootloader_busy = 1;
162#ifdef __linux__
163Atomic g_bootloader_lock;
164#endif
165
166#ifdef BOOTLOADER_STREAM
168#define PIP_REPLY_VALUE \
169 (Defs::DATAGRAM | Defs::STREAM | Defs::MEMORY_CONFIGURATION | \
170 Defs::FIRMWARE_UPGRADE_ACTIVE)
171#else
173#define PIP_REPLY_VALUE \
174 (Defs::DATAGRAM | Defs::MEMORY_CONFIGURATION | \
175 Defs::FIRMWARE_UPGRADE_ACTIVE)
176#endif
178static const uint64_t PIP_REPLY = //
179 ((PIP_REPLY_VALUE >> 40) & 0xff) | //
180 ((PIP_REPLY_VALUE >> 24) & 0xff00) | //
181 ((PIP_REPLY_VALUE >> 8) & 0xff0000) | //
182 ((PIP_REPLY_VALUE & 0xff0000) << 8) | //
183 ((PIP_REPLY_VALUE & 0xff00) << 24) | //
184 ((PIP_REPLY_VALUE & 0xff) << 40);
185
187void reset_stream_state();
188
191{
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;
197}
198
204{
206 uint32_t id;
209 SET_CAN_FRAME_ID_EFF(state_.output_frame, id);
210}
211
220 GET_CAN_FRAME_ID_EFF(state_.input_frame)))
221{
223 uint32_t id;
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;
230}
231
234{
235 uint64_t node_id = nmranet_nodeid();
236 for (int i = 5; i >= 0; --i)
237 {
238 state_.output_frame.data[i] = node_id & 0xff;
239 node_id >>= 8;
240 }
241 state_.output_frame.can_dlc = 6;
242}
243
251{
252 if (state_.input_frame.can_dlc != 6)
253 return false;
254 uint64_t node_id = nmranet_nodeid();
255 for (int i = 5; i >= 0; --i)
256 {
257 if (state_.input_frame.data[i] != (node_id & 0xff))
258 return false;
259 node_id >>= 8;
260 }
261 return true;
262}
263
266void set_error_code(uint16_t error_code)
267{
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;
271}
272
276{
277 set_can_frame_addressed(Defs::MTI_DATAGRAM_REJECTED);
278 set_error_code(Defs::ERROR_PERMANENT);
279 state_.input_frame_full = 0;
280}
281
287uint32_t load_uint32_be(const uint8_t *ptr)
288{
289 return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
290}
291
298void add_memory_config_error_response(uint16_t error_code)
299{
300 state_.datagram_payload[1] |= 0x08; // Turns success into error reply.
301 state_.datagram_payload[state_.datagram_dlc++] = error_code >> 8;
302 state_.datagram_payload[state_.datagram_dlc++] = error_code & 0xff;
303}
304
307{
308 memset(g_write_buffer, 0xff, WRITE_BUFFER_SIZE);
309}
310
319{
320 const void *flash_min;
321 const void *flash_max;
322 const struct app_header *app_header;
323 get_flash_boundaries(&flash_min, &flash_max, &app_header);
324 if (state_.write_buffer_offset >=
325 ((uintptr_t)flash_max - (uintptr_t)flash_min))
326 {
327 return false;
328 }
329 state_.write_buffer_offset += (uintptr_t)flash_min;
331 return true;
332}
333
337{
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;
342 get_flash_page_info(address, &page_start, &page_length_bytes);
343 if (page_start == address)
344 {
345 // Beginning of a page -- let's do an erase.
346 erase_flash_page(address);
347 }
348 write_flash((const void *)state_.write_buffer_offset, g_write_buffer,
349 state_.write_buffer_index);
350 state_.write_buffer_offset += state_.write_buffer_index;
351 state_.write_buffer_index = 0;
353}
354
357{
358 uint8_t command = state_.input_frame.data[1];
359 switch (command)
360 {
362 {
363 if (state_.input_frame.can_dlc < 3 ||
364 state_.input_frame.data[2] != MemoryConfigDefs::SPACE_FIRMWARE)
365 {
366 // Invalid request.
369 return;
370 }
371 // fall through
372 }
374 {
375 // Poor man's reset. Clears the entire state machine, which will
376 // cause us to run the boot sequence again.
377 memset(&state_, 0, sizeof(state_));
378 return;
379 }
381 {
382 if (state_.input_frame.can_dlc < 3 ||
383 state_.input_frame.data[2] != MemoryConfigDefs::SPACE_FIRMWARE)
384 {
385 // Invalid request.
388 return;
389 }
390 uint16_t r = flash_complete();
391 if (r != 0) {
392 // Invalid request.
395 return;
396 }
397 // fall through
398 }
400 {
401 set_can_frame_addressed(Defs::MTI_DATAGRAM_OK);
402 state_.request_reset = 1;
403 state_.input_frame_full = 0;
404 return;
405 }
406#ifdef BOOTLOADER_DATAGRAM
408 {
409 NodeAlias src =
410 CanDefs::get_src(GET_CAN_FRAME_ID_EFF(state_.input_frame));
411 if (state_.incoming_datagram_pending)
412 {
414 if (src == state_.write_src_alias)
415 {
417 }
418 else
419 {
421 }
422 return;
423 }
424 if (state_.input_frame.can_dlc < 8)
425 {
427 set_error_code(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
428 return;
429 }
430 if (state_.input_frame.data[6] != FLASH_SPACE)
431 {
433 set_error_code(MemoryConfigDefs::ERROR_SPACE_NOT_KNOWN);
434 return;
435 }
436
437 state_.write_buffer_offset =
438 load_uint32_be(state_.input_frame.data + 2);
441 set_error_code(MemoryConfigDefs::ERROR_OUT_OF_BOUNDS);
442 return;
443 }
444
445 state_.incoming_datagram_pending = 1;
446 state_.datagram_write_pending = 1;
447 state_.write_src_alias =
448 CanDefs::get_src(GET_CAN_FRAME_ID_EFF(state_.input_frame));
449
450 g_write_buffer[0] = state_.input_frame.data[7];
451 state_.write_buffer_index = 1;
452
453 if (CanDefs::get_can_frame_type(GET_CAN_FRAME_ID_EFF(
454 state_.input_frame)) == CanDefs::DATAGRAM_ONE_FRAME)
455 {
456 // We also need to finish writing here.
458 set_can_frame_addressed(Defs::MTI_DATAGRAM_OK);
459 }
460
461 state_.input_frame_full = 0;
462 return;
463 }
464#endif
465#ifdef BOOTLOADER_STREAM
467 {
468 if (state_.datagram_output_pending || state_.stream_open)
469 {
470 // No buffer for response datagram or we are busy
473 return;
474 }
475 if (state_.input_frame.can_dlc < 7)
476 {
477 // Invalid request.
480 return;
481 }
482 // Replies OK.
483 set_can_frame_addressed(Defs::MTI_DATAGRAM_OK);
484 state_.input_frame_full = 0;
485
486 // Composes write stream reply datagram.
487 state_.datagram_dlc = state_.input_frame.can_dlc - 1;
488 memcpy(state_.datagram_payload, state_.input_frame.data,
489 state_.input_frame.can_dlc - 1);
490 state_.datagram_payload[1] |= MemoryConfigDefs::COMMAND_WRITE_REPLY;
491 state_.datagram_output_pending = 1;
492 state_.output_frame.data[state_.output_frame.can_dlc++] =
494 state_.datagram_dst =
495 CanDefs::get_src(GET_CAN_FRAME_ID_EFF(state_.input_frame));
496 state_.datagram_offset = 0;
497
498 if (state_.input_frame.data[6] != FLASH_SPACE)
499 {
501 return;
502 }
503 state_.stream_pending = 1;
504 state_.write_src_alias = state_.datagram_dst;
505 state_.stream_src_id = state_.input_frame.data[7];
506 state_.write_buffer_index = 0;
507 state_.write_buffer_offset =
508 load_uint32_be(state_.input_frame.data + 2);
510 {
513 return reset_stream_state();
514 }
515 return;
516 }
517#endif
518 } // switch
521 return;
522}
523
526
529{
530 switch (mti)
531 {
532 case Defs::MTI_PROTOCOL_SUPPORT_INQUIRY:
533 {
534 if (state_.output_frame_full)
535 {
536 // No buffer. Re-try next round.
537 return;
538 }
539 set_can_frame_addressed(Defs::MTI_PROTOCOL_SUPPORT_REPLY);
540 state_.output_frame.can_dlc = 8;
541 memcpy(state_.output_frame.data + 2, &PIP_REPLY, 6);
542 break;
543 }
544 case Defs::MTI_VERIFY_NODE_ID_ADDRESSED:
545 {
546 if (state_.output_frame_full)
547 {
548 // No buffer. Re-try next round.
549 return;
550 }
551 set_can_frame_global(Defs::MTI_VERIFIED_NODE_ID_NUMBER);
553 state_.input_frame_full = 0;
554 return;
555 }
556 case Defs::MTI_DATAGRAM_OK:
557 {
558 if (state_.datagram_reply_waiting)
559 {
560 state_.datagram_reply_waiting = 0;
561 state_.datagram_output_pending = 0;
562 }
563 break;
564 }
565#ifdef BOOTLOADER_STREAM
566 case Defs::MTI_STREAM_INITIATE_REQUEST:
567 {
568 if (state_.output_frame_full)
569 {
570 // No buffer. Re-try next round.
571 return;
572 }
573 if (state_.input_frame.can_dlc < 7)
574 {
575 set_can_frame_addressed(Defs::MTI_TERMINATE_DUE_TO_ERROR);
577 break;
578 }
579 set_can_frame_addressed(Defs::MTI_STREAM_INITIATE_REPLY);
580 state_.output_frame.can_dlc = 8;
581 state_.output_frame.data[6] = state_.input_frame.data[6];
582 state_.output_frame.data[7] = STREAM_ID;
583 if (!state_.stream_pending ||
584 state_.input_frame.data[6] != state_.stream_src_id)
585 {
586 // Request out of the blue. Reject.
587 state_.output_frame.data[2] = 0;
588 state_.output_frame.data[3] = 0;
589 state_.output_frame.data[4] =
590 0b01000010; // permanent error, "should not happen"
591 // invalid stream request
592 state_.output_frame.data[5] = 0b00100000;
593 }
594 else
595 {
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)
600 {
601 final_size >>= 1;
602 }
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; // accept, no type id.
608 state_.output_frame.data[5] = 0x00;
609
610 state_.stream_pending = 0;
611 state_.stream_open = 1;
612 }
613 break;
614 }
615 case Defs::MTI_STREAM_COMPLETE:
616 {
617 return handle_stream_complete();
618 }
619#endif
620 default:
621 {
622 // Send reject.
623 if (state_.output_frame_full)
624 {
625 // No buffer. Re-try next round.
626 return;
627 }
628 set_can_frame_addressed(Defs::MTI_OPTIONAL_INTERACTION_REJECTED);
630 break;
631 }
632 }
633 state_.input_frame_full = 0;
634 return;
635}
636
639{
640 switch (mti)
641 {
642 case Defs::MTI_VERIFY_NODE_ID_GLOBAL:
643 {
644 if (state_.output_frame_full)
645 {
646 // No buffer. Re-try next round.
647 return;
648 }
649 set_can_frame_global(Defs::MTI_VERIFIED_NODE_ID_NUMBER);
651 state_.input_frame_full = 0;
652 return;
653 }
654 default:
655 break;
656 }
657 // Drop to the floor.
658 state_.input_frame_full = 0;
660 return;
661}
662
663#ifdef BOOTLOADER_STREAM
666{
667 state_.stream_open = 0;
668 state_.stream_pending = 0;
669 state_.stream_proceed_pending = 0;
670 state_.stream_src_id = 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;
676}
677
678void handle_stream_data()
679{
680 if (!state_.stream_open || state_.input_frame.data[0] != STREAM_ID)
681 {
682 // no or wrong stream -- reject.
683 if (state_.output_frame_full)
684 {
685 return; // re-try.
686 }
687 set_can_frame_addressed(Defs::MTI_TERMINATE_DUE_TO_ERROR);
688 set_error_code(Defs::ERROR_TEMPORARY);
689 state_.input_frame_full = 0;
690 return;
691 }
692 int len = state_.input_frame.can_dlc - 1;
693 if (WRITE_BUFFER_SIZE < state_.write_buffer_index + len)
694 {
695 if (state_.output_frame_full)
696 {
697 return; // re-try.
698 }
699 set_can_frame_addressed(Defs::MTI_TERMINATE_DUE_TO_ERROR);
700 set_error_code(Defs::ERROR_TEMPORARY);
701 state_.input_frame_full = 0;
702 return;
703 }
704 state_.input_frame_full = 0;
705 memcpy(&g_write_buffer[state_.write_buffer_index],
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)
710 {
711 state_.stream_proceed_pending = 1;
712 state_.stream_buffer_remaining += state_.stream_buffer_size;
713 }
714 if (state_.write_buffer_index >= WRITE_BUFFER_SIZE)
715 {
717 }
718}
719
721{
722 if (!state_.stream_open ||
723 state_.input_frame.data[2] != state_.stream_src_id ||
724 state_.input_frame.data[3] != STREAM_ID ||
725 CanDefs::get_src(GET_CAN_FRAME_ID_EFF(state_.input_frame)) !=
726 state_.write_src_alias)
727 {
728 // no or wrong stream -- reject.
729 if (state_.output_frame_full)
730 {
731 return; // re-try.
732 }
733 set_can_frame_addressed(Defs::MTI_TERMINATE_DUE_TO_ERROR);
734 set_error_code(Defs::ERROR_TEMPORARY);
735 state_.input_frame_full = 0;
736 // We might be leaking the stream here. If the src id is actually
737 // correct, and we send this termination command, we might never get
738 // the stream close command ever again. The caller will have to reset
739 // the node to be able to start a new stream. On the other hand if the
740 // message comes from an unexpected source, then this is a very valid
741 // thing to do and we might be getting more data from the original
742 // source.
743 return;
744 }
745 if (state_.write_buffer_index)
746 {
748 }
749 // Input frame is processed.
750 state_.input_frame_full = 0;
752}
753#endif
754
757{
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))
761 {
762 state_.input_frame_full = 0;
763 return;
764 }
765 uint32_t can_id = GET_CAN_FRAME_ID_EFF(state_.input_frame);
766 int dlc = state_.input_frame.can_dlc;
768 {
769 // Non-OpenLCB frame. Ignore.
770 state_.input_frame_full = 0;
771 return;
772 }
773 else if ((can_id & 0xfff) == state_.alias)
774 {
775 // Alias conflict.
776 if (CanDefs::is_cid_frame(can_id))
777 {
778 // Someone is sending a CID for our alias. Reply with an RID.
779 if (state_.output_frame_full)
780 {
781 return; // re-try.
782 }
785 state_.output_frame, state_.alias, CanDefs::RID_FRAME, 0);
786 }
787 else
788 {
789 // The hard way. Reset to init state to pick a new
790 // alias.
791 state_.init_state = (InitState)0;
792 }
793 state_.input_frame_full = 0;
794 return;
795 }
797 {
799 (dlc == 0 || is_can_frame_nodeid()))
800 {
801 if (state_.output_frame_full)
802 {
803 return; // re-try.
804 }
807 state_.output_frame, state_.alias, CanDefs::AMD_FRAME, 0);
809 }
810 // All other control messages are ignored.
811 state_.input_frame_full = 0;
812 return;
813 }
814 else if ((can_id >> 12) == (0x1A000 | state_.alias) ||
815 (can_id >> 12) == (0x1B000 | state_.alias))
816 {
817 // Datagram start frame.
818
819 // Datagrams always need an answer. If we cannot render the answer,
820 // let's not even try to parse the message.
821 if (state_.output_frame_full)
822 {
823 return; // re-try.
824 }
825 if (dlc > 1 &&
826 state_.input_frame.data[0] == DatagramDefs::CONFIGURATION)
827 {
829 }
830 else
831 {
832 return reject_datagram();
833 }
834 }
835#ifdef BOOTLOADER_DATAGRAM
836 else if ((can_id >> 12) == (0x1C000 | state_.alias) ||
837 (can_id >> 12) == (0x1D000 | state_.alias))
838 {
839 if (!state_.incoming_datagram_pending ||
840 CanDefs::get_src(can_id) != state_.write_src_alias)
841 {
842 if (state_.output_frame_full)
843 {
844 return; // re-try.
845 }
848 return;
849 }
850 memcpy(g_write_buffer + state_.write_buffer_index,
851 state_.input_frame.data, state_.input_frame.can_dlc);
852 state_.write_buffer_index += state_.input_frame.can_dlc;
853
854 if (CanDefs::get_can_frame_type(can_id) ==
856 {
858 set_can_frame_addressed(Defs::MTI_DATAGRAM_OK);
859 state_.incoming_datagram_pending = 0;
860 state_.datagram_write_pending = 0;
861 }
862
863 state_.input_frame_full = 0;
864 return;
865 }
866#endif
867#ifdef BOOTLOADER_STREAM
868 else if ((can_id >> 12) == (0x1F000 | state_.alias) && dlc > 1)
869 {
870 return handle_stream_data();
871 }
872#endif
873 else if ((can_id >> 24) == 0x19)
874 {
875 // global or addressed message
876 Defs::MTI mti = (Defs::MTI)CanDefs::get_mti(can_id);
877 if (Defs::get_mti_address(mti) && state_.input_frame.can_dlc >= 2 &&
878 (state_.input_frame.data[0] & 0xf) == ((state_.alias >> 8) & 0xf) &&
879 state_.input_frame.data[1] == (state_.alias & 0xff))
880 {
881 return handle_addressed_message(mti);
882 }
883 else if (!Defs::get_mti_address(mti))
884 {
885 return handle_global_message(mti);
886 }
887 }
888 // NMRAnet message
889 state_.input_frame_full = 0;
890 return;
891}
892
896{
897 switch (state_.init_state)
898 {
899 case PICK_ALIAS:
900 {
901 auto node = nmranet_nodeid();
902 if (!state_.alias)
903 {
904 // No alias yet. Pick default.
905 state_.alias = nmranet_alias();
906 // If no default, then generate from the node id.
907 if (!state_.alias)
908 {
909 state_.alias = (node & 0xfff) ^ ((node >> 12) & 0xfff) ^
910 ((node >> 24) & 0xfff) ^ ((node >> 36) & 0xfff);
911 }
912 }
913 else
914 {
915 do
916 {
917 state_.alias =
918 (state_.alias + ((uint16_t(node)) & 0xffe) + 1759) &
919 0xfff;
920 } while (!state_.alias);
921 }
922 break;
923 }
924 case SEND_CID_7:
925 {
928 state_.output_frame, state_.alias, nmranet_nodeid() >> 36, 7);
929 break;
930 }
931 case SEND_CID_6:
932 {
934 CanDefs::control_init(state_.output_frame, state_.alias,
935 (nmranet_nodeid() >> 24) & 0xfff, 6);
936 break;
937 }
938 case SEND_CID_5:
939 {
941 CanDefs::control_init(state_.output_frame, state_.alias,
942 (nmranet_nodeid() >> 12) & 0xfff, 5);
943 break;
944 }
945 case SEND_CID_4:
946 {
949 state_.output_frame, state_.alias, nmranet_nodeid() & 0xfff, 4);
950 break;
951 }
952 case WAIT_RID:
953 {
954 break;
955 }
956 case SEND_RID:
957 {
960 state_.output_frame, state_.alias, CanDefs::RID_FRAME, 0);
961 break;
962 }
963 case SEND_AMD:
964 {
967 state_.output_frame, state_.alias, CanDefs::AMD_FRAME, 0);
969 break;
970 }
971 case SEND_NMRANET_INIT:
972 {
973 set_can_frame_global(Defs::MTI_INITIALIZATION_COMPLETE);
975 break;
976 }
977 case INITIALIZED:
978 {
979 // shouldn't get here.
980 return;
981 }
982 }
983 state_.init_state = static_cast<InitState>(state_.init_state + 1);
984}
985
988{
990 uint32_t id;
991 CanDefs::CanFrameType frame_type;
992 if (!state_.datagram_offset)
993 {
994 if (state_.datagram_dlc <= 8)
995 {
996 frame_type = CanDefs::DATAGRAM_ONE_FRAME;
997 }
998 else
999 {
1000 frame_type = CanDefs::DATAGRAM_FIRST_FRAME;
1001 }
1002 }
1003 else if (state_.datagram_dlc - state_.datagram_offset <= 8)
1004 {
1005 frame_type = CanDefs::DATAGRAM_FINAL_FRAME;
1006 }
1007 else
1008 {
1009 frame_type = CanDefs::DATAGRAM_MIDDLE_FRAME;
1010 }
1012 &id, state_.alias, state_.datagram_dst, frame_type);
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;
1019 if (state_.datagram_offset >= state_.datagram_dlc)
1020 {
1021 state_.datagram_reply_waiting = 1;
1022 }
1023}
1024
1029 {
1030 bool request = request_bootloader();
1031 bootloader_led(LED_REQUEST, request);
1032 bool csum_ok = check_application_checksum();
1033 bootloader_led(LED_CSUM_ERROR, !csum_ok);
1034 if (!request && csum_ok)
1035 {
1038 return true;
1039 }
1040 }
1041 memset(&state_, 0, sizeof(state_));
1042 return false;
1043}
1044
1050{
1051 {
1052#ifdef __linux__
1053 AtomicHolder h(&g_bootloader_lock);
1054#endif
1055 if (!state_.input_frame_full && read_can_frame(&state_.input_frame))
1056 {
1057 state_.input_frame_full = 1;
1058 }
1059 unsigned new_busy =
1060 (state_.input_frame_full || state_.output_frame_full ||
1061 state_.init_state != INITIALIZED ||
1062 (state_.datagram_output_pending
1063 /*&& !state_.datagram_reply_waiting*/))
1064 ? 1
1065 : 0;
1066 if (g_bootloader_busy != new_busy)
1067 {
1068 bootloader_led(LED_ACTIVE, new_busy);
1069 g_bootloader_busy = new_busy;
1070 }
1071 }
1072#ifdef BOOTLOADER_LOOP_HOOK
1073 BOOTLOADER_LOOP_HOOK();
1074#endif
1075 if (state_.output_frame_full && try_send_can_frame(state_.output_frame))
1076 {
1077 state_.output_frame_full = 0;
1078 }
1079 if (state_.request_reset && !g_bootloader_busy)
1080 {
1083 return true;
1084 }
1085 if (state_.input_frame_full)
1086 {
1088 }
1089 if (state_.init_state != INITIALIZED && !state_.output_frame_full)
1090 {
1091 handle_init();
1092 }
1093#ifdef BOOTLOADER_STREAM
1094 if (state_.stream_proceed_pending && !state_.output_frame_full)
1095 {
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++] =
1100 state_.stream_src_id;
1101 state_.output_frame.data[state_.output_frame.can_dlc++] = STREAM_ID;
1102 state_.output_frame.data[state_.output_frame.can_dlc++] = 0;
1103 state_.output_frame.data[state_.output_frame.can_dlc++] = 0;
1104 }
1105#endif
1106 if (state_.datagram_output_pending && !state_.datagram_reply_waiting &&
1107 !state_.output_frame_full)
1108 {
1110 }
1111 return false;
1112}
1113
1116{
1119
1120 if (bootloader_init()) return;
1121
1122 while (true)
1123 {
1124 if (bootloader_loop()) return;
1125#ifdef __linux__
1126 usleep(10);
1127#endif
1128 } // while true
1129 try_send_can_frame(state_.output_frame);
1130}
1131
1132} // extern "C"
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.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
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...
Static definitions of the application.
Internal state of the bootloader stack.
static bool is_cid_frame(uint32_t can_id)
Tests if the incoming frame is a CID frame.
Definition CanDefs.hxx:208
static CanFrameType get_can_frame_type(uint32_t can_id)
Get the CAN frame type field value of the CAN ID.
Definition CanDefs.hxx:181
@ NMRANET_MSG
normal NMRAnet message
Definition CanDefs.hxx:112
@ CONTROL_MSG
CAN control frame message.
Definition CanDefs.hxx:111
static ControlField get_control_field(uint32_t can_id)
Get the control field of a can control frame.
Definition CanDefs.hxx:334
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.
Definition CanDefs.hxx:379
static FrameType get_frame_type(uint32_t can_id)
Get the frame type field value of the CAN ID.
Definition CanDefs.hxx:190
static CanMTI get_mti(uint32_t can_id)
Get the MTI field value of the CAN ID.
Definition CanDefs.hxx:163
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.
Definition CanDefs.hxx:305
@ AMD_FRAME
Alias Map Definition frame.
Definition CanDefs.hxx:127
@ AME_FRAME
Alias Mapping Enquiry.
Definition CanDefs.hxx:128
@ RID_FRAME
Reserve ID Frame.
Definition CanDefs.hxx:126
static Priority get_priority(uint32_t can_id)
Get the priority field value of the CAN ID.
Definition CanDefs.hxx:199
CanFrameType
CAN Frame Types.
Definition CanDefs.hxx:99
@ DATAGRAM_MIDDLE_FRAME
middle frame of multi-frame datagram
Definition CanDefs.hxx:103
@ DATAGRAM_FINAL_FRAME
last frame of multi-frame datagram
Definition CanDefs.hxx:104
@ DATAGRAM_FIRST_FRAME
first frame of multi-frame datagram
Definition CanDefs.hxx:102
@ GLOBAL_ADDRESSED
most CAN frame types fall in this category
Definition CanDefs.hxx:100
@ DATAGRAM_ONE_FRAME
a single frame datagram
Definition CanDefs.hxx:101
@ NORMAL_PRIORITY
normal priority CAN message
Definition CanDefs.hxx:121
static NodeAlias get_src(uint32_t can_id)
Get the source field value of the CAN ID.
Definition CanDefs.hxx:154
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.
Definition CanDefs.hxx:290
@ 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