35#ifndef _OPENLCB_MEMORYCONFIG_HXX_
36#define _OPENLCB_MEMORYCONFIG_HXX_
50#if OPENMRN_FEATURE_REBOOT
67 typedef uint32_t address_t;
68 typedef uint16_t errorcode_t;
103 virtual size_t write(address_t destination,
const uint8_t *data,
size_t len,
106 DIE(
"Unimplemented");
114 virtual size_t read(address_t source, uint8_t *dst,
size_t len,
120 return Defs::ERROR_INVALID_ARGS;
126 return Defs::ERROR_INVALID_ARGS;
141 : data_(reinterpret_cast<const uint8_t *>(data))
142 , len_(strlen((const char*)data_))
150 : data_(reinterpret_cast<const uint8_t*>(data))
160 size_t read(address_t source, uint8_t *dst,
size_t len, errorcode_t *error,
163 if (source >= len_) {
164 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
168 if (source + count > len_)
170 count = len_ - source;
172 memcpy(dst, data_ + source, count);
177 const uint8_t *data_;
178 const address_t len_;
191 : data_(reinterpret_cast<uint8_t*>(data))
207 size_t read(address_t source, uint8_t *dst,
size_t len, errorcode_t *error,
210 if (source >= len_) {
211 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
215 if (source + count > len_)
217 count = len_ - source;
219 memcpy(dst, data_ + source, count);
223 size_t write(address_t destination,
const uint8_t *data,
size_t len,
225 if (destination >= len_) {
226 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
229 if (destination + len > len_) {
230 len = len_ - destination;
232 memcpy(data_ + destination, data, len);
238 const address_t len_;
247 static const address_t AUTO_LEN = (address_t) - 1;
248 static const address_t UNLIMITED_LEN = (address_t) - 2;
281 size_t write(address_t destination,
const uint8_t *data,
size_t len,
284 size_t read(address_t source, uint8_t *dst,
size_t len, errorcode_t *error,
339 , responseFlow_(
nullptr)
343 typedef MemorySpace::address_t address_t;
344 typedef MemorySpace::errorcode_t errorcode_t;
349 if (!response_.empty())
352 STATE(client_allocated), dg_service()->client_allocator());
367 Action client_allocated()
378 b->set_done(b_.
reset(
this));
381 b->data()->payload.swap(response_);
387 Action response_flow_complete()
389 if (!(responseFlow_->
result() & DatagramClient::OPERATION_SUCCESS))
392 "MemoryConfig: Failed to send response datagram. error code %x",
393 (
unsigned)responseFlow_->
result());
411 int len =
message()->data()->payload.size();
412 uint8_t cmd = bytes[1];
416 return MemoryConfigDefs::COMMAND_MASK +
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,
437 int len =
message()->data()->payload.size();
439 uint8_t cmd = bytes[1];
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,
459 return bytes[ofs] & 0x7F;
462 int get_write_length()
464 int len =
message()->data()->payload.size();
473 address_t a = bytes[2];
491 memcpy(resp_bytes + 2, bytes + 2, 4);
494 resp_bytes[6] = bytes[6];
506 bytes[5] = address & 0xff;
508 bytes[4] = address & 0xff;
510 bytes[3] = address & 0xff;
512 bytes[2] = address & 0xff;
518 return reinterpret_cast<uint8_t *
>(&response_[0]);
524 return reinterpret_cast<const uint8_t *
>(
525 message()->data()->payload.data());
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;
573 is_client_command =
true;
578 case MemoryConfigDefs::COMMAND_OPTIONS_REPLY:
579 case MemoryConfigDefs::COMMAND_INFORMATION_REPLY:
583 is_client_command =
true;
588 LOG(
VERBOSE,
"MemoryConfig incoming cmd 0x%02x is_client %d", cmd,
590 if (is_client_command)
622 size_t len =
message()->data()->payload.size();
624 HASSERT(bytes[0] == DATAGRAM_ID);
627 return respond_reject(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
630 uint8_t cmd = bytes[1];
632 if ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
637 else if ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
642 else if ((((cmd & MemoryConfigDefs::COMMAND_MASK) ==
644 ((cmd & MemoryConfigDefs::COMMAND_MASK) ==
666 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
668 uint8_t space = bytes[2];
688#if OPENMRN_FEATURE_REBOOT
710 case MemoryConfigDefs::COMMAND_OPTIONS:
714 case MemoryConfigDefs::COMMAND_INFORMATION:
726 case MemoryConfigDefs::COMMAND_OPTIONS_REPLY:
727 case MemoryConfigDefs::COMMAND_INFORMATION_REPLY:
736 LOG(
VERBOSE,
"memcfg handler reply: no client registered");
756#if OPENMRN_FEATURE_REBOOT
778 Action handle_options()
780 response_.reserve(7);
781 response_.push_back(DATAGRAM_ID);
782 response_.push_back(MemoryConfigDefs::COMMAND_OPTIONS_REPLY);
783 uint16_t available_commands =
797 if (!memspace->read_only()) {
801 response_.push_back(available_commands >> 8);
802 response_.push_back(available_commands & 0xff);
804 response_.push_back(
static_cast<char> (
808 uint8_t min_space = 0xFF;
809 uint8_t max_space = 0;
813 uint8_t space = h.first.second;
816 if (node && node !=
message()->data()->dst) {
819 if (space >= 0xFD)
continue;
820 if (space > max_space) max_space = space;
821 if (space < min_space) min_space = space;
823 if (max_space < min_space) {
827 response_.push_back(max_space);
828 response_.push_back(min_space);
829 return respond_ok(DatagramClient::REPLY_PENDING);
832 Action handle_get_space_info()
838 uint8_t space_number =
in_bytes()[2];
840 response_.reserve(8);
841 response_.push_back(DATAGRAM_ID);
842 response_.push_back(MemoryConfigDefs::COMMAND_INFORMATION_REPLY);
843 response_.push_back(space_number);
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);
855 address = space->min_address();
859 if (space->read_only()) {
862 response_.push_back(flags);
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);
874 size_t len =
message()->data()->payload.size();
889 size_t response_data_offset = 6;
892 ++response_data_offset;
894 size_t response_len = response_data_offset + read_len;
897 response_.assign(response_len, c);
905 size_t response_data_offset = 6;
908 ++response_data_offset;
913 errorcode_t error = 0;
917 int byte_read = space->read(address,
918 response_bytes + response_data_offset, read_len, &error,
this);
920 read_len -= byte_read;
925 else if (error == 0 && read_len)
930 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS &&
currentOffset_)
935 response_bytes[0] = DATAGRAM_ID;
939 response_data_offset = 6;
942 ++response_data_offset;
946 response_.resize(response_data_offset + 2);
947 out_bytes()[response_data_offset] = error >> 8;
948 out_bytes()[response_data_offset + 1] = error & 0xff;
954 return respond_ok(DatagramClient::REPLY_PENDING);
959 size_t len =
message()->data()->payload.size();
962 return respond_reject(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
969 if (space->read_only())
973 int write_len = get_write_length();
991 int write_len = get_write_length();
993 size_t data_offset = 6;
1001 errorcode_t error = 0;
1004 size_t written = space->
write(address,
in_bytes() + data_offset,
1005 write_len, &error,
this);
1007 write_len -= written;
1012 else if (error == 0 && write_len)
1018 int response_len = 6;
1025 response_.assign(response_len, c);
1030 response_.assign(response_len + 2, c);
1033 out_bytes()[response_len + 1] = error & 0xff;
1045 if (space_number < 0)
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,
1059 LOG(
WARNING,
"MemoryConfig: Global space %d rejected node.",
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.
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()
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.
An object that can schedule itself on an executor to run.
Collection of related state machines that pend on incoming messages.
ExecutorBase * executor()
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.
@ DELETE
delete the timer, use with extreme caution
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.
uint32_t result()
Returns a bitmask of ResultCodes for the transmission operation.
virtual void write_datagram(Buffer< GenMessage > *b, unsigned priority=UINT_MAX)=0
Triggers sending a datagram.
Transport-agnostic dispatcher of datagrams.
TypedQAsync< DatagramClient > * client_allocator()
Datagram clients.
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.
const uint8_t * payload()
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.
bool read_only() OVERRIDE
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.
const uint8_t * in_bytes()
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.
Action send_response_datagram()
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 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.
Memory space implementation that exports the contents of a file as a memory space.
bool read_only() OVERRIDE
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
bool read_only() 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.
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
#define DIE(MSG)
Unconditionally terminates the current process with a message.
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
NodeID data_to_node_id(const void *d)
Converts 6 bytes of big-endian data to a node ID.
uint64_t NodeID
48-bit NMRAnet Node ID type
Payload DatagramPayload
Contents of a Datagram message.
static Device * first
first device in linked list
@ REPLY_PENDING
A reply is pending.
@ CONFIGURATION
configuration message
Static constants and helper functions related to the Memory Configuration Protocol.
@ FLAG_RO
space is read only
@ 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