Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MemoryConfig.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_MEMORYCONFIG_HXX_
36#define _OPENLCB_MEMORYCONFIG_HXX_
37
41#include "openmrn_features.h"
44
45class Notifiable;
46
47extern "C" {
48extern void enter_bootloader();
50#if OPENMRN_FEATURE_REBOOT
51extern void reboot();
52#endif
53}
54
55namespace openlcb
56{
57
65{
66public:
67 typedef uint32_t address_t;
68 typedef uint16_t errorcode_t;
69
73 static const errorcode_t ERROR_AGAIN = 0x3FFF;
74
77 virtual bool set_node(Node* node)
78 {
79 return true;
80 }
81
83 virtual bool read_only()
84 {
85 return true;
86 }
88 virtual address_t min_address()
89 {
90 return 0;
91 }
95 virtual address_t max_address() = 0;
96
103 virtual size_t write(address_t destination, const uint8_t *data, size_t len,
104 errorcode_t *error, Notifiable *again)
105 {
106 DIE("Unimplemented");
107 }
114 virtual size_t read(address_t source, uint8_t *dst, size_t len,
115 errorcode_t *error, Notifiable *again) = 0;
116
119 virtual errorcode_t freeze() {
120 return Defs::ERROR_INVALID_ARGS;
121 }
122
125 virtual errorcode_t unfreeze() {
126 return Defs::ERROR_INVALID_ARGS;
127 }
128};
129
135{
136public:
140 ReadOnlyMemoryBlock(const void *data)
141 : data_(reinterpret_cast<const uint8_t *>(data))
142 , len_(strlen((const char*)data_))
143 {
144 }
145
149 ReadOnlyMemoryBlock(const void *data, address_t len)
150 : data_(reinterpret_cast<const uint8_t*>(data))
151 , len_(len)
152 {
153 }
154
156 {
157 return len_ - 1;
158 }
159
160 size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error,
161 Notifiable *again) OVERRIDE
162 {
163 if (source >= len_) {
164 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
165 return 0;
166 }
167 size_t count = len;
168 if (source + count > len_)
169 {
170 count = len_ - source;
171 }
172 memcpy(dst, data_ + source, count);
173 return count;
174 }
175
176private:
177 const uint8_t *data_; //< Data bytes to serve.
178 const address_t len_; //< Length of block to serve.
179};
180
185{
186public:
190 ReadWriteMemoryBlock(void *data, address_t len)
191 : data_(reinterpret_cast<uint8_t*>(data))
192 , len_(len)
193 {
194 }
195
198 {
199 return false;
200 }
201
203 {
204 return len_ - 1;
205 }
206
207 size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error,
208 Notifiable *again) OVERRIDE
209 {
210 if (source >= len_) {
211 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
212 return 0;
213 }
214 size_t count = len;
215 if (source + count > len_)
216 {
217 count = len_ - source;
218 }
219 memcpy(dst, data_ + source, count);
220 return count;
221 }
222
223 size_t write(address_t destination, const uint8_t *data, size_t len,
224 errorcode_t *error, Notifiable *again) OVERRIDE {
225 if (destination >= len_) {
226 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
227 return 0;
228 }
229 if (destination + len > len_) {
230 len = len_ - destination;
231 }
232 memcpy(data_ + destination, data, len);
233 return len;
234 }
235
236private:
237 uint8_t *data_; //< Data bytes to serve.
238 const address_t len_; //< Length of block to serve.
239};
240
245{
246public:
247 static const address_t AUTO_LEN = (address_t) - 1;
248 static const address_t UNLIMITED_LEN = (address_t) - 2;
249
257 FileMemorySpace(int fd, address_t len = AUTO_LEN);
258
268 FileMemorySpace(const char *name, address_t len = AUTO_LEN);
269
271 {
272 return false;
273 }
274
276 {
278 return fileSize_;
279 }
280
281 size_t write(address_t destination, const uint8_t *data, size_t len,
282 errorcode_t *error, Notifiable *again) OVERRIDE;
283
284 size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error,
285 Notifiable *again) OVERRIDE;
286
287private:
289 void ensure_file_open();
290
291 address_t fileSize_;
292 const char *name_;
293 int fd_;
294};
295
300public:
308 ROFileMemorySpace(int fd, address_t len = AUTO_LEN)
309 : FileMemorySpace(fd, len) {}
310
320 ROFileMemorySpace(const char *name, address_t len = AUTO_LEN)
321 : FileMemorySpace(name, len) {}
322
324 {
325 return true;
326 }
327};
328
330{
331protected:
332 enum
333 {
334 DATAGRAM_ID = DatagramDefs::CONFIGURATION,
335 };
336
339 , responseFlow_(nullptr)
340 {
341 }
342
343 typedef MemorySpace::address_t address_t;
344 typedef MemorySpace::errorcode_t errorcode_t;
346
348 {
349 if (!response_.empty())
350 {
351 return allocate_and_call(
352 STATE(client_allocated), dg_service()->client_allocator());
353 }
354 else
355 {
356 release();
357 return call_immediately(STATE(cleanup));
358 }
359 }
360
361 Action cleanup()
362 {
363 HASSERT(!message());
364 return exit();
365 }
366
367 Action client_allocated()
368 {
369 responseFlow_ =
370 full_allocation_result(dg_service()->client_allocator());
371 return allocate_and_call(
372 dg_service()->iface()->dispatcher(), STATE(send_response_datagram));
373 }
374
376 {
377 auto *b = get_allocation_result(dg_service()->iface()->dispatcher());
378 b->set_done(b_.reset(this));
379 b->data()->reset(Defs::MTI_DATAGRAM, message()->data()->dst->node_id(),
380 message()->data()->src, EMPTY_PAYLOAD);
381 b->data()->payload.swap(response_);
382 release();
383 responseFlow_->write_datagram(b);
384 return wait_and_call(STATE(response_flow_complete));
385 }
386
387 Action response_flow_complete()
388 {
389 if (!(responseFlow_->result() & DatagramClient::OPERATION_SUCCESS))
390 {
391 LOG(WARNING,
392 "MemoryConfig: Failed to send response datagram. error code %x",
393 (unsigned)responseFlow_->result());
394 }
395 dg_service()->client_allocator()->typed_insert(responseFlow_);
396 return call_immediately(STATE(cleanup));
397 }
398
401 {
402 return !(in_bytes()[1] & ~MemoryConfigDefs::COMMAND_MASK);
403 }
404
409 {
410 const uint8_t *bytes = in_bytes();
411 int len = message()->data()->payload.size();
412 uint8_t cmd = bytes[1];
413 // Handles special memory spaces FD, FE, FF.
414 if (!has_custom_space())
415 {
416 return MemoryConfigDefs::COMMAND_MASK +
417 (cmd & ~MemoryConfigDefs::COMMAND_MASK);
418 }
419 if (len <= 6)
420 {
421 LOG(WARNING,
422 "MemoryConfig: Incoming datagram asked for custom "
423 "space but datagram not long enough. command=0x%02x, "
424 "length=%d. Source {0x%012" PRIx64 ", %03x}",
425 cmd, len, message()->data()->src.id,
426 message()->data()->src.alias);
427 return -1;
428 }
429 return bytes[6];
430 }
431
435 {
436 const uint8_t *bytes = in_bytes();
437 int len = message()->data()->payload.size();
438 int ofs;
439 uint8_t cmd = bytes[1];
440 // Handles special memory spaces FD, FE, FF.
441 if (!has_custom_space())
442 {
443 ofs = 6;
444 }
445 else
446 {
447 ofs = 7;
448 }
449 if (len <= ofs)
450 {
451 LOG(WARNING,
452 "MemoryConfig::read_len: Incoming datagram not long "
453 "enough. command=0x%02x, length=%d. Source "
454 "{0x%012" PRIx64 ", %03x}",
455 cmd, len, message()->data()->src.id,
456 message()->data()->src.alias);
457 return -1;
458 }
459 return bytes[ofs] & 0x7F; // highest bit is reserved.
460 }
461
462 int get_write_length()
463 {
464 int len = message()->data()->payload.size();
465 return len - (has_custom_space() ? 7 : 6);
466 }
467
470 address_t get_address()
471 {
472 const uint8_t *bytes = in_bytes();
473 address_t a = bytes[2];
474 a <<= 8;
475 a |= bytes[3];
476 a <<= 8;
477 a |= bytes[4];
478 a <<= 8;
479 a |= bytes[5];
480 return a;
481 }
482
488 {
489 uint8_t *resp_bytes = out_bytes();
490 const uint8_t *bytes = in_bytes();
491 memcpy(resp_bytes + 2, bytes + 2, 4);
492 if (has_custom_space())
493 {
494 resp_bytes[6] = bytes[6];
495 }
496 else
497 {
498 resp_bytes[1] |= (bytes[1] & ~MemoryConfigDefs::COMMAND_MASK);
499 }
500 }
501
503 void set_address(address_t address)
504 {
505 uint8_t *bytes = out_bytes();
506 bytes[5] = address & 0xff;
507 address >>= 8;
508 bytes[4] = address & 0xff;
509 address >>= 8;
510 bytes[3] = address & 0xff;
511 address >>= 8;
512 bytes[2] = address & 0xff;
513 }
514
516 uint8_t *out_bytes()
517 {
518 return reinterpret_cast<uint8_t *>(&response_[0]);
519 }
520
522 const uint8_t *in_bytes()
523 {
524 return reinterpret_cast<const uint8_t *>(
525 message()->data()->payload.data());
526 }
527
528 DatagramPayload response_; //< reply payload to send back.
529 DatagramClient *responseFlow_;
531}; // class MemoryConfigHandlerBase
532
540{
541public:
543 MemoryConfigHandler(DatagramService *if_dg, Node *node, int registry_size)
545 , registry_(registry_size)
546 {
547 dg_service()->registry()->insert(node, DATAGRAM_ID, this);
548 }
549
551 {
553 }
554
555 Registry *registry()
556 {
557 return &registry_;
558 }
559
562 void send(DefaultDatagramHandler::message_type *message, unsigned priority = UINT_MAX) override
563 {
564 size_t len = message->data()->payload.size();
565 const uint8_t *bytes = (const uint8_t *)message->data()->payload.data();
566 uint8_t cmd = ((len >= 2) && (client_ != nullptr)) ? bytes[1] : 0;
567 bool is_client_command = false;
568 // To recognize replies for read & write commands, we need to look at a
569 // bit.
572 {
573 is_client_command = true;
574 }
575 // For the rest of the commands we can enumerate which are replies.
576 switch (cmd)
577 {
578 case MemoryConfigDefs::COMMAND_OPTIONS_REPLY:
579 case MemoryConfigDefs::COMMAND_INFORMATION_REPLY:
582 {
583 is_client_command = true;
584 }
585 default:
586 break;
587 }
588 LOG(VERBOSE, "MemoryConfig incoming cmd 0x%02x is_client %d", cmd,
589 is_client_command);
590 if (is_client_command)
591 {
593 return;
594 }
596 }
597
601 HASSERT(client_ == nullptr || client_ == client);
602 client_ = client;
603 }
604
607 HASSERT(client_ == client);
608 client_ = nullptr;
609 }
610
613 {
614 streamHandler_ = stream_handler;
615 }
616
617private:
619 {
620 response_.clear();
621 const uint8_t *bytes = in_bytes();
622 size_t len = message()->data()->payload.size();
623 HASSERT(len >= 1);
624 HASSERT(bytes[0] == DATAGRAM_ID);
625 if (len < 2)
626 {
627 return respond_reject(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
628 }
629
630 uint8_t cmd = bytes[1];
631
632 if ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
634 {
635 return call_immediately(STATE(handle_read));
636 }
637 else if ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
639 {
640 return call_immediately(STATE(handle_write));
641 }
642 else if ((((cmd & MemoryConfigDefs::COMMAND_MASK) ==
644 ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
647 {
649 return exit();
650 }
651 switch (cmd)
652 {
654 {
655 // Unknown/unsupported command, reject datagram.
656 return respond_reject(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
657
658 // if (
659 break;
660 }
662 {
663 if (len < 3)
664 {
665 return respond_reject(
666 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
667 }
668 uint8_t space = bytes[2];
670 {
671 // Custom spaces cannot do free yet.
672 return respond_reject(Defs::ERROR_INVALID_ARGS);
673 }
674 }
675 // Fall through
677 {
679 return respond_reject(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
680 }
682 {
684 return respond_ok(0);
685 }
687 {
688#if OPENMRN_FEATURE_REBOOT
689 reboot();
690#endif
691 return respond_reject(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
692 }
694 {
695 NodeID id = message()->data()->dst->node_id();
696 if (len < 8 || (data_to_node_id(&bytes[2]) != id))
697 {
698 return respond_reject(Defs::ERROR_INVALID_ARGS);
699 }
700 uint16_t ret = handle_factory_reset(id);
701 if (!ret)
702 {
703 return respond_ok(0);
704 }
705 else
706 {
707 return respond_reject(ret);
708 }
709 }
710 case MemoryConfigDefs::COMMAND_OPTIONS:
711 {
712 return call_immediately(STATE(handle_options));
713 }
714 case MemoryConfigDefs::COMMAND_INFORMATION:
715 {
716 return call_immediately(STATE(handle_get_space_info));
717 }
726 case MemoryConfigDefs::COMMAND_OPTIONS_REPLY:
727 case MemoryConfigDefs::COMMAND_INFORMATION_REPLY:
730 {
731 if (client_)
732 {
734 return exit();
735 }
736 LOG(VERBOSE, "memcfg handler reply: no client registered");
737 // fall through to unsupported
738 } // fall through
739 default:
740 // Unknown/unsupported command, reject datagram.
741 return respond_reject(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
742 }
743 }
744
747 class RebootTimer : public ::Timer
748 {
749 public:
752 { }
753
754 long long timeout() override
755 {
756#if OPENMRN_FEATURE_REBOOT
757 reboot();
758#endif
759 return DELETE;
760 }
761 };
762
767 uint16_t handle_factory_reset(NodeID target);
768
775 uint16_t __attribute__((noinline)) app_handle_factory_reset(NodeID target);
776
777
778 Action handle_options()
779 {
780 response_.reserve(7);
781 response_.push_back(DATAGRAM_ID);
782 response_.push_back(MemoryConfigDefs::COMMAND_OPTIONS_REPLY);
783 uint16_t available_commands =
785 if (streamHandler_)
786 {
787 available_commands |= MemoryConfigDefs::AVAIL_SR;
788 }
789 // Figure out about ACDI spaces
790 MemorySpace* memspace = registry_.lookup(message()->data()->dst, 0xFC);
791 if (memspace) {
792 available_commands |= MemoryConfigDefs::AVAIL_R0xFC;
793 }
794 memspace = registry_.lookup(message()->data()->dst, 0xFB);
795 if (memspace) {
796 available_commands |= MemoryConfigDefs::AVAIL_R0xFB;
797 if (!memspace->read_only()) {
798 available_commands |= MemoryConfigDefs::AVAIL_W0xFB;
799 }
800 }
801 response_.push_back(available_commands >> 8);
802 response_.push_back(available_commands & 0xff);
803 // Write lengths
804 response_.push_back(static_cast<char> (
807
808 uint8_t min_space = 0xFF;
809 uint8_t max_space = 0;
810 // Walks the spaces.
811 for (auto it = registry_.begin(); it != registry_.end(); ++it) {
812 auto h = *it;
813 uint8_t space = h.first.second;
814 Node* node = h.first.first;
815 //MemorySpace* space_impl = h.second;
816 if (node && node != message()->data()->dst) {
817 continue;
818 }
819 if (space >= 0xFD) continue;
820 if (space > max_space) max_space = space;
821 if (space < min_space) min_space = space;
822 }
823 if (max_space < min_space) {
824 max_space = 0xff;
825 min_space = 0xfd;
826 }
827 response_.push_back(max_space);
828 response_.push_back(min_space);
829 return respond_ok(DatagramClient::REPLY_PENDING);
830 }
831
832 Action handle_get_space_info()
833 {
834 if (message()->data()->payload.size() < 3) {
835 // Incoming message too short.
836 return respond_reject(DatagramClient::PERMANENT_ERROR);
837 }
838 uint8_t space_number = in_bytes()[2];
839 MemorySpace* space = registry_.lookup(message()->data()->dst, space_number);
840 response_.reserve(8);
841 response_.push_back(DATAGRAM_ID);
842 response_.push_back(MemoryConfigDefs::COMMAND_INFORMATION_REPLY);
843 response_.push_back(space_number);
844 if (!space) {
846 } else {
847 response_[1]++;
848 }
849 MemorySpace::address_t address = space->max_address();
850 response_.push_back((address >> 24) & 0xff);
851 response_.push_back((address >> 16) & 0xff);
852 response_.push_back((address >> 8) & 0xff);
853 response_.push_back(address & 0xff);
854 uint8_t flags = 0;
855 address = space->min_address();
856 if (address) {
858 }
859 if (space->read_only()) {
861 }
862 response_.push_back(flags);
863 if (address) {
864 response_.push_back((address >> 24) & 0xff);
865 response_.push_back((address >> 16) & 0xff);
866 response_.push_back((address >> 8) & 0xff);
867 response_.push_back(address & 0xff);
868 }
870 }
871
872 Action handle_read()
873 {
874 size_t len = message()->data()->payload.size();
875 if (len <= 6)
876 {
877 return respond_reject(Defs::ERROR_INVALID_ARGS);
878 }
879 MemorySpace *space = get_space();
880 if (!space)
881 {
882 return respond_reject(MemoryConfigDefs::ERROR_SPACE_NOT_KNOWN);
883 }
884 int read_len = get_read_length();
885 if (read_len < 0)
886 {
887 return respond_reject(DatagramClient::PERMANENT_ERROR);
888 }
889 size_t response_data_offset = 6;
890 if (has_custom_space())
891 {
892 ++response_data_offset;
893 }
894 size_t response_len = response_data_offset + read_len;
895 currentOffset_ = 0;
896 char c = 0;
897 response_.assign(response_len, c);
898 return call_immediately(STATE(try_read));
899 }
900
901 Action try_read() {
902 MemorySpace *space = get_space();
903 int read_len = get_read_length();
904 address_t address = get_address();
905 size_t response_data_offset = 6;
906 if (has_custom_space())
907 {
908 ++response_data_offset;
909 }
910 address += currentOffset_;
911 response_data_offset += currentOffset_;
912 read_len -= currentOffset_;
913 errorcode_t error = 0;
914 uint8_t *response_bytes = out_bytes();
915 if (read_len > 0)
916 {
917 int byte_read = space->read(address,
918 response_bytes + response_data_offset, read_len, &error, this);
919 currentOffset_ += byte_read;
920 read_len -= byte_read;
921 if (error == MemorySpace::ERROR_AGAIN)
922 {
923 return wait();
924 }
925 else if (error == 0 && read_len)
926 {
927 return again();
928 }
929 }
930 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS && currentOffset_)
931 {
932 // We can return a partial response.
933 error = 0;
934 }
935 response_bytes[0] = DATAGRAM_ID;
936 response_bytes[1] = error ? MemoryConfigDefs::COMMAND_READ_FAILED
939 response_data_offset = 6;
940 if (has_custom_space())
941 {
942 ++response_data_offset;
943 }
944 if (error)
945 {
946 response_.resize(response_data_offset + 2);
947 out_bytes()[response_data_offset] = error >> 8;
948 out_bytes()[response_data_offset + 1] = error & 0xff;
949 }
950 else
951 {
952 response_.resize(response_data_offset + currentOffset_);
953 }
954 return respond_ok(DatagramClient::REPLY_PENDING);
955 }
956
957 Action handle_write()
958 {
959 size_t len = message()->data()->payload.size();
960 if (len <= 6)
961 {
962 return respond_reject(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
963 }
964 MemorySpace *space = get_space();
965 if (!space)
966 {
967 return respond_reject(MemoryConfigDefs::ERROR_SPACE_NOT_KNOWN);
968 }
969 if (space->read_only())
970 {
971 return respond_reject(MemoryConfigDefs::ERROR_WRITE_TO_RO);
972 }
973 int write_len = get_write_length();
974 if (write_len <= 0)
975 {
976 return respond_reject(Defs::ERROR_INVALID_ARGS);
977 }
978 currentOffset_ = 0;
979 inline_respond_ok(DatagramClient::REPLY_PENDING);
981 }
982
989 {
990 MemorySpace *space = get_space();
991 int write_len = get_write_length();
992 address_t address = get_address();
993 size_t data_offset = 6;
994 if (has_custom_space())
995 {
996 ++data_offset;
997 }
998 address += currentOffset_;
999 data_offset += currentOffset_;
1000 write_len -= currentOffset_;
1001 errorcode_t error = 0;
1002 if (write_len > 0)
1003 {
1004 size_t written = space->write(address, in_bytes() + data_offset,
1005 write_len, &error, this);
1006 currentOffset_ += written;
1007 write_len -= written;
1008 if (error == MemorySpace::ERROR_AGAIN)
1009 {
1010 return wait();
1011 }
1012 else if (error == 0 && write_len)
1013 {
1014 return again();
1015 }
1016 }
1017 char c = 0;
1018 int response_len = 6;
1019 if (has_custom_space())
1020 {
1021 response_len++;
1022 }
1023 if (error == 0)
1024 {
1025 response_.assign(response_len, c);
1027 }
1028 else
1029 {
1030 response_.assign(response_len + 2, c);
1032 out_bytes()[response_len] = error >> 8;
1033 out_bytes()[response_len + 1] = error & 0xff;
1034 }
1035 out_bytes()[0] = DATAGRAM_ID;
1038 }
1039
1043 {
1044 int space_number = get_space_number();
1045 if (space_number < 0)
1046 return nullptr;
1047 MemorySpace *space =
1048 registry_.lookup(message()->data()->dst, space_number);
1049 if (!space)
1050 {
1051 LOG(WARNING, "MemoryConfig: asked node 0x%012" PRIx64 " for unknown space "
1052 "%d. Source {0x%012" PRIx64 ", %03x}",
1053 message()->data()->dst->node_id(), space_number,
1054 message()->data()->src.id, message()->data()->src.alias);
1055 return nullptr;
1056 }
1057 if (!space->set_node(message()->data()->dst))
1058 {
1059 LOG(WARNING, "MemoryConfig: Global space %d rejected node.",
1060 space_number);
1061 return nullptr;
1062 }
1063 return space;
1064 }
1065
1067 //NodeID lockNode_; //< Holds the node ID that locked us.
1068
1069 Registry registry_; //< holds the known memory spaces
1076
1080};
1081
1082} // namespace openlcb
1083
1084#endif // _OPENLCB_MEMORYCONFIG_HXX_
void enter_bootloader()
Implement this function (usually in HwInit.cxx) to enter the bootloader.
void enter_bootloader()
Implement this function (usually in HwInit.cxx) to enter the bootloader.
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
Base class of everything with a virtual destructor.
ActiveTimers * active_timers()
Definition Executor.hxx:144
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
MessageType message_type
Stores the message template type for external reference.
Node information.
Definition Devtab.hxx:549
An object that can schedule itself on an executor to run.
Collection of related state machines that pend on incoming messages.
ExecutorBase * executor()
static T * instance()
Definition Singleton.hxx:77
Return type for a state flow callback.
Action allocate_and_call(FlowInterface< Buffer< T > > *target_flow, Callback c, Pool *pool=nullptr)
Allocates a buffer from a pool and proceed to the next state when allocation is successful.
Buffer< T > * full_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation without resetting the object.
Buffer< T > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
Action wait()
Wait for an asynchronous call.
Action again()
Call the current state again via call_immediately.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
Action exit()
Terminates the processing of this flow.
State flow with a given typed input queue.
A timer that can schedule itself to run on an executor at specified times in the future.
Definition Timer.hxx:134
@ DELETE
delete the timer, use with extreme caution
Definition Timer.hxx:163
A type-safe map that allows registration and lookup or per-node handler of a particular message ID.
void insert(Node *node, uint32_t id, Handler *handler)
Inserts a handler into the map.
Handler * lookup(Node *node, uint32_t id)
Finds a handler for a particular node and particular messageID.
void release() OVERRIDE
Unrefs the current buffer.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
MessageType * transfer_message()
Releases ownership of the current message.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Use this class to send datagrams.
Definition Datagram.hxx:79
uint32_t result()
Returns a bitmask of ResultCodes for the transmission operation.
Definition Datagram.hxx:105
virtual void write_datagram(Buffer< GenMessage > *b, unsigned priority=UINT_MAX)=0
Triggers sending a datagram.
Transport-agnostic dispatcher of datagrams.
Definition Datagram.hxx:161
TypedQAsync< DatagramClient > * client_allocator()
Datagram clients.
Definition Datagram.hxx:186
Base class with utility functionality that implements some common functionality.
Action respond_reject(uint16_t error_code)
Sends a DATAGRAM_REJECT response to the datagram originator node.
Action respond_ok(uint8_t flags)
Sends a DATAGRAM_OK response to the datagram originator node.
void inline_respond_ok(uint8_t flags)
Sends a DATAGRAM_OK response to the datagram originator node.
Memory space implementation that exports the contents of a file as a memory space.
void ensure_file_open()
Makes fd a valid parameter, and ensures fileSize is filled in.
size_t write(address_t destination, const uint8_t *data, size_t len, errorcode_t *error, Notifiable *again) OVERRIDE
size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error, Notifiable *again) OVERRIDE
address_t max_address() OVERRIDE
Action ok_response_sent() OVERRIDE
This state is where the handling will end up after a respond_ok call.
int get_space_number()
Returns the memory space number, or -1 if the incoming datagram is of incorrect format.
void set_address_and_space()
Copies the address and memory space information from the incoming datagram to the outgoing datagram p...
address_t get_address()
Returns the address from the incoming datagram.
void set_address(address_t address)
Sets the address in the response payload buffer.
int get_read_length()
Returns the read/write length from byte 6 or 7 of the incoming datagram, or -1 if the incoming datagr...
Used internally by the factory_reset implementation to reboot the binary asynchronously.
long long timeout() override
Clients of timer should override this function.
Implementation of the Memory Access Configuration Protocol for OpenLCB.
DatagramHandlerFlow * client_
If there is a memory config client, we will forward response traffic to it.
MemorySpace * get_space()
Looks up the memory space for the current datagram.
MemoryConfigHandler(DatagramService *if_dg, Node *node, int registry_size)
node can be nullptr, and then the handler will be registered globally.
void send(DefaultDatagramHandler::message_type *message, unsigned priority=UINT_MAX) override
Overrides the default send method in orderto decide whether the queue the incoming datagram in the se...
void clear_client(DatagramHandlerFlow *client)
Unregisters the previously registered second handler.
uint16_t handle_factory_reset(NodeID target)
Invokes the openlcb config handler to do a factory reset.
DatagramHandlerFlow * streamHandler_
If there is a handler for stream requests, we will forward the respective traffic to it.
uint8_t currentOffset_
Offset withing the current write/read datagram.
uint16_t app_handle_factory_reset(NodeID target)
Weak definition for invoking a factory reset on virtual nodes.
Action entry() OVERRIDE
Entry into the StateFlow activity.
void set_stream_handler(DatagramHandlerFlow *stream_handler)
This will be called by the constructor of the stream handler plugin.
void set_client(DatagramHandlerFlow *client)
Registers a second handler to forward all the client interactions, i.e.
Action try_write()
Internal loop performing the writes.
Abstract base class for the address spaces exported via the Memory Config Protocol.
virtual size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error, Notifiable *again)=0
virtual address_t min_address()
virtual bool set_node(Node *node)
Specifies which node the next operation pertains.
virtual bool read_only()
virtual size_t write(address_t destination, const uint8_t *data, size_t len, errorcode_t *error, Notifiable *again)
static const errorcode_t ERROR_AGAIN
This error code signals that the operation was only partially completed, the again notify was used an...
virtual address_t max_address()=0
virtual errorcode_t unfreeze()
Handles space unfreeze command.
virtual errorcode_t freeze()
Handles space freeze command.
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
Memory space implementation that exports the contents of a file as a memory space.
ROFileMemorySpace(int fd, address_t len=AUTO_LEN)
Creates a memory space based on an fd.
ROFileMemorySpace(const char *name, address_t len=AUTO_LEN)
Creates a memory space based on a file name.
Memory space implementation that exports a some memory-mapped data as a read-only memory space.
ReadOnlyMemoryBlock(const void *data, address_t len)
Initializes a memory block with a given block of memory.
ReadOnlyMemoryBlock(const void *data)
Creates a memory block for a given pointer of data.
size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error, Notifiable *again) OVERRIDE
address_t max_address() OVERRIDE
Memory space implementation that exports a some memory-mapped data as a read-write memory space.
size_t write(address_t destination, const uint8_t *data, size_t len, errorcode_t *error, Notifiable *again) OVERRIDE
address_t max_address() OVERRIDE
size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error, Notifiable *again) OVERRIDE
ReadWriteMemoryBlock(void *data, address_t len)
Initializes a memory block with a given block of memory.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
Definition logging.h:55
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
NodeID data_to_node_id(const void *d)
Converts 6 bytes of big-endian data to a node ID.
Definition If.cxx:59
uint64_t NodeID
48-bit NMRAnet Node ID type
Payload DatagramPayload
Contents of a Datagram message.
Definition Datagram.hxx:51
static Device * first
first device in linked list
Definition Devtab.hxx:535
@ REPLY_PENDING
A reply is pending.
@ CONFIGURATION
configuration message
@ MTI_DATAGRAM
datagram
Static constants and helper functions related to the Memory Configuration Protocol.
@ FLAG_NZLA
space has a nonzero low address
@ AVAIL_UW
unaligned writes supported
@ AVAIL_R0xFB
read from adddress space 0xFB available
@ AVAIL_SR
stream reads supported
@ AVAIL_W0xFB
write from adddress space 0xFB available
@ AVAIL_R0xFC
read from adddress space 0xFC available
@ AVAIL_UR
unaligned reads supported
@ COMMAND_WRITE_STREAM_FAILED
failed to write data using a stream
@ COMMAND_LOCK_REPLY
unlock the configuration space
@ COMMAND_READ_STREAM
command to read data using a stream
@ COMMAND_READ_REPLY
reply to read data from address space
@ COMMAND_RESET
reset node to its power on state
@ COMMAND_READ_STREAM_FAILED
failed to read data using a stream
@ COMMAND_READ
command to read data from address space
@ COMMAND_FREEZE
freeze operation of node
@ COMMAND_LOCK
lock the configuration space
@ COMMAND_UPDATE_COMPLETE
indicate that a sequence of commands is complete
@ COMMAND_WRITE
command to write data to address space
@ COMMAND_WRITE_STREAM
command to write data using a stream
@ COMMAND_UNIQUE_ID_REPLY
node unique id
@ COMMAND_FACTORY_RESET
reset node to factory defaults
@ COMMAND_REPLY_BIT_FOR_RW
This bit is present in REPLY commands for read-write commands.
@ COMMAND_WRITE_STREAM_REPLY
reply to write data using a stream
@ COMMAND_WRITE_REPLY
reply to write data to address space
@ COMMAND_READ_FAILED
failed to read data from address space
@ COMMAND_READ_STREAM_REPLY
reply to read data using a stream
@ COMMAND_ENTER_BOOTLOADER
reset node in bootloader mode
@ COMMAND_WRITE_FAILED
failed to write data to address space
@ COMMAND_MAX_FOR_RW
command <= this value have fixed bit arrangement.
@ LENGTH_2
write length of 2 supported
@ LENGTH_4
write length of 4 supported
@ LENGTH_1
write length of 1 supported
@ LENGTH_ARBITRARY
arbitrary write of any length supported
@ SPACE_FIRMWARE
firmware upgrade space