IgH EtherCAT Master  1.7.0
fsm_mbox.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * Copyright (C) 2026 Florian Pose, Ingenieurgemeinschaft IgH
4  *
5  * This file is part of the IgH EtherCAT Master.
6  *
7  * The IgH EtherCAT Master is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version 2, as
9  * published by the Free Software Foundation.
10  *
11  * The IgH EtherCAT Master is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with the IgH EtherCAT Master; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  ****************************************************************************/
21 
26 /****************************************************************************/
27 
28 #include "globals.h"
29 #include "master.h"
30 #include "slave.h"
31 #include "fsm_mbox.h"
32 
33 /****************************************************************************/
34 
40 
41 /****************************************************************************/
42 
46  ec_fsm_mbox_t *fsm
47  )
48 {
49  fsm->slave = NULL;
50  fsm->mbox_data = NULL;
51  fsm->mode = EC_FSM_MBOX_WAIT;
52  fsm->jiffies_start = 0;
53  fsm->timeout_ms = 0;
55  fsm->datagram = NULL;
56  fsm->retries = 0;
57  fsm->result = -EINVAL;
58 }
59 
60 /****************************************************************************/
61 
65  ec_fsm_mbox_t *fsm
66  )
67 {
68  ec_fsm_mbox_abort(fsm);
69 }
70 
71 /****************************************************************************/
72 
75 static void ec_fsm_mbox_start(
76  ec_fsm_mbox_t *fsm,
77  ec_slave_t *slave,
78  ec_mbox_data_t *mbox_data,
79  ec_fsm_mbox_mode_t mode,
80  unsigned long jiffies_start,
81  unsigned long timeout_ms
82  )
83 {
84  fsm->slave = slave;
85  fsm->mbox_data = mbox_data;
86  fsm->mode = mode;
87  fsm->jiffies_start = jiffies_start;
88  fsm->timeout_ms = timeout_ms;
90  fsm->datagram = NULL;
91  fsm->retries = 0;
92  fsm->result = 0;
93 }
94 
95 /****************************************************************************/
96 
103  ec_fsm_mbox_t *fsm,
104  ec_slave_t *slave,
105  ec_mbox_data_t *mbox_data,
106  unsigned long jiffies_start,
109  unsigned long timeout_ms
110  )
111 {
112  ec_fsm_mbox_start(fsm, slave, mbox_data, EC_FSM_MBOX_WAIT,
113  jiffies_start, timeout_ms);
114 }
115 
116 /****************************************************************************/
117 
124  ec_fsm_mbox_t *fsm,
125  ec_slave_t *slave,
126  ec_mbox_data_t *mbox_data
127  )
128 {
129  ec_fsm_mbox_start(fsm, slave, mbox_data, EC_FSM_MBOX_POLL, jiffies, 0);
130 }
131 
132 /****************************************************************************/
133 
141  ec_fsm_mbox_t *fsm,
142  ec_slave_t *slave,
143  ec_mbox_data_t *mbox_data,
144  unsigned long jiffies_start,
145  unsigned long timeout_ms
146  )
147 {
148  ec_fsm_mbox_start(fsm, slave, mbox_data, EC_FSM_MBOX_NOSYNC,
149  jiffies_start, timeout_ms);
150 }
151 
152 /****************************************************************************/
153 
162  ec_fsm_mbox_t *fsm,
163  ec_datagram_t *datagram
164  )
165 {
166  if (fsm->state == ec_fsm_mbox_state_end ||
167  fsm->state == ec_fsm_mbox_state_error) {
168  return 0;
169  }
170 
171  fsm->state(fsm, datagram);
172 
173  if (fsm->state == ec_fsm_mbox_state_end ||
174  fsm->state == ec_fsm_mbox_state_error) {
175  fsm->datagram = NULL;
176  return 0;
177  }
178 
179  if (datagram->state != EC_DATAGRAM_UNUSED) {
180  fsm->datagram = datagram;
181  }
182  return 1;
183 }
184 
185 /****************************************************************************/
186 
196  const ec_fsm_mbox_t *fsm
197  )
198 {
199  return fsm->result;
200 }
201 
202 /****************************************************************************/
203 
209  const ec_fsm_mbox_t *fsm
210  )
211 {
212  return fsm->state == ec_fsm_mbox_state_check ||
214 }
215 
216 /****************************************************************************/
217 
221  ec_fsm_mbox_t *fsm
222  )
223 {
224  if (ec_fsm_mbox_holds_lock(fsm)) {
226  }
227 
228  if (fsm->state != ec_fsm_mbox_state_end &&
229  fsm->state != ec_fsm_mbox_state_error) {
230  fsm->result = -ECANCELED;
232  }
233  fsm->datagram = NULL;
234 }
235 
236 /****************************************************************************/
237 
243  const ec_fsm_mbox_t *fsm
244  )
245 {
246  return smp_load_acquire(&fsm->mbox_data->payload_size) > 0;
247 }
248 
249 /****************************************************************************/
250 
256  const ec_fsm_mbox_t *fsm,
257  unsigned long now
258  )
259 {
260  return (now - fsm->jiffies_start) * 1000 / HZ >= fsm->timeout_ms;
261 }
262 
263 /****************************************************************************/
264 
267 static void ec_fsm_mbox_fail(
268  ec_fsm_mbox_t *fsm,
269  ec_datagram_t *datagram,
270  int result
271  )
272 {
274  datagram->state = EC_DATAGRAM_UNUSED;
275  fsm->result = result;
277 }
278 
279 /****************************************************************************/
280 
287  ec_fsm_mbox_t *fsm,
288  ec_datagram_t *datagram
289  )
290 {
291  ec_slave_t *slave = fsm->slave;
292 
293  if (ec_fsm_mbox_data_available(fsm)) {
294  // data were already received by another read request
295  datagram->state = EC_DATAGRAM_UNUSED;
297  return;
298  }
299 
300  if (ec_read_mbox_locked(slave)) {
301  // another state machine reads the mailbox
302  datagram->state = EC_DATAGRAM_UNUSED;
303 
304  if (fsm->mode == EC_FSM_MBOX_POLL) {
305  fsm->result = -EBUSY;
307  } else if (ec_fsm_mbox_timed_out(fsm, jiffies)) {
308  fsm->result = -ETIMEDOUT;
310  }
311  return;
312  }
313 
314  fsm->retries = EC_FSM_RETRIES;
315 
316  if (fsm->mode == EC_FSM_MBOX_NOSYNC) {
317  ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
319  } else {
320  ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
322  }
323 }
324 
325 /****************************************************************************/
326 
332  ec_fsm_mbox_t *fsm,
333  ec_datagram_t *datagram
334  )
335 {
336  ec_slave_t *slave = fsm->slave;
337  int verbose = fsm->mode != EC_FSM_MBOX_POLL;
338 
339  if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
340  ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
341  return;
342  }
343 
344  if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
345  if (verbose) {
346  EC_SLAVE_ERR(slave, "Failed to receive mailbox check"
347  " datagram: ");
349  }
350  ec_fsm_mbox_fail(fsm, datagram, -EIO);
351  return;
352  }
353 
354  if (fsm->datagram->working_counter != 1) {
355  if (verbose) {
356  EC_SLAVE_ERR(slave, "Reception of mailbox check"
357  " datagram failed: ");
359  }
360  ec_fsm_mbox_fail(fsm, datagram, -EIO);
361  return;
362  }
363 
364  if (ec_slave_mbox_check(fsm->datagram)) {
365  ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
366  fsm->retries = EC_FSM_RETRIES;
368  return;
369  }
370 
371  // mailbox empty
372 
373  if (ec_fsm_mbox_data_available(fsm)) {
374  // data were already received by another read request
376  datagram->state = EC_DATAGRAM_UNUSED;
378  return;
379  }
380 
381  if (fsm->mode == EC_FSM_MBOX_POLL) {
382  ec_fsm_mbox_fail(fsm, datagram, -EAGAIN);
383  return;
384  }
385 
387  ec_fsm_mbox_fail(fsm, datagram, -ETIMEDOUT);
388  return;
389  }
390 
391  ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
392  fsm->retries = EC_FSM_RETRIES;
393 }
394 
395 /****************************************************************************/
396 
404  ec_fsm_mbox_t *fsm,
405  ec_datagram_t *datagram
406  )
407 {
408  ec_slave_t *slave = fsm->slave;
409  int verbose = fsm->mode != EC_FSM_MBOX_POLL;
410 
411  if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
412  ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
413  return;
414  }
415 
416  if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
417  if (verbose) {
418  EC_SLAVE_ERR(slave, "Failed to receive mailbox fetch"
419  " datagram: ");
421  }
422  ec_fsm_mbox_fail(fsm, datagram, -EIO);
423  return;
424  }
425 
426  // a mailbox error response is not dispatched by the receive path
428 
430  datagram->state = EC_DATAGRAM_UNUSED;
431 
432  if (ec_fsm_mbox_data_available(fsm)) {
434  return;
435  }
436 
437  if (fsm->mode == EC_FSM_MBOX_NOSYNC) {
438  // no data in the mailbox, or a message of another protocol
439  fsm->result = -EAGAIN;
441  return;
442  }
443 
444  if (fsm->datagram->working_counter != 1) {
445  if (verbose) {
446  EC_SLAVE_ERR(slave, "Reception of mailbox fetch"
447  " datagram failed: ");
449  }
450  fsm->result = -EIO;
452  return;
453  }
454 
455  if (fsm->mode == EC_FSM_MBOX_POLL) {
456  // message of another protocol
457  fsm->result = -EAGAIN;
459  return;
460  }
461 
462  // message of another protocol: continue waiting
464  fsm->state(fsm, datagram);
465 }
466 
467 /****************************************************************************/
468 
472  ec_fsm_mbox_t *fsm,
473  ec_datagram_t *datagram
474  )
475 {
476 }
477 
478 /****************************************************************************/
479 
483  ec_fsm_mbox_t *fsm,
484  ec_datagram_t *datagram
485  )
486 {
487 }
488 
489 /****************************************************************************/
#define EC_FSM_RETRIES
Number of state machine retries on datagram timeout.
Definition: globals.h:47
void ec_fsm_mbox_state_start(ec_fsm_mbox_t *, ec_datagram_t *)
State: START.
Definition: fsm_mbox.c:286
ec_fsm_mbox_mode_t mode
Receive mode.
Definition: fsm_mbox.h:65
static void ec_fsm_mbox_fail(ec_fsm_mbox_t *fsm, ec_datagram_t *datagram, int result)
Releases the mailbox read lock and terminates with an error.
Definition: fsm_mbox.c:267
ec_datagram_t * datagram
Datagram used in the last step.
Definition: fsm_mbox.h:69
EtherCAT mailbox receive state machine.
Definition: fsm_mbox.h:62
unsigned long jiffies_start
Reference time for the timeout.
Definition: fsm_mbox.h:66
Fetch the mailbox once without checking it.
Definition: fsm_mbox.h:42
int ec_fsm_mbox_exec(ec_fsm_mbox_t *fsm, ec_datagram_t *datagram)
Executes the current state of the state machine.
Definition: fsm_mbox.c:161
EtherCAT slave structure.
void ec_fsm_mbox_receive(ec_fsm_mbox_t *fsm, ec_slave_t *slave, ec_mbox_data_t *mbox_data, unsigned long jiffies_start, unsigned long timeout_ms)
Starts waiting for a mailbox response.
Definition: fsm_mbox.c:102
int ec_slave_mbox_prepare_fetch(const ec_slave_t *slave, ec_datagram_t *datagram)
Prepares a datagram to fetch mailbox data.
Definition: mailbox.c:219
EtherCAT mailbox response data.
Definition: mailbox.h:58
Not used by its state machine in this cycle and not to be queued.
Definition: datagram.h:73
EtherCAT datagram.
Definition: datagram.h:81
ec_fsm_mbox_mode_t
Mode of the mailbox receive state machine.
Definition: fsm_mbox.h:39
static void ec_fsm_mbox_start(ec_fsm_mbox_t *fsm, ec_slave_t *slave, ec_mbox_data_t *mbox_data, ec_fsm_mbox_mode_t mode, unsigned long jiffies_start, unsigned long timeout_ms)
Starts the state machine in the given mode.
Definition: fsm_mbox.c:75
uint16_t working_counter
Working counter.
Definition: datagram.h:96
Wait for a response until the timeout elapsed.
Definition: fsm_mbox.h:40
unsigned int retries
Retries upon datagram timeout.
Definition: fsm_mbox.h:70
void ec_fsm_mbox_fetch_nosync(ec_fsm_mbox_t *fsm, ec_slave_t *slave, ec_mbox_data_t *mbox_data, unsigned long jiffies_start, unsigned long timeout_ms)
Starts a single mailbox fetch without a preceding check.
Definition: fsm_mbox.c:140
Global definitions and macros.
EtherCAT master structure.
void ec_slave_mbox_take_error(ec_mbox_data_t *mbox_data, const ec_datagram_t *datagram)
Takes over a mailbox error response from a mailbox fetch datagram.
Definition: mailbox.c:246
void(* state)(ec_fsm_mbox_t *, ec_datagram_t *)
State function.
Definition: fsm_mbox.h:68
EtherCAT slave.
Definition: slave.h:170
static int ec_fsm_mbox_timed_out(const ec_fsm_mbox_t *fsm, unsigned long now)
Checks, if the timeout elapsed.
Definition: fsm_mbox.c:255
int ec_fsm_mbox_result(const ec_fsm_mbox_t *fsm)
Returns the result of the terminated state machine.
Definition: fsm_mbox.c:195
ec_datagram_state_t state
State.
Definition: datagram.h:97
void ec_fsm_mbox_abort(ec_fsm_mbox_t *fsm)
Aborts the state machine and releases the mailbox read lock, if held.
Definition: fsm_mbox.c:220
ec_mbox_data_t * mbox_data
Response buffer of the protocol.
Definition: fsm_mbox.h:64
#define EC_SLAVE_ERR(slave, fmt, args...)
Convenience macro for printing slave-specific errors to syslog.
Definition: slave.h:70
void ec_datagram_print_wc_error(const ec_datagram_t *datagram)
Evaluates the working counter of a single-cast datagram.
Definition: datagram.c:605
EtherCAT mailbox receive state machine.
void ec_fsm_mbox_state_check(ec_fsm_mbox_t *, ec_datagram_t *)
State: CHECK.
Definition: fsm_mbox.c:331
unsigned long timeout_ms
Response timeout in [ms].
Definition: fsm_mbox.h:67
static int ec_fsm_mbox_data_available(const ec_fsm_mbox_t *fsm)
Checks, if a message for the protocol is available.
Definition: fsm_mbox.c:242
int ec_read_mbox_locked(ec_slave_t *slave)
Return the current mailbox lock status and lock it if not locked.
Definition: slave.c:201
void ec_read_mbox_lock_clear(ec_slave_t *slave)
Clears the mailbox lock.
Definition: slave.c:187
int ec_slave_mbox_prepare_check(const ec_slave_t *slave, ec_datagram_t *datagram)
Prepares a datagram for checking the mailbox state.
Definition: mailbox.c:188
void ec_datagram_print_state(const ec_datagram_t *datagram)
Prints the state of a datagram.
Definition: datagram.c:565
void ec_fsm_mbox_state_end(ec_fsm_mbox_t *, ec_datagram_t *)
State: END.
Definition: fsm_mbox.c:471
Check the mailbox once, do not wait.
Definition: fsm_mbox.h:41
Timed out (dequeued).
Definition: datagram.h:71
void ec_fsm_mbox_init(ec_fsm_mbox_t *fsm)
Constructor.
Definition: fsm_mbox.c:45
void ec_fsm_mbox_state_fetch(ec_fsm_mbox_t *, ec_datagram_t *)
State: FETCH.
Definition: fsm_mbox.c:403
ec_slave_t * slave
Slave the FSM runs on.
Definition: fsm_mbox.h:63
int ec_fsm_mbox_holds_lock(const ec_fsm_mbox_t *fsm)
Checks, if the state machine holds the slave's mailbox read lock.
Definition: fsm_mbox.c:208
Received (dequeued).
Definition: datagram.h:70
void ec_fsm_mbox_poll(ec_fsm_mbox_t *fsm, ec_slave_t *slave, ec_mbox_data_t *mbox_data)
Starts a single mailbox check without waiting.
Definition: fsm_mbox.c:123
void ec_fsm_mbox_state_error(ec_fsm_mbox_t *, ec_datagram_t *)
State: ERROR.
Definition: fsm_mbox.c:482
int result
Zero on success, otherwise a negative error code.
Definition: fsm_mbox.h:71
unsigned long jiffies_received
Jiffies, when the datagram was received.
Definition: datagram.h:105
size_t payload_size
Size of the mailbox response payload data.
Definition: mailbox.h:61
int ec_slave_mbox_check(const ec_datagram_t *datagram)
Processes a mailbox state checking datagram.
Definition: mailbox.c:207
void ec_fsm_mbox_clear(ec_fsm_mbox_t *fsm)
Destructor.
Definition: fsm_mbox.c:64