Statistics
| Branch: | Tag: | Revision:

amiro-os / core / src / aos_sssp.c @ 849b383a

History | View | Annotate | Download (47.633 KB)

1
/*
2
AMiRo-OS is an operating system designed for the Autonomous Mini Robot (AMiRo) platform.
3
Copyright (C) 2016..2020  Thomas Schöpping et al.
4

5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9

10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14

15
You should have received a copy of the GNU General Public License
16
along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18

    
19
/**
20
 * @file    aos_sssp.c
21
 * @brief   SSSP related code.
22
 *
23
 * @addtogroup aos_sssp
24
 * @{
25
 */
26

    
27
#include <amiroos.h>
28

    
29
#if (AMIROOS_CFG_SSSP_ENABLE == true) || defined(__DOXYGEN__)
30

    
31
/******************************************************************************/
32
/* LOCAL DEFINITIONS                                                          */
33
/******************************************************************************/
34

    
35
#if ((AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)) || defined(__DOXYGEN__)
36

    
37
/**
38
 * @brief   Weighting factor for smoothing the @p _syncskew value.
39
 */
40
#define SYNCSKEW_SMOOTHFACTOR                   (0.1f / AOS_SYSTEM_TIME_RESOLUTION)
41

    
42
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
43

    
44
/******************************************************************************/
45
/* EXPORTED VARIABLES                                                         */
46
/******************************************************************************/
47

    
48
/******************************************************************************/
49
/* LOCAL TYPES                                                                */
50
/******************************************************************************/
51

    
52
/******************************************************************************/
53
/* LOCAL VARIABLES                                                            */
54
/******************************************************************************/
55

    
56
/**
57
 * @brief   Pointer to the system uptime.
58
 */
59
static aos_timestamp_t* _uptime;
60

    
61
#if (AMIROOS_CFG_SSSP_MASTER == true) || defined(__DOXYGEN__)
62

    
63
/**
64
 * @brief   Timer to drive the S signal for system wide time synchronization during operation phase.
65
 */
66
static virtual_timer_t _synctimer;
67

    
68
/**
69
 * @brief   Last uptime of system wide time synchronization.
70
 */
71
static aos_timestamp_t _synctime;
72

    
73
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
74

    
75
#if ((AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)) || defined(__DOXYGEN__)
76

    
77
/**
78
 * @brief   Offset between local clock and system wide synchronization signal.
79
 */
80
static float _syncskew;
81

    
82
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
83

    
84
/**
85
 * @brief   A timer event based delays.
86
 * @details This timer must not be an AMiRo-OS timer, since delays occurr before system is initialized.
87
 */
88
static virtual_timer_t _delayTimer;
89

    
90
/**
91
 * @brief   Event source for the delay timer.
92
 */
93
static event_source_t _delayEventSource;
94

    
95
/**
96
 * @brief   Event listener for the delay event.
97
 */
98
static event_listener_t _delayEventListener;
99

    
100
/******************************************************************************/
101
/* LOCAL FUNCTIONS                                                            */
102
/******************************************************************************/
103

    
104
#if (AMIROOS_CFG_SSSP_MASTER != true) || defined(__DOXYGEN__)
105

    
106
/**
107
 * @brief   Callback function for the S signal interrupt.
108
 *
109
 * @param[in] args   Pointer to the GPIO line identifier.
110
 */
111
static void _gpioCallbackSSignal(void *args)
112
{
113
  aosDbgCheck((args != NULL) && (*((ioline_t*)args) != PAL_NOLINE) && (PAL_PAD(*((ioline_t*)args)) < sizeof(eventflags_t) * 8));
114

    
115
  apalControlGpioState_t s;
116
  aos_timestamp_t t;
117

    
118
  chSysLockFromISR();
119

    
120
  // if the system is in operation phase
121
  if (aos.sssp.stage == AOS_SSSP_STAGE_OPERATION) {
122
    // read signal S
123
    apalControlGpioGet(moduleSsspSignalS(), &s);
124
    // if S was toggled from on to off
125
    if (s == APAL_GPIO_OFF) {
126
      // get current uptime
127
      aosSysGetUptimeX(&t);
128
      // align the uptime with the synchronization period
129
      t %= AMIROOS_CFG_SSSP_SYSSYNCPERIOD;
130
      if (t < AMIROOS_CFG_SSSP_SYSSYNCPERIOD / 2) {
131
        *_uptime -= t;
132
#if (AMIROOS_CFG_PROFILE == true)
133
        _syncskew = ((1.0f - SYNCSKEW_SMOOTHFACTOR) * _syncskew) + (SYNCSKEW_SMOOTHFACTOR * t);
134
#endif /* (AMIROOS_CFG_PROFILE == true) */
135
      } else {
136
        t = AMIROOS_CFG_SSSP_SYSSYNCPERIOD - t;
137
        *_uptime += t;
138
#if (AMIROOS_CFG_PROFILE == true)
139
        _syncskew = ((1.0f - SYNCSKEW_SMOOTHFACTOR) * _syncskew) - (SYNCSKEW_SMOOTHFACTOR * t);
140
#endif /* (AMIROOS_CFG_PROFILE == true) */
141
      }
142
    }
143
  }
144

    
145
  // broadcast event
146
  chEvtBroadcastFlagsI(&aos.events.gpio, AOS_GPIOEVENT_FLAG(PAL_PAD(*((ioline_t*)args))));
147
  chSysUnlockFromISR();
148

    
149
  return;
150
}
151

    
152
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) */
153

    
154
#if (AMIROOS_CFG_SSSP_MASTER == true) || defined (__DOXYGEN__)
155

    
156
/**
157
 * @brief   Periodic system synchronization callback function.
158
 * @details Toggles the S signal and reconfigures the system synchronization timer.
159
 *
160
 * @param[in] par   Unused parameters.
161
 */
162
static void _syncTimerCallback(void* par)
163
{
164
  (void)par;
165

    
166
  // local variables
167
  apalControlGpioState_t state;
168
  aos_timestamp_t uptime;
169

    
170
  chSysLockFromISR();
171
  // toggle and read signal S
172
  apalGpioToggle(moduleSsspSignalS()->gpio);
173
  apalControlGpioGet(moduleSsspSignalS(), &state);
174
  // if S was toggled from off to on
175
  if (state == APAL_GPIO_ON) {
176
    // reconfigure the timer precisely, because the logically falling edge (next interrupt) snychronizes the system time
177
    _synctime += AMIROOS_CFG_SSSP_SYSSYNCPERIOD;
178
    aosSysGetUptimeX(&uptime);
179
    chVTSetI(&_synctimer, chTimeUS2I((time_usecs_t)(_synctime - uptime)), _syncTimerCallback, NULL);
180
  }
181
  // if S was toggled from on to off
182
  else /* if (state == APAL_GPIO_OFF) */ {
183
    // reconfigure the timer (lazy)
184
    chVTSetI(&_synctimer, chTimeUS2I(AMIROOS_CFG_SSSP_SYSSYNCPERIOD / 2), _syncTimerCallback, NULL);
185
  }
186
  chSysUnlockFromISR();
187

    
188
  return;
189
}
190

    
191
/**
192
 * @brief   Start the timer that toggles S for synchronization of the system.
193
 * @note    Must be called from a locked context.
194
 */
195
static inline void _syncTimerStartS(void)
196
{
197
  chDbgCheckClassS();
198

    
199
  // start synchronization timer
200
  // The first iteration of the timer is set to the next 'center' of a 'slice'.
201
  aos_timestamp_t t;
202
  aosSysGetUptimeX(&t);
203
  t = AMIROOS_CFG_SSSP_SYSSYNCPERIOD - (t % AMIROOS_CFG_SSSP_SYSSYNCPERIOD);
204
  chVTSetI(&_synctimer, chTimeUS2I((time_usecs_t)((t > (AMIROOS_CFG_SSSP_SYSSYNCPERIOD / 2)) ? (t - (AMIROOS_CFG_SSSP_SYSSYNCPERIOD / 2)) : (t + (AMIROOS_CFG_SSSP_SYSSYNCPERIOD / 2)))), _syncTimerCallback, NULL);
205

    
206
  return;
207
}
208

    
209
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
210

    
211
/**
212
 * @brief   General callback function to be used for any local timers.
213
 *
214
 * @param[in] par   A pointer to an @p event_source_t to be fired.
215
 */
216
static void _timerCallback(void* par)
217
{
218
  aosDbgCheck(par != NULL);
219

    
220
  chSysLockFromISR();
221
  chEvtBroadcastI((event_source_t*)par);
222
  chSysUnlockFromISR();
223

    
224
  return;
225
}
226

    
227
/**
228
 * @brief   Waits for the S signal to switch to the desired state.
229
 *
230
 * @param[in]   listener      Pointer to the GPIO event listener to be used.
231
 * @param[in]   signalstate   Desired state of the S signal ti be waited for.
232
 * @param[out]  received      Output variable to store the received event mask to.
233
 *
234
 * @return  Status, indicating whether the expected event was received.
235
 */
236
static inline aos_status_t _waitForS(event_listener_t* listener, apalControlGpioState_t signalstate, eventmask_t* received)
237
{
238
  aosDbgCheck(listener != NULL);
239
  aosDbgCheck(received != NULL);
240

    
241
  // local variables
242
  aos_status_t status = AOS_FAILURE;
243

    
244
  // wait for the next event (do not apply any filters in order not to miss any events)
245
  *received = chEvtWaitOne(ALL_EVENTS);
246
  // if the correct event was triggered
247
  if (*received & listener->events) {
248
    apalControlGpioState_t s;
249
    apalControlGpioGet(moduleSsspSignalS(), &s);
250
    chSysLock();
251
    // only check for the expected event
252
    if ((listener->flags & moduleSsspEventflagS()) && (s == signalstate)) {
253
      // unset the expected flags but keep any other ones
254
      listener->flags &= ~moduleSsspEventflagS();
255
      status = AOS_SUCCESS;
256
    }
257
    // if no further flags are set
258
    if (listener->flags == 0) {
259
      // clear event
260
      *received &= ~(listener->events);
261
    }
262
    chSysUnlock();
263
  }
264

    
265
  return status;
266
}
267

    
268
/**
269
 * @brief   Uses the local delay timer to setup a timed event and waits for it to fire.
270
 *
271
 * @param[in]   dt          Delay time in microseconds to be set to the timer.
272
 * @param[in]   mask        Event mask to be used for the delay timer.
273
 * @param[out]  received    Output variable to store the received event mask to.
274
 *
275
 * @return  Status, indicating whether the expected event (delay timer) was received.
276
 */
277
static inline aos_status_t _delay(uint32_t dt, eventmask_t mask, eventmask_t* received)
278
{
279
  aosDbgCheck(dt != TIME_IMMEDIATE);
280
  aosDbgCheck(mask != 0);
281
  aosDbgCheck(received != NULL);
282

    
283
  // arm the delay timer once
284
  if (!chVTIsArmed(&_delayTimer)) {
285
    chEvtRegisterMask(&_delayEventSource, &_delayEventListener, mask);
286
    chVTSet(&_delayTimer, chTimeUS2I(dt), _timerCallback, &_delayEventSource);
287
  }
288

    
289
  // wait for any event to occur (do not apply any filters in order not to miss any events)
290
  *received = chEvtWaitOne(ALL_EVENTS);
291

    
292
  // if the timer event was received, cleanup
293
  if (*received & _delayEventListener.events) {
294
    chEvtUnregister(&_delayEventSource, &_delayEventListener);
295
    *received &= ~(_delayEventListener.events);
296
    return AOS_SUCCESS;
297
  }
298

    
299
  return AOS_FAILURE;
300
}
301

    
302
#if (AMIROOS_CFG_SSSP_MSI == true) || defined(__DOXYGEN__)
303

    
304
/**
305
 * @brief   Serialize a BCB message.
306
 * @details The individual primitives are serialized in big-endian fashion.
307
 *
308
 * @param[out]  buffer    Buffer to write the serialized message to.
309
 * @param[in]   message   The message to be serialized.
310
 */
311
static aos_status_t _serializeBcbMessage(uint8_t* buffer, aos_ssspbcbmessage_t message)
312
{
313
  aosDbgCheck(buffer != NULL);
314

    
315
  // encode the message depending on its type
316
  switch (message.type) {
317
    case AOS_SSSP_BCBMESSAGE_MSIINIT:
318
    case AOS_SSSP_BCBMESSAGE_MSIABORT:
319
    {
320
      buffer[0] = message.type;
321
      return AOS_SUCCESS;
322
    }
323
    case AOS_SSSP_BCBMESSAGE_MSIID:
324
    {
325
      buffer[0] = message.type;
326
      for (size_t byte = 0; byte < sizeof(aos_ssspmoduleid_t); ++byte) {
327
        buffer[byte+1] = (message.payload.id >> ((sizeof(aos_ssspmoduleid_t) - (byte+1)) * 8)) & 0xFF;
328
      }
329
      return AOS_SUCCESS;
330
    }
331
    default:
332
    {
333
      return AOS_FAILURE;
334
    }
335
  }
336
}
337

    
338
/**
339
 * @brief   Deserialize a BCB message.
340
 * @details The individual primitives must be serialized in big-endian fashion.
341
 *
342
 * @param[out]  message   Message object to be filled.
343
 * @param[in]   buffer    Buffer holding the serialized message.
344
 */
345
static aos_status_t _deserializeBcbMessage(aos_ssspbcbmessage_t* message, uint8_t* buffer)
346
{
347
  aosDbgCheck(message != NULL);
348
  aosDbgCheck(buffer != NULL);
349

    
350
  // only decode the first byte, which contains the message type
351
  message->type = buffer[0];
352

    
353
  // decode the message depedning on its type
354
  switch (message->type) {
355
    case AOS_SSSP_BCBMESSAGE_MSIINIT:
356
    case AOS_SSSP_BCBMESSAGE_MSIABORT:
357
    {
358
      return AOS_SUCCESS;
359
    }
360
    case AOS_SSSP_BCBMESSAGE_MSIID:
361
    {
362
      message->payload.id = 0;
363
      for (size_t byte = 0; byte < sizeof(aos_ssspmoduleid_t); ++byte) {
364
        message->payload.id |= (aos_ssspmoduleid_t)(buffer[byte+1] << ((sizeof(aos_ssspmoduleid_t) - (byte+1)) * 8));
365
      }
366
      return AOS_SUCCESS;
367
    }
368
    default:
369
    {
370
      message->type = AOS_SSSP_BCBMESSAGE_INVALID;
371
      return AOS_FAILURE;
372
    }
373
  }
374
}
375

    
376
/**
377
 * @brief   Common outro of the module stack initialization (MSI).
378
 * @details This function must be called on failure, success and if MSI is skipped.
379
 *
380
 * @param[in] msidata   Pointer to the temporary data structure used during MSI.
381
 */
382
static void _msiOutro(aos_ssspmsidata_t* msidata)
383
{
384
  aosDbgCheck(msidata != NULL);
385

    
386
  // local variables
387
  eventmask_t mask = 0;
388

    
389
  // start the internal uptime aggregation
390
  chSysLock();
391
  aosSysStartUptimeS();
392
#if (AMIROOS_CFG_SSSP_MASTER == true)
393
  // start toggling S for system synchronization
394
  _syncTimerStartS();
395
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
396
  chSysUnlock();
397

    
398
  // reset delay timer
399
  chVTIsArmed(&_delayTimer);
400
  // unregister delay events
401
  mask |= _delayEventListener.events;
402
  chEvtUnregister(&_delayEventSource, &_delayEventListener);
403

    
404
#if (AMIROOS_CFG_SSSP_STACK_END != true)
405
  // deactivate UP signal
406
  apalControlGpioSet(moduleSsspSignalUP(), APAL_GPIO_OFF);
407
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
408

    
409
  // reset timeout timer
410
  chVTReset(&msidata->timeout.timer);
411
  // unregister timeout events
412
  mask |= msidata->timeout.eventlistener.events;
413
  chEvtUnregister(&msidata->timeout.eventsource, &msidata->timeout.eventlistener);
414

    
415
  // discard any pending timing events
416
  chEvtWaitAnyTimeout(mask, TIME_IMMEDIATE);
417

    
418
  // proceed to operation phase
419
  aos.sssp.stage = AOS_SSSP_STAGE_OPERATION;
420

    
421
  return;
422
}
423

    
424
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
425

    
426
/******************************************************************************/
427
/* EXPORTED FUNCTIONS                                                         */
428
/******************************************************************************/
429

    
430
/**
431
 * @brief   Initialize all SSSP related data.
432
 */
433
void aosSsspInit(aos_timestamp_t* system_uptime)
434
{
435
  aosDbgCheck(system_uptime != NULL);
436

    
437
#if (AMIROOS_CFG_SSSP_STARTUP == true)
438
  // AMiRo-OS has to perform the basic initialization
439
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_1_1;
440
#else /* (AMIROOS_CFG_SSSP_STARTUP == true) */
441
  // basic initialization was already performed by a bootloader
442
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_2_1;
443
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
444

    
445
  // module ID is initialized as 'invalid'
446
  aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
447

    
448
#if (AMIROOS_CFG_DBG == true)
449
  // check all SSSP signals for correct state
450
  {
451
    apalControlGpioState_t state;
452
    apalControlGpioGet(moduleSsspSignalS(), &state);
453
    aosDbgAssert(state == APAL_GPIO_ON);
454
    apalControlGpioGet(moduleSsspSignalPD(), &state);
455
    aosDbgAssert(state == APAL_GPIO_OFF);
456
#if (AMIROOS_CFG_SSSP_MSI == true)
457
#if (AMIROOS_CFG_SSSP_STACK_START != true)
458
    apalControlGpioGet(moduleSsspSignalDN(), &state);
459
    aosDbgAssert(state == APAL_GPIO_OFF);
460
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
461
#if (AMIROOS_CFG_SSSP_STACK_END != true)
462
    apalControlGpioGet(moduleSsspSignalUP(), &state);
463
    aosDbgAssert(state == APAL_GPIO_OFF);
464
#endif /* (AMIROOS_CFG_SSSP_END_START != true) */
465
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
466
  }
467
#endif /* (AMIROOS_CFG_DBG == true) */
468

    
469
  // initialize static variables
470
  _uptime = system_uptime;
471
#if (AMIROOS_CFG_SSSP_MASTER == true)
472
  chVTObjectInit(&_synctimer);
473
  _synctime = 0;
474
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
475
#if (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)
476
  _syncskew = 0.0f;
477
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
478
  chVTObjectInit(&_delayTimer);
479
  chEvtObjectInit(&_delayEventSource);
480

    
481
  // signal interrupt setup
482
  palSetLineCallback(moduleSsspSignalPD()->gpio->line, aosSysGetStdGpioCallback(), &moduleSsspSignalPD()->gpio->line);
483
  palEnableLineEvent(moduleSsspSignalPD()->gpio->line, APAL2CH_EDGE(moduleSsspSignalPD()->meta.edge));
484
#if (AMIROOS_CFG_SSSP_MASTER == true)
485
  palSetLineCallback(moduleSsspSignalS()->gpio->line, aosSysGetStdGpioCallback(), &moduleSsspSignalS()->gpio->line);
486
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
487
  palSetLineCallback(moduleSsspSignalS()->gpio->line, _gpioCallbackSSignal, &moduleSsspSignalS()->gpio->line);
488
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
489
  palEnableLineEvent(moduleSsspSignalS()->gpio->line, APAL2CH_EDGE(moduleSsspSignalS()->meta.edge));
490
#if (AMIROOS_CFG_SSSP_MSI == true)
491
#if (AMIROOS_CFG_SSSP_STACK_START != true)
492
  palSetLineCallback(moduleSsspSignalDN()->gpio->line, aosSysGetStdGpioCallback(), &moduleSsspSignalDN()->gpio->line);
493
  palEnableLineEvent(moduleSsspSignalDN()->gpio->line, APAL2CH_EDGE(moduleSsspSignalDN()->meta.edge));
494
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
495
#if (AMIROOS_CFG_SSSP_STACK_END != true)
496
  palSetLineCallback(moduleSsspSignalUP()->gpio->line, aosSysGetStdGpioCallback(), &moduleSsspSignalUP()->gpio->line);
497
  palEnableLineEvent(moduleSsspSignalUP()->gpio->line, APAL2CH_EDGE(moduleSsspSignalUP()->meta.edge));
498
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
499
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
500

    
501
  return;
502
}
503

    
504
/**
505
 * @brief   Proceed to the next SSSP stage.
506
 * @note    Calling this function when in operation phase is invalid.
507
 *          The function aosSsspShutdownInit() must be used instead.
508
 *
509
 * @param[in]   listener  An optional listener, in case the system has to wait for a specific event.
510
 * @param[in]   mask      If the stage transition involves a timer event (handled internally by this function),
511
 *                        the given input value of this parameter defines the mask to be used for that event.
512
 *                        In case a listener was specified, the according mask is retrieved from the listener but this parameter is still used as input for sanity checks.
513
 * @param[out]  received  Output variable to store the received event mask to.
514
 *
515
 * @return  Status, indicating whether proceeding in the protocol was successful.
516
 */
517
aos_status_t aosSsspProceed(event_listener_t* listener, eventmask_t mask, eventmask_t* received)
518
{
519
  // local variables
520
#pragma GCC diagnostic push
521
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
522
  static aos_ssspstage_t laststage = AOS_SSSP_STAGE_UNDEFINED;
523
#pragma GCC diagnostic pop
524
  aos_ssspstage_t nextstage = aos.sssp.stage;
525

    
526
  // behaviour depends on current SSSP stage of the system
527
  switch (aos.sssp.stage) {
528
#if (AMIROOS_CFG_SSSP_STARTUP == true)
529

    
530
    /*
531
     * Deactivate S signal.
532
     * Master node delays execution by one period AOS_SSSP_DELAY.
533
     */
534
    case AOS_SSSP_STAGE_STARTUP_1_1:
535
    {
536
#if (AMIROOS_CFG_SSSP_MASTER == true)
537
      aosDbgCheck(listener == NULL);
538
      aosDbgCheck(mask != 0);
539
      aosDbgCheck(received != NULL);
540

    
541
      // delay execution and deactivate the S signal on success
542
      if (_delay(AOS_SSSP_DELAY, mask, received) == AOS_SUCCESS) {
543
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
544
        nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
545
      }
546
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
547
      aosDbgCheck(listener == NULL);
548
      aosDbgCheck(mask == 0);
549
      aosDbgCheck(received == NULL);
550

    
551
      // deactivate S signal and proceed
552
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
553
      nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
554
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
555
      break;
556
    }
557

    
558
    /*
559
     * Wait for the S signal to become inactive.
560
     */
561
    case AOS_SSSP_STAGE_STARTUP_1_2:
562
    {
563
      aosDbgCheck(listener != NULL);
564
      aosDbgCheck(mask == listener->events);
565
      aosDbgCheck(received != NULL);
566

    
567
      // wait for the S signal to become inactive and proceed on success
568
      if (_waitForS(listener, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
569
        nextstage = AOS_SSSP_STAGE_STARTUP_1_3;
570
      }
571
      break;
572
    }
573

    
574
    /*
575
     * Wait for the S signal to be activated by the master module.
576
     * The master delays execution by one period AOS_SSSP_DELAY.
577
     */
578
    case AOS_SSSP_STAGE_STARTUP_1_3:
579
    {
580
#if (AMIROOS_CFG_SSSP_MASTER == true)
581
      aosDbgCheck(listener == NULL);
582
      aosDbgCheck(mask != 0);
583
      aosDbgCheck(received != NULL);
584

    
585
      // delay execution and deactivate the S signal on success
586
      if (_delay(AOS_SSSP_DELAY, mask, received) == AOS_SUCCESS) {
587
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
588
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
589
      }
590
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
591
      aosDbgCheck(listener != NULL);
592
      aosDbgCheck(mask == listener->events);
593
      aosDbgCheck(received != NULL);
594

    
595
      // wait for the S signal to become active and activate it as well and proceed on success
596
      if (_waitForS(listener, APAL_GPIO_ON, received) == AOS_SUCCESS) {
597
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
598
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
599
      }
600
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
601
      break;
602
    }
603

    
604
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
605

    
606
    /*
607
     * Deactivate S signal.
608
     * Master node delays execution by one period AOS_SSSP_DELAY
609
     */
610
    case AOS_SSSP_STAGE_STARTUP_2_1:
611
    {
612
#if (AMIROOS_CFG_SSSP_MASTER == true)
613
      aosDbgCheck(listener == NULL);
614
      aosDbgCheck(mask != 0);
615
      aosDbgCheck(received != NULL);
616

    
617
      // delay execution and deactivate the S signal on success
618
      if (_delay(AOS_SSSP_DELAY, mask, received) == AOS_SUCCESS) {
619
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
620
        nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
621
      } else {
622
        aosprintf(".");
623
      }
624
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
625
      aosDbgCheck(listener == NULL);
626
      aosDbgCheck(mask == 0);
627
      aosDbgCheck(received == NULL);
628

    
629
      // deactivate S signal and proceed
630
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
631
      nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
632
      mask = 0;
633
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
634
      break;
635
    }
636

    
637
    /*
638
     * Wait for the S signal to become inactive.
639
     * When proceeding to operation phase, the master starts toggling S for synchronization
640
     */
641
    case AOS_SSSP_STAGE_STARTUP_2_2:
642
    {
643
      aosDbgCheck(listener != NULL);
644
      aosDbgCheck(mask == listener->events);
645
      aosDbgCheck(received != NULL);
646

    
647
      // wait for the S signal to become inactive and proceed on success
648
      if (_waitForS(listener, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
649
#if (AMIROOS_CFG_SSSP_MSI == true)
650
        nextstage = AOS_SSSP_STAGE_STARTUP_3;
651
#else /* (AMIROOS_CFG_SSSP_MSI == true) */
652
        // start the internal uptime aggregation
653
        chSysLock();
654
        aosSysStartUptimeS();
655
#if (AMIROOS_CFG_SSSP_MASTER == true)
656
        // start toggling S for system synchronization
657
        _syncTimerStartS();
658
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
659
        chSysUnlock();
660
        nextstage = AOS_SSSP_STAGE_OPERATION;
661
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
662
      }
663
      break;
664
    }
665

    
666
    /*
667
     * PLEASE NOTE:
668
     * Stage transitions during MSI are not hanlded by thins function but are executed in aosSsspMsi().
669
     */
670

    
671
    /*
672
     * Invalid operation.
673
     * Shutdon must be initiatid via the aosSsspShutdownInit() function.
674
     */
675
    case AOS_SSSP_STAGE_OPERATION:
676
    {
677
      aosDbgAssertMsg(false, "in order to exit operation phase, aosSsspShutdownInit() function must be used");
678
      break;
679
    }
680

    
681
    /*
682
     * Delay execution by one perion AOS_SSSP_DELAY, deactivate the SYNC signal and proceed.
683
     * NOTE: Actually only the module that initiated the shutdown has to delay execution here.
684
     *       For the sake of simlicity though, each module delays execution.
685
     */
686
    case AOS_SSSP_STAGE_SHUTDOWN_1_2:
687
    {
688
      aosDbgCheck(listener == NULL);
689
      aosDbgCheck(mask == 0);
690
      aosDbgCheck(received == NULL);
691

    
692
      // delay execution
693
      aosThdUSleep(AOS_SSSP_DELAY);
694
      // deactivate S signal
695
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
696
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_1_3;
697
      break;
698
    }
699

    
700
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true)
701

    
702
    /*
703
     * Wait for S signal to become inactive.
704
     */
705
    case AOS_SSSP_STAGE_SHUTDOWN_1_3:
706
    {
707
      aosDbgCheck(listener != NULL);
708
      aosDbgCheck(mask == listener->events);
709
      aosDbgCheck(received != NULL);
710

    
711
      // wait for the S signal to become inactive and proceed on success
712
      if (_waitForS(listener, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
713
        nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_1;
714
      }
715
      break;
716
    }
717

    
718
    /*
719
     * Evaluate PD signal to determine whether a shutdown or restart is requested.
720
     * The logical state is returned by the function call.
721
     */
722
    case AOS_SSSP_STAGE_SHUTDOWN_2_1:
723
    {
724
      aosDbgCheck(listener == NULL);
725
      aosDbgCheck(mask == 0);
726
      aosDbgCheck(received != NULL);
727

    
728
      // evaluate PD signal
729
      apalControlGpioState_t pd;
730
      apalControlGpioGet(moduleSsspSignalPD(), &pd);
731
      *received = (pd == APAL_GPIO_ON) ? (eventmask_t)~0 : 0;
732
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_2;
733
      break;
734
    }
735

    
736
    /*
737
     * Proceed with no further actions required.
738
     */
739
    case AOS_SSSP_STAGE_SHUTDOWN_2_2:
740
    {
741
      aosDbgCheck(listener == NULL);
742
      aosDbgCheck(mask == 0);
743
      aosDbgCheck(received == NULL);
744

    
745
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_3;
746
      break;
747
    }
748

    
749
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
750

    
751
    default:
752
    {
753
      // this case must never occur!
754
      aosDbgAssertMsg(false, "Invalid SSSP stage tranition occurred");
755
      break;
756
    }
757
  } /* end of switch (aos.sssp.stage) */
758

    
759
  // outro and return
760
  laststage = aos.sssp.stage;
761
  const aos_status_t status = (nextstage > aos.sssp.stage) ? AOS_SUCCESS : AOS_FAILURE;
762
  aos.sssp.stage = nextstage;
763

    
764
  return status;
765
}
766

    
767
#if (AMIROOS_CFG_SSSP_MSI == true) || defined(__DOXYGEN__)
768

    
769
/**
770
 * @brief   Initialize MSI data structure.
771
 *
772
 * @param[out]  msidata       Pointer to the MSI data structure to be initialized.
773
 * @param[in]   delayMask     Event maks used for delay events.
774
 * @param[in]   timeoutMask   Event mask used for timeout events.
775
 * @param[in]   gpioListener  Event listener for GPIO events.
776
 */
777
void aosSsspMsiInit(aos_ssspmsidata_t* msidata, eventmask_t delayMask, eventmask_t timeoutMask, event_listener_t* gpioListener)
778
{
779
  aosDbgCheck(msidata != NULL);
780
  aosDbgCheck(gpioListener != NULL);
781

    
782
  // register delay events
783
  chEvtRegisterMask(&_delayEventSource, &_delayEventListener, delayMask);
784

    
785
  // initialize timeout related data
786
  chVTObjectInit(&msidata->timeout.timer);
787
  chEvtObjectInit(&msidata->timeout.eventsource);
788
  chEvtRegisterMask(&msidata->timeout.eventsource, &msidata->timeout.eventlistener, timeoutMask);
789

    
790
  // set GPIO event information
791
  msidata->signals.eventlistener = gpioListener;
792

    
793
  // initialize BCB related data
794
  msidata->bcb.lastid = AOS_SSSP_MODULEID_INVALID;
795

    
796
  return;
797
}
798

    
799
/**
800
 * @brief   Execute module stack initialization (MSI).
801
 *
802
 * @param[in]   msidata   Data required for MSI.
803
 * @param[out]  received  Output variable to store the received event mask to.
804
 */
805
void aosSsspMsi(aos_ssspmsidata_t* msidata, eventmask_t* received)
806
{
807
  aosDbgCheck(msidata != NULL);
808
  aosDbgCheck(received != NULL);
809

    
810
  /*
811
   * execute anything that does not depend on BCB messages or events
812
   */
813
  switch (aos.sssp.stage) {
814
    case AOS_SSSP_STAGE_STARTUP_3:
815
    case AOS_SSSP_STAGE_STARTUP_3_1:
816
    {
817
      aosDbgPrintf(">>> MSI init\n");
818
      // setup timeout timer
819
      chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
820
      // proceed
821
#if (AMIROOS_CFG_SSSP_MASTER == true)
822
      aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_1_BROADCASTINIT;
823
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
824
      aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_1_WAITFORINIT;
825
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
826
      break;
827
    }
828

    
829
#if (AMIROOS_CFG_SSSP_MASTER == true)
830
    case AOS_SSSP_STAGE_STARTUP_3_1_BROADCASTINIT:
831
    {
832
      aosDbgPrintf("%u\tsend INIT\t", chVTGetSystemTime());
833
      // send init command
834
      msidata->bcb.message.type = AOS_SSSP_BCBMESSAGE_MSIINIT;
835
      _serializeBcbMessage(msidata->bcb.buffer, msidata->bcb.message);
836
      if (moduleSsspBcbTransmit(msidata->bcb.buffer, sizeof(aos_ssspbcbmessage_t)) == AOS_SSSP_BCB_SUCCESS) {
837
        aosDbgPrintf("ok\tS+\t");
838
        // activate S
839
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
840
        // restart timeout timer
841
        chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
842
        // proceed
843
#if (AMIROOS_CFG_SSSP_STACK_START == true)
844
        aosDbgPrintf("->start\n");
845
        aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_2;
846
#else /* (AMIROOS_CFG_SSSP_STACK_START == true) */
847
        aosDbgPrintf("->count\n");
848
        aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT;
849
#endif /* (AMIROOS_CFG_SSSP_STACK_START == true) */
850
      } else {
851
        aosDbgPrintf("fail\t->retry\n");
852
        // retry in next iteration
853
      }
854
      break;
855
    }
856
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
857

    
858
#if (AMIROOS_CFG_SSSP_STACK_START == true)
859
    case AOS_SSSP_STAGE_STARTUP_3_2:
860
    {
861
      aosDbgPrintf("%u\tstart\t", chVTGetSystemTime());
862
      // set the own module ID
863
      aos.sssp.moduleId = 1;
864
      // broadcast the new ID
865
      msidata->bcb.message.type = AOS_SSSP_BCBMESSAGE_MSIID;
866
      msidata->bcb.message.payload.id = aos.sssp.moduleId;
867
      aosDbgPrintf("bc ID (%u)\t", msidata->bcb.message.payload.id);
868
      _serializeBcbMessage(msidata->bcb.buffer, msidata->bcb.message);
869
      if (moduleSsspBcbTransmit(msidata->bcb.buffer, sizeof(aos_ssspbcbmessage_t)) == AOS_SSSP_BCB_SUCCESS) {
870
        aosDbgPrintf("ok\t->count\n");
871
        // restart timeout timer
872
        chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
873
        // set delay timer so UP will be activated and S deactivated later
874
        chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_DELAY), _timerCallback, &_delayEventSource);
875
        // store the transmitted ID
876
        msidata->bcb.lastid = msidata->bcb.message.payload.id;
877
        // proceed
878
        aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT;
879
      } else {
880
        aosDbgPrintf("fail\t->retry\n");
881
        // retry next iteration
882
      }
883
      break;
884
    }
885
#endif /* (AMIROOS_CFG_SSSP_STACK_START == true) */
886

    
887
    case AOS_SSSP_STAGE_STARTUP_3_3_BROADCASTID:
888
    {
889
      // broadcast ID
890
      msidata->bcb.message.type = AOS_SSSP_BCBMESSAGE_MSIID;
891
      msidata->bcb.message.payload.id = aos.sssp.moduleId;
892
      aosDbgPrintf("%u\tbc ID (%u)\t", chVTGetSystemTime(), msidata->bcb.message.payload.id);
893
      _serializeBcbMessage(msidata->bcb.buffer, msidata->bcb.message);
894
      if (moduleSsspBcbTransmit(msidata->bcb.buffer, sizeof(aos_ssspbcbmessage_t)) == AOS_SSSP_BCB_SUCCESS) {
895
        aosDbgPrintf("ok->count\n");
896
        // restart timeout timer
897
        chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
898
        // set delay timer so UP will be activated and S deactivated later
899
        chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_DELAY), _timerCallback, &_delayEventSource);
900
        // store the transmitted ID
901
        msidata->bcb.lastid = msidata->bcb.message.payload.id;
902
        // return to receiving
903
        aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT;
904
      } else {
905
        aosDbgPrintf("fail\t->retry\n");
906
        // retry next iteration
907
      }
908
      break;
909
    }
910

    
911
    case AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT:
912
    {
913
      aosDbgPrintf("%u\tabort\t", chVTGetSystemTime());
914
      // reset timeout timer
915
      chVTReset(&msidata->timeout.timer);
916
      // transmit abort message
917
      msidata->bcb.message.type = AOS_SSSP_BCBMESSAGE_MSIABORT;
918
      _serializeBcbMessage(msidata->bcb.buffer, msidata->bcb.message);
919
      if (moduleSsspBcbTransmit(msidata->bcb.buffer, sizeof(aos_ssspbcbmessage_t)) == AOS_SSSP_BCB_SUCCESS) {
920
        aosDbgPrintf("ok\tS-\t->sync\n");
921
        // deactivate S
922
        apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
923
        // proceed
924
        aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_SYNC;
925
      } else {
926
        aosDbgPrintf("fail\t->retry\n");
927
        // retry next iteration
928
      }
929
      break;
930
    }
931

    
932
    default:
933
    {
934
      // all other stages depend on events or BCB messages and are thus handled below
935
      break;
936
    }
937
  }
938

    
939
  /*
940
   * handle BCB messages
941
   */
942
  msidata->bcb.status = moduleSsspBcbReceive(msidata->bcb.buffer, sizeof(aos_ssspbcbmessage_t));
943
  switch (msidata->bcb.status) {
944

    
945
    /* VALID BCB MESSAGE
946
     * A message was received via BCB that seems to be plausible.
947
     * The first BCB message initiates the sequence once.
948
     * During MSI an arbitrary number of IDs will be received.
949
     * Abort messages can be received at any time.
950
     */
951
    case AOS_SSSP_BCB_SUCCESS:
952
    {
953
      aosDbgPrintf("%u\tBCB\t", chVTGetSystemTime());
954
      // deserialize message and handle types differently
955
      _deserializeBcbMessage(&msidata->bcb.message, msidata->bcb.buffer);
956
      switch (msidata->bcb.message.type) {
957
        // initialization message
958
        case AOS_SSSP_BCBMESSAGE_MSIINIT:
959
        {
960
#if (AMIROOS_CFG_SSSP_MASTER == true)
961
          aosDbgPrintf("INIT\t->abort\n");
962
          // initiate abortion
963
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
964
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
965
          aosDbgPrintf("INIT\t");
966
          // check for correct SSSP stage
967
          if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_1_WAITFORINIT) {
968
            aosDbgPrintf("S+\t");
969
            // activate S
970
            apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
971
            // restart timeout timer
972
            chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
973
            // proceed
974
#if (AMIROOS_CFG_SSSP_STACK_START == true)
975
            aosDbgPrintf("->start\n");
976
            aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_2;
977
#else /* (AMIROOS_CFG_SSSP_STACK_START == true) */
978
            aosDbgPrintf("->count\n");
979
            aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT;
980
#endif /* (AMIROOS_CFG_SSSP_STACK_START == true) */
981
          } else {
982
            aosDbgPrintf("->abort\n");
983
            // initiate abortion
984
            aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
985
          }
986
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
987
          break;
988
        }
989
        // ID message
990
        case AOS_SSSP_BCBMESSAGE_MSIID:
991
        {
992
          aosDbgPrintf("ID (%u)\t", msidata->bcb.message.payload.id);
993
          // check for correct stage and whether ID was increased
994
          if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT &&
995
              msidata->bcb.message.payload.id > msidata->bcb.lastid) {
996
            aosDbgPrintf("ok\n");
997
            // restart timeout timer
998
            chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
999
            // store ID
1000
            msidata->bcb.lastid = msidata->bcb.message.payload.id;
1001
          } else {
1002
            aosDbgPrintf("fail\t->abort\n");
1003
            // initiate abortion
1004
            aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1005
          }
1006
          break;
1007
        }
1008
        // abort message
1009
        case AOS_SSSP_BCBMESSAGE_MSIABORT:
1010
        {
1011
          aosDbgPrintf("ABORT\tS-\t->sync\n");
1012
          // reset timeout timer
1013
          chVTReset(&msidata->timeout.timer);
1014
          // deactivate S
1015
          apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
1016
          // proceed to abort synchronization
1017
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_SYNC;
1018
          break;
1019
        }
1020
        // invalid message
1021
        default:
1022
        {
1023
          aosDbgPrintf("invalid\t->abort\n");
1024
          // initiate abortion
1025
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1026
          break;
1027
        }
1028
      }
1029
      return;
1030
    }
1031

    
1032
    /* INVALID BCB MESSAGE
1033
     * An obviously invalid message was received via BCB.
1034
     */
1035
    case AOS_SSSP_BCB_INVALIDMSG:
1036
    {
1037
      // error but ignore
1038
      aosDbgPrintf("WARNING: unknown message received via BCB\n");
1039
      break;
1040
    }
1041

    
1042
    /* NO BCB MESSAGE
1043
     * No message was received via BCB at all.
1044
     */
1045
    case AOS_SSSP_BCB_ERROR:
1046
    {
1047
      // do nothing and proceed
1048
      break;
1049
    }
1050
  }
1051

    
1052
  /*
1053
   * handle events
1054
   */
1055
  *received = chEvtWaitOneTimeout(ALL_EVENTS, TIME_IMMEDIATE);
1056
  /* TIMEOUT EVENT
1057
   * Timeouts can occur at any time during MSI after they have been initialized.
1058
   * As soon as the module has made it to the 3.4 completion stage, there will be no more timeouts, though.
1059
   */
1060
  if (*received & msidata->timeout.eventlistener.events) {
1061
    aosDbgPrintf("%u\ttimeout\t", chVTGetSystemTime());
1062
    // special cases
1063
    if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_1_WAITFORINIT ||
1064
        aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_4_COMPLETION) {
1065
      // master did not initiate the sequence
1066
      if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_1_WAITFORINIT) {
1067
        aosDbgPrintf("no init\t->operation\n");
1068
        // invalidate module ID
1069
        aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
1070
        aosDbgPrintf("<<< MSI skipped\n");
1071
      }
1072
      // no abort messages received after completion
1073
      else /* if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_4_COMPLETION) */ {
1074
        aosDbgPrintf("success\t->operation\n");
1075
        aosDbgPrintf("<<< MSI completed\n");
1076
      }
1077
      // cleanup and proceed to operation phase
1078
      _msiOutro(msidata);
1079
    }
1080
    // general timeout
1081
    else {
1082
      aosDbgPrintf("->abort\n");
1083
      // initiate active abort
1084
      aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1085
    }
1086
    // clear event
1087
    chEvtGetAndClearFlags(&msidata->timeout.eventlistener);
1088
    *received &= ~(msidata->timeout.eventlistener.events);
1089
  }
1090
  /* DELAY EVENT
1091
   * Delays are only used to toggle the UP signal.
1092
   */
1093
  else if (*received & _delayEventListener.events) {
1094
    aosDbgPrintf("%u\tdelay\t", chVTGetSystemTime());
1095
#if (AMIROOS_CFG_SSSP_STACK_END == true)
1096
    apalControlGpioState_t s;
1097
    aosDbgPrintf("S-\t");
1098
    // deactivate S
1099
    apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
1100
    // verify deactivation
1101
    apalControlGpioGet(moduleSsspSignalS(), &s);
1102
    if (s == APAL_GPIO_OFF) {
1103
      aosDbgPrintf("ok\t->done\n");
1104
      // set the timeout timer so no abort messages are missed
1105
      chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
1106
      // proceed to the completion stage
1107
      aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_COMPLETION;
1108
    } else {
1109
      aosDbgPrintf("fail\t->abort\n");
1110
      // initiate active abort
1111
      aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1112
    }
1113
#else /* (AMIROOS_CFG_SSSP_STACK_END == true) */
1114
    apalControlGpioState_t s;
1115
    // read the current state of the UP signal
1116
    apalControlGpioGet(moduleSsspSignalUP(), &s);
1117
    if (s == APAL_GPIO_OFF) {
1118
      aosDbgPrintf("UP+ & S-\n");
1119
      // activate UP in order to trigger the next module
1120
      apalControlGpioSet(moduleSsspSignalUP(), APAL_GPIO_ON);
1121
      // deactivate S
1122
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
1123
      // set the delay timer so the UP signal will get deactivated again later
1124
      chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_DELAY), _timerCallback, &_delayEventSource);
1125
    } else {
1126
      aosDbgPrintf("UP-\n");
1127
      // deactvate UP
1128
      apalControlGpioSet(moduleSsspSignalUP(), APAL_GPIO_OFF);
1129
    }
1130
#endif /* (AMIROOS_CFG_SSSP_STACK_END == true) */
1131
    // clear event
1132
    chEvtGetAndClearFlags(&_delayEventListener);
1133
    *received &= ~(_delayEventListener.events);
1134
  }
1135
  /* GPIO EVENT
1136
   * GPIO events can occurr at any time, but only DN and S events are expected.
1137
   * DN may be activated only once per module to trigger the counting process and is deactivated briefly after activation.
1138
   * S must only deactivate when MSI is comleted by the last module in the stack.
1139
   */
1140
  else if (*received & msidata->signals.eventlistener->events) {
1141
    aosDbgPrintf("%u\tGPIO\t", chVTGetSystemTime());
1142
    apalControlGpioState_t s;
1143
    // fetch event flags
1144
    eventflags_t flags = chEvtGetAndClearFlags(msidata->signals.eventlistener);
1145
#if (AMIROOS_CFG_SSSP_STACK_START != true)
1146
    // if the DN signal was triggered
1147
    if (flags & moduleSsspEventflagDN()) {
1148
      // read the signal state
1149
      apalControlGpioGet(moduleSsspSignalDN(), &s);
1150
      // rising edge
1151
      if (s == APAL_GPIO_ON) {
1152
        aosDbgPrintf("+DN\t");
1153
        // Correct SSSP stage and no ID assigned yet?
1154
        if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT &&
1155
            aos.sssp.moduleId == AOS_SSSP_MODULEID_INVALID) {
1156
          // set the own module ID
1157
          aos.sssp.moduleId = msidata->bcb.lastid + 1;
1158
          aosDbgPrintf("set ID (%u)\t->bc ID\n", aos.sssp.moduleId);
1159
          // proceed to broadcasting
1160
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_3_BROADCASTID;
1161
        }
1162
        // incorrect SSSP stage or ID already assigned (triggered for a second time)
1163
        else {
1164
          aosDbgPrintf("->abort\n");
1165
          // initiate active abort
1166
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1167
        }
1168
      }
1169
      // falling egde
1170
      else {
1171
        aosDbgPrintf("-DN\n");
1172
        // ignored
1173
      }
1174
      // clear DN flags
1175
      flags &= ~moduleSsspEventflagDN();
1176
    }
1177
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
1178
#if (AMIROOS_CFG_SSSP_STACK_END != true)
1179
    // if the UP signal was triggered
1180
    if (flags & moduleSsspEventflagUP()) {
1181
      /*
1182
       * The UP signal is acting as output only.
1183
       * Any events resulting from toggling the signal are thus ignored.
1184
       */
1185
      aosDbgPrintf("?UP\n");
1186
      // clear UP flags
1187
      flags &= ~moduleSsspEventflagUP();
1188
    }
1189
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
1190
    // if the S signal was triggered
1191
    if (flags & moduleSsspEventflagS()) {
1192
      // read the signal state
1193
      apalControlGpioGet(moduleSsspSignalS(), &s);
1194
      // rising edge
1195
      if (s == APAL_GPIO_ON) {
1196
        aosDbgPrintf("+S\n");
1197
        // ignored
1198
      }
1199
      // falling edge
1200
      else {
1201
        aosDbgPrintf("-S\t");
1202
        // special case
1203
        if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_SYNC) {
1204
          aosDbgPrintf("->operation\n");
1205
          // invalidate module ID
1206
          aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
1207
          // proceed
1208
          aosDbgPrintf("<<< MSI aborted\n");
1209
          _msiOutro(msidata);
1210
        }
1211
#if (AMIROOS_CFG_SSSP_STACK_END == true)
1212
        // Correct SSSP stage?
1213
        else if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_4_COMPLETION) {
1214
          aosDbgPrintf("ok\n");
1215
          // ignored
1216
        } else {
1217
          aosDbgPrintf("->abort\n");
1218
          // initialize abortion
1219
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1220
        }
1221
#else /* (AMIROOS_CFG_SSSP_STACK_END == true) */
1222
        // Correct SSSP stage and ID assigned?
1223
        else if (aos.sssp.stage == AOS_SSSP_STAGE_STARTUP_3_3_WAIT4EVENT &&
1224
            aos.sssp.moduleId != AOS_SSSP_MODULEID_INVALID) {
1225
          aosDbgPrintf("->done\n");
1226
          // set the timeout timer so no abort messages are missed
1227
          chVTSet(&msidata->timeout.timer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &msidata->timeout.eventsource);
1228
          // proceed to the completion stage
1229
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_COMPLETION;
1230
        } else {
1231
          aosDbgPrintf("->abort\n");
1232
          // initiate abortion
1233
          aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_3_4_ABORTION_INIT;
1234
        }
1235
#endif /* (AMIROOS_CFG_SSSP_STACK_END == true) */
1236
      }
1237
      // clear S flags
1238
      flags &= ~moduleSsspEventflagS();
1239
    }
1240
    // any further flags set?
1241
    if (flags) {
1242
      aosDbgPrintf("0x%08X\n", flags);
1243
      // reassign remaining flags and do not clear event mask
1244
      msidata->signals.eventlistener->flags |= flags;
1245
    } else {
1246
      // clear event
1247
      *received &= ~(msidata->signals.eventlistener->events);
1248
    }
1249
  }
1250
  /* OTHER EVENT
1251
   * Any other events may occur as well but are not handled by this function
1252
   */
1253
  else if (*received != 0){
1254
    aosDbgPrintf("%u\tOTHER\t0x%08X\n", chVTGetSystemTime(), *received);
1255
    // do not clear the event mask
1256
  }
1257

    
1258
  return;
1259
}
1260

    
1261
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
1262

    
1263
#if ((AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)) || defined(__DOXYGEN__)
1264

    
1265
/**
1266
 * @brief   Retrieve the offset between local clock and system synchronization signal.
1267
 *
1268
 * @return
1269
 */
1270
inline float aosSsspGetSyncSkew(void)
1271
{
1272
  return _syncskew;
1273
}
1274

    
1275
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
1276

    
1277
/**
1278
 * @brief   Leave the operation pahse and initiate or acknowledge shutdown.
1279
 *
1280
 * @param[in] active  Flag, indicating whether the shutdon shall be activeliy initiated (true) or a passive request was received (false).
1281
 */
1282
void aosSsspShutdownInit(bool active)
1283
{
1284
  aosDbgAssert(aos.sssp.stage == AOS_SSSP_STAGE_OPERATION);
1285

    
1286
  // proceed to shutdown phase
1287
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_1;
1288

    
1289
  // activate PD if this was an active shutdown initiation
1290
  if (active) {
1291
    apalControlGpioSet(moduleSsspSignalPD(), APAL_GPIO_ON);
1292
  }
1293

    
1294
#if (AMIROOS_CFG_SSSP_MASTER == true)
1295
  // stop toggling S
1296
  chVTReset(&_synctimer);
1297
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
1298

    
1299
  // activate S
1300
  apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
1301

    
1302
  // proceed in the shutdown phase
1303
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_2;
1304

    
1305
  return;
1306
}
1307

    
1308
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true) || defined(__DOXYGEN__)
1309

    
1310
/**
1311
 * @brief   Sequnetially broadcast an identifier via S to specify a specific way to shutdown or restart.
1312
 *
1313
 * @param[in] identifier  Identifier to be broadcasted.
1314
 */
1315
void aosSsspShutdownBroadcastIdentifier(unsigned int identifier)
1316
{
1317
  // only broadcast anything if a identifier greater than 0 was specified
1318
  if (identifier > 0) {
1319
    // broadcast identifier
1320
    for (unsigned int pulse = 0; pulse < identifier; ++pulse) {
1321
      // wait one delay time with S being deactivated
1322
      aosThdUSleep(AOS_SSSP_DELAY);
1323

    
1324
      // activate S for one AOS_SSSP_DELAY.
1325
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_ON);
1326
      aosThdUSleep(AOS_SSSP_DELAY);
1327
      apalControlGpioSet(moduleSsspSignalS(), APAL_GPIO_OFF);
1328
    }
1329
  }
1330

    
1331
  // let a timeout pass and return
1332
  aosThdUSleep(AOS_SSSP_TIMEOUT);
1333

    
1334
  return;
1335
}
1336

    
1337
/**
1338
 * @brief   Wait for a pulse during the SSSP shutdown disambiguation procedure.
1339
 * @details Whenever a pulse is received, the identifier is incremented.
1340
 *          Otherwise the internal timer is configured and will eventually trigger a timeout event.
1341
 *
1342
 * @param[in]     gpiolistener  Event listener for GPIO events.
1343
 * @param[in]     sflags        Event flags to be set by edges on the S signal.
1344
 * @param[in]     timermask     Event maks to use for the internal timer.
1345
 * @param[in,out] identifier    Identifier variable to increment.
1346
 *
1347
 * @return  The event mask of whatever event was received during this function call.
1348
 */
1349
eventmask_t aosSsspShutdownWaitForIdentifierPulse(event_listener_t* gpiolistener, const eventflags_t sflags, eventmask_t timermask, unsigned int* identifier)
1350
{
1351
  aosDbgCheck(gpiolistener != NULL);
1352
  aosDbgCheck(sflags != 0);
1353
  aosDbgCheck(timermask != 0);
1354
  aosDbgCheck(identifier != NULL);
1355

    
1356
  // local variables
1357
  eventmask_t mask;
1358
  eventflags_t flags;
1359
  apalControlGpioState_t sstate;
1360

    
1361
  // arm the timer once
1362
  if (!chVTIsArmed(&_delayTimer)) {
1363
    chEvtRegisterMask(&_delayEventSource, &_delayEventListener, timermask);
1364
    chVtSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_delayEventSource);
1365
  }
1366

    
1367
  // wait for any event to occur (do not apply any filters in order not to miss any events)
1368
  mask = chEvtWaitOne(ALL_EVENTS);
1369

    
1370
  // GPIO event
1371
  if (mask & gpiolistener->events) {
1372
    // retrieve flags without clearing them
1373
    flags = gpiolistener->flags;
1374
    apalControlGpioGet(moduleSsspSignalS(), &sstate);
1375
    if (flags == sflags) {
1376
      apalControlGpioGet(moduleSsspSignalS(), &sstate);
1377
      // if this was the end of a pulse
1378
      if (sstate == APAL_GPIO_OFF) {
1379
        // restart the timer
1380
        chVtSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_delayEventSource);
1381
        // increment the identifier
1382
        ++(*identifier);
1383
      }
1384
    }
1385
  }
1386
  // timer event
1387
  else if (mask & _delayEventListener.events) {
1388
    // unregister event
1389
    chEvtUnregister(&_delayEventSource, &_delayEventListener);
1390
  }
1391
  // any further events must be handled externally
1392

    
1393
  return mask;
1394
}
1395

    
1396
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
1397

    
1398
#endif /* (AMIROOS_CFG_SSSP_ENABLE == true) */
1399

    
1400
/** @} */