SatCat5
cfgbus_remote.cc
1 // Copyright 2021-2024 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/cfgbus_remote.h>
7 #include <satcat5/ip_dispatch.h>
8 #include <satcat5/log.h>
9 #include <satcat5/timeref.h>
10 #include <satcat5/udp_dispatch.h>
11 #include <satcat5/utils.h>
12 
13 namespace cfg = satcat5::cfg;
14 namespace util = satcat5::util;
16 using satcat5::net::Type;
17 
18 // Legacy compatibility for very old versions with no sequence counter.
19 #ifndef SATCAT5_CFGBUS_IGNORE_SEQ
20 #define SATCAT5_CFGBUS_IGNORE_SEQ 0
21 #endif
22 
23 // Set verbosity level (0/1/2)
24 static const unsigned DEBUG_VERBOSE = 0;
25 
26 // Define command opcodes
27 static const u8 OPCODE_WRITE0 = 0x2F; // Write no-increment
28 static const u8 OPCODE_WRITE1 = 0x3F; // Write auto-increment
29 static const u8 OPCODE_READ0 = 0x40; // Read no-increment
30 static const u8 OPCODE_READ1 = 0x50; // Read auto-increment
31 
32 // Internal software flags (m_status)
33 static const u32 STATUS_PENDING = (1u << 0);
34 static const u32 STATUS_BUSY = (1u << 1);
35 static const u32 STATUS_POLLING = (1u << 2);
36 
37 // Define Type codes for each supported protocol.
38 static const Type TYPE_ETH_ACK =
39  Type(satcat5::eth::ETYPE_CFGBUS_ACK.value);
40 static const Type TYPE_UDP_ACK =
41  Type(satcat5::udp::PORT_CFGBUS_ACK.value);
42 
43 satcat5::eth::ConfigBus::ConfigBus(satcat5::eth::Dispatch* iface)
44  : satcat5::eth::AddressContainer(iface)
45  , ConfigBusRemote(&m_addr, TYPE_ETH_ACK)
46 {
47  // Nothing else to initialize
48 }
49 
50 void satcat5::eth::ConfigBus::connect(
51  const satcat5::eth::MacAddr& dst)
52 {
53  m_addr.connect(dst, satcat5::eth::ETYPE_CFGBUS_CMD);
54 }
55 
56 satcat5::udp::ConfigBus::ConfigBus(
57  satcat5::udp::Dispatch* udp) // UDP interface
58  : satcat5::udp::AddressContainer(udp)
59  , ConfigBusRemote(&m_addr, TYPE_UDP_ACK)
60 {
61  // Nothing else to initialize
62 }
63 
64 void satcat5::udp::ConfigBus::connect(
65  const satcat5::ip::Addr& dstaddr) // Remote address
66 {
67  m_addr.connect(
68  dstaddr, // New IP address
69  satcat5::udp::PORT_CFGBUS_CMD, // Dst = Cmd port
70  satcat5::udp::PORT_CFGBUS_ACK); // Src = Ack port
71 }
72 
73 ConfigBusRemote::ConfigBusRemote(
74  satcat5::net::Address* dst, // Remote iface + address
75  const satcat5::net::Type& ack) // Ack type parameter
76  : satcat5::cfg::ConfigBus()
77  , satcat5::net::Protocol(ack)
78  , m_dst(dst)
79  , m_timeout_rd(100000) // Default = 100 msec
80  , m_timeout_wr(0) // Default = Non-blocking
81  , m_status(0)
82  , m_sequence(0)
83  , m_response_opcode(0)
84  , m_response_ptr(0)
85  , m_response_len(0)
86  , m_response_status(cfg::IoStatus::OK)
87 {
88  // Register to receive traffic.
89  m_dst->iface()->add(this);
90 }
91 
92 #if SATCAT5_ALLOW_DELETION
93 ConfigBusRemote::~ConfigBusRemote()
94 {
95  m_dst->iface()->remove(this);
96 }
97 #endif
98 
99 cfg::IoStatus ConfigBusRemote::read(unsigned regaddr, u32& rdval)
100 {
101  rdval = 0; // Default response zero if read fails.
102  return send_and_wait(OPCODE_READ1, regaddr, 1, &rdval, m_timeout_rd);
103 }
104 
105 cfg::IoStatus ConfigBusRemote::write(unsigned regaddr, u32 wrval)
106 {
107  return send_and_wait(OPCODE_WRITE1, regaddr, 1, &wrval, m_timeout_wr);
108 }
109 
111  unsigned regaddr, unsigned count, u32* result)
112 {
113  return send_and_wait(OPCODE_READ1, regaddr, count, result, m_timeout_rd);
114 }
115 
117  unsigned regaddr, unsigned count, u32* result)
118 {
119  return send_and_wait(OPCODE_READ0, regaddr, count, result, m_timeout_rd);
120 }
121 
123  unsigned regaddr, unsigned count, const u32* data)
124 {
125  return send_and_wait(OPCODE_WRITE1, regaddr, count, data, m_timeout_wr);
126 }
127 
129  unsigned regaddr, unsigned count, const u32* data)
130 {
131  return send_and_wait(OPCODE_WRITE0, regaddr, count, data, m_timeout_wr);
132 }
133 
135 {
136  if (DEBUG_VERBOSE > 1) log::Log(log::DEBUG, "CfgRemote: frame_rcvd");
137 
138  // Ignore everything if the PENDING flag isn't set.
139  if (!(m_status & STATUS_PENDING)) return;
140 
141  // Sanity check on the header length.
142  if (src.get_read_ready() < 8) {
143  log::Log(log::ERROR, "CfgRemote: Invalid response");
144  return; // Ignore bad packet, keep waiting...
145  }
146 
147  // Read the response header.
148  u8 opcode = src.read_u8();
149  u8 len8 = src.read_u8();
150  u8 seq = src.read_u8();
151  src.read_u8(); // Reserved
152  u32 addr = src.read_u32();
153  unsigned len = 1 + (unsigned)len8;
154 
155  // Discard packets with mismatched header fields.
156  // Sequence check is optional, since it's not present in old versions.
157  // (Frequently WRITE commands don't wait for the response, so there
158  // may be a number of queued responses before we get to a READ.)
159  if (!(opcode == m_response_opcode && len == m_response_len
160  && (SATCAT5_CFGBUS_IGNORE_SEQ || seq == m_sequence))) {
161  if (DEBUG_VERBOSE > 1) {
162  log::Log(log::DEBUG, "CfgRemote: Response ignored")
163  .write(opcode).write(addr).write((u16)len);
164  }
165  return; // Ignore mismatched header.
166  } else if (DEBUG_VERBOSE > 0) {
167  log::Log(log::DEBUG, "CfgRemote: Response received")
168  .write(opcode).write(addr).write((u16)len);
169  }
170 
171  // If applicable, store the read-response.
172  unsigned rdbytes = 4 * m_response_len + 1;
173  if ((m_response_ptr) && (src.get_read_ready() >= rdbytes)) {
174  for (unsigned a = 0 ; a < m_response_len ; ++a)
175  m_response_ptr[a] = src.read_u32();
176  u8 errflag = src.read_u8();
177  if (errflag) {
178  log::Log(log::WARNING, "CfgRemote: Read error");
179  m_response_status = cfg::IoStatus::BUSERROR;
180  }
181  } else if (m_response_ptr) {
182  log::Log msg(log::ERROR, "CfgRemote: Invalid response");
183  m_response_status = cfg::IoStatus::CMDERROR;
184  if (DEBUG_VERBOSE > 1) {
185  msg.write((u16)src.get_read_ready());
186  msg.write(", expected").write((u16)rdbytes);
187  }
188  return; // Ignore bad packet, keep waiting...
189  }
190 
191  // Signal wait_response() that operation is complete.
192  util::clr_mask_u32(m_status, STATUS_PENDING);
193 }
194 
196 {
197  // Do not poll status if we are already busy for any reason.
198  if (m_status) return;
199 
200  // Service any pending tasks before we start.
201  satcat5::poll::service_all();
202 
203  // Set POLLING flag until we have queried every ConfigBus interrupt.
204  util::set_mask_u32(m_status, STATUS_POLLING);
205  irq_poll();
206  util::clr_mask_u32(m_status, STATUS_POLLING);
207 }
208 
210  u8 opcode, unsigned addr, unsigned len, const u32* ptr, unsigned timeout)
211 {
212  // Attempt to send the read command.
213  bool ok = send_command(opcode, addr, len, ptr);
214 
215  // Wait for response?
216  if (!ok) {
217  return cfg::IoStatus::CMDERROR;
218  } else if (timeout) {
219  return wait_response(timeout);
220  } else {
221  return cfg::IoStatus::OK;
222  }
223 }
224 
226  u8 opcode, unsigned addr, unsigned len, const u32* ptr)
227 {
228  if (DEBUG_VERBOSE > 1) {
229  log::Log(log::DEBUG, "CfgRemote: send_command")
230  .write(opcode).write((u32)addr).write((u16)len);
231  }
232 
233  // Sanity check: Never allow overlapping command/response.
234  if (m_status & STATUS_BUSY) {
235  log::Log(log::ERROR, "CfgRemote: Already busy");
236  return false; // Failed to send
237  }
238 
239  // Sanity check: Bulk read/write cannot exceed 256 items.
240  if (len > 256) {
241  log::Log(log::ERROR, "CfgRemote: Bad length");
242  return false; // Failed to send
243  }
244 
245  // Predict command length.
246  unsigned cmd_bytes = 8;
247  if ((opcode == OPCODE_WRITE0) || (opcode == OPCODE_WRITE1))
248  cmd_bytes += 4 * len;
249 
250  // Attempt to open connection. (Also writes Eth/UDP headers.)
251  io::Writeable* dst = m_dst->open_write(cmd_bytes);
252  if (!dst) { // Unable to proceed?
253  log::Log(log::ERROR, "CfgRemote: Connection error");
254  return false;
255  } else if (DEBUG_VERBOSE > 0) {
256  log::Log(log::DEBUG, "CfgRemote: Sending command")
257  .write(opcode).write((u32)addr).write((u16)len);
258  }
259 
260  // Write frame contents (see cfgbus_host_eth.vhd)
261  m_response_opcode = opcode; // Updated expected response...
262  m_response_len = len;
263  dst->write_u8(opcode); // Opcode
264  dst->write_u8(len-1); // Length
265  dst->write_u8(++m_sequence); // Sequence counter
266  dst->write_u8(0); // Reserved
267  dst->write_u32(addr); // Combined address
268  if ((opcode == OPCODE_WRITE0) || (opcode == OPCODE_WRITE1)) {
269  for (unsigned a = 0 ; a < len ; ++a)
270  dst->write_u32(ptr[a]);
271  m_response_ptr = 0; // No read-response
272  } else {
273  m_response_ptr = (u32*)ptr; // Store response at PTR
274  }
275 
276  // Send the packet!
277  return dst->write_finalize();
278 }
279 
281 {
282  m_response_status = cfg::IoStatus::OK;
283 
284  // Set the busy and response-pending flag.
285  util::set_mask_u32(m_status, STATUS_BUSY | STATUS_PENDING);
286 
287  // Keep polling until we get a response or timeout.
288  auto tref = SATCAT5_CLOCK->checkpoint_usec(timeout);
289  while (1) {
290  satcat5::poll::service(); // Yield to other SatCat5 tasks
291  if (!(m_status & STATUS_PENDING)) {
292  break; // Response received
293  } else if (tref.checkpoint_elapsed()) {
294  log::Log(log::ERROR, "CfgRemote: Timeout");
295  m_response_status = cfg::IoStatus::TIMEOUT;
296  break; // Timeout
297  }
298  }
299 
300  // Clear status flags before returning.
301  util::clr_mask_u32(m_status, STATUS_BUSY | STATUS_PENDING);
302  return m_response_status;
303 }
IoStatus
Status codes for ConfigBus read/write operations.
Definition: cfgbus_core.h:99
Generic ConfigBus API.
Definition: cfgbus_core.h:124
void irq_poll()
Poll all registered ConfigBus interrupt handlers.
Definition: cfgbus_core.cc:111
Controller for a remote ConfigBus, connected over network.
Definition: cfgbus_remote.h:39
satcat5::cfg::IoStatus read_array(unsigned regaddr, unsigned count, u32 *result) override
Bulk read from consecutive registers.
satcat5::cfg::IoStatus write_repeat(unsigned regaddr, unsigned count, const u32 *data) override
Repeated write to the same register.
satcat5::net::Address *const m_dst
MAC address for the remote interface.
Definition: cfgbus_remote.h:96
satcat5::cfg::IoStatus wait_response(unsigned timeout)
Busywait until response is received.
void timer_event() override
Child class MUST override this method.
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
bool send_command(u8 opcode, unsigned addr, unsigned len, const u32 *ptr)
Send the specified opcode.
satcat5::cfg::IoStatus read_repeat(unsigned regaddr, unsigned count, u32 *result) override
Repeated read from the same register.
satcat5::cfg::IoStatus send_and_wait(u8 opcode, unsigned addr, unsigned len, const u32 *ptr, unsigned timeout)
Send, then wait if requested.
satcat5::cfg::IoStatus write(unsigned regaddr, u32 wrval) override
Basic read and write operations (ConfigBus API).
satcat5::cfg::IoStatus write_array(unsigned regaddr, unsigned count, const u32 *data) override
Bulk write to consecutive registers.
satcat5::cfg::IoStatus read(unsigned regaddr, u32 &rdval) override
Basic read and write operations (ConfigBus API).
void connect(const satcat5::eth::MacAddr &addr, const satcat5::eth::MacType &type, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Connect to the designated address.
Definition: eth_address.cc:19
Implemention of "net::Dispatch" for Ethernet frames.
Definition: eth_dispatch.h:21
Limited read of next N bytes.
Definition: io_readable.h:255
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
u8 read_u8()
One of many functions for reading integer/floating point values, see details.
Definition: io_readable.cc:44
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
The Log class creates and formats one log message.
Definition: log.h:195
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
Defines a generic API for sending data to a specific destination, such as a MAC address,...
Definition: net_address.h:30
virtual satcat5::net::Dispatch * iface() const =0
Fetch a pointer to the underlying interface.
virtual satcat5::io::Writeable * open_write(unsigned len)=0
Open a new frame to the designated address and type.
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
void connect(const satcat5::udp::Addr &dstaddr, const satcat5::eth::MacAddr &dstmac, const satcat5::udp::Port &dstport, const satcat5::udp::Port &srcport=satcat5::udp::PORT_NONE, const satcat5::eth::VlanTag &vtag=satcat5::eth::VTAG_NONE)
Manual address resolution (user supplies IP + MAC).
Definition: udp_core.cc:30
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
Diagnostic logging to UART and/or Ethernet ports.
An Ethernet MAC address (with serializable interface).
Definition: eth_header.h:29
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
Multipurpose filter for matching fields in network packets.
Definition: net_type.h:38
TimeRef and TimeVal define the API for monotonic timers.
Miscellaneous mathematical utility functions.