Statistics
| Branch: | Tag: | Revision:

amiro-os / core / src / aos_sssp.c @ c218345a

History | View | Annotate | Download (26.088 KB)

1
/*
2
AMiRo-OS is an operating system designed for the Autonomous Mini Robot (AMiRo) platform.
3
Copyright (C) 2016..2019  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
 */
87
static virtual_timer_t _delayTimer;
88

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

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

    
99
/******************************************************************************/
100
/* LOCAL FUNCTIONS                                                            */
101
/******************************************************************************/
102

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

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

    
114
  apalControlGpioState_t s;
115
  aos_timestamp_t t;
116

    
117
  chSysLockFromISR();
118

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

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

    
148
  return;
149
}
150

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

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

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

    
165
  apalControlGpioState_t state;
166
  aos_timestamp_t uptime;
167

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

    
186
  return;
187
}
188

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

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

    
204
  return;
205
}
206

    
207
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
208

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

    
218
  chSysLockFromISR();
219
  chEvtBroadcastI((event_source_t*)par);
220
  chSysUnlockFromISR();
221

    
222
  return;
223
}
224

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

    
241
  // wait for the next event (do not apply any filters in order not to miss any events)
242
  *received = chEvtWaitOne(ALL_EVENTS);
243
  // if the correct event was triggered
244
  if (*received == listener->events) {
245
    apalControlGpioState_t s;
246
    apalControlGpioGet(&moduleSsspGpioS, &s);
247
    // only check for the expected event
248
    if (flags & listener->flags && s == signalstate) {
249
      // unset the expected flags but keep any other ones
250
      listener->flags &= ~flags;
251
      return AOS_SUCCESS;
252
    }
253
  }
254

    
255
  return AOS_FAILURE;
256
}
257

    
258
/**
259
 * @brief   Uses the local delay timer to setup a timed event and waits for it to fire.
260
 *
261
 * @param[in]   dt          Delay time in microseconds to be set to the timer.
262
 * @param[in]   mask        Event mask to be used for the delay timer.
263
 * @param[in]   arm_timer   Flag to indicate whether the timer has to be armed yet (i.e. on first call of this function).
264
 * @param[out]  received    Output variable to store the received event mask to.
265
 *
266
 * @return  Status, indicating whether the expected event (delay timer) was received.
267
 */
268
static inline aos_status_t _delay(uint32_t dt, eventmask_t mask, bool arm_timer, eventmask_t* received)
269
{
270
  aosDbgCheck(dt != TIME_IMMEDIATE);
271
  aosDbgCheck(mask != 0);
272
  aosDbgCheck(received != NULL);
273

    
274
  // arm the delay timer once
275
  if (arm_timer) {
276
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, mask);
277
    chVTSet(&_delayTimer, chTimeUS2I(dt), _timerCallback, &_eventSourceDelay);
278
  }
279

    
280
  // wait for any event to occur (do not apply any filters in order not to miss any events)
281
  *received = chEvtWaitOne(ALL_EVENTS);
282

    
283
  // if the timer event was received, cleanup
284
  if (*received & _eventListenerDelay.events) {
285
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
286
    return AOS_SUCCESS;
287
  }
288

    
289
  return AOS_FAILURE;
290
}
291

    
292
/******************************************************************************/
293
/* EXPORTED FUNCTIONS                                                         */
294
/******************************************************************************/
295

    
296
/**
297
 * @brief   Initialize all SSSP related data.
298
 */
299
void aosSsspInit(aos_timestamp_t* system_uptime)
300
{
301
  aosDbgCheck(system_uptime != NULL);
302

    
303
  // local variables
304
  apalControlGpioState_t state;
305

    
306
#if (AMIROOS_CFG_SSSP_STARTUP == true)
307
  // AMiRo-OS has to perform the basic initialization
308
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_1_1;
309
#else /* (AMIROOS_CFG_SSSP_STARTUP == true) */
310
  // basic initialization was already performed by a bootloader
311
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_2_1;
312
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
313

    
314
  // check all SSSP signals for correct state
315
  apalControlGpioGet(&moduleSsspGpioS, &state);
316
  aosDbgAssert(state == APAL_GPIO_ON);
317
  apalControlGpioGet(&moduleSsspGpioPD, &state);
318
  aosDbgAssert(state == APAL_GPIO_OFF);
319
#if (AMIROOS_CFG_SSSP_MSI == true)
320
#if (AMIROOS_CFG_SSSP_STACK_END != true)
321
  apalControlGpioGet(&moduleSsspGpioUP, &state);
322
  aosDbgAssert(state == APAL_GPIO_OFF);
323
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
324
#if (AMIROOS_CFG_SSSP_STACK_START != true)
325
  apalControlGpioGet(&moduleSsspGpioDN, &state);
326
  aosDbgAssert(state == APAL_GPIO_OFF);
327
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
328
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
329

    
330
#if (AMIROOS_CFG_SSSP_MSI == true)
331
  // module ID is initialized as 'invalid'
332
  aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
333
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
334

    
335
  // initialize static variables
336
  _uptime = system_uptime;
337
#if (AMIROOS_CFG_SSSP_MASTER == true)
338
  chVTObjectInit(&_synctimer);
339
  _synctime = 0;
340
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
341
#if (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)
342
  _syncskew = 0.0f;
343
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
344
  chVTObjectInit(&_delayTimer);
345
  chEvtObjectInit(&_eventSourceDelay);
346

    
347
  // signal interrupt setup
348
  palSetLineCallback(moduleSsspGpioPD.gpio->line, aosSysGetStdGpioCallback(), &moduleSsspGpioPD.gpio->line);
349
  palEnableLineEvent(moduleSsspGpioPD.gpio->line, APAL2CH_EDGE(moduleSsspGpioPD.meta.edge));
350
#if (AMIROOS_CFG_SSSP_MASTER == true)
351
  palSetLineCallback(moduleSsspGpioS.gpio->line, aosSysGetStdGpioCallback(), &moduleSsspGpioS.gpio->line);
352
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
353
  palSetLineCallback(moduleSsspGpioS.gpio->line, _gpioCallbackSSignal, &moduleSsspGpioS.gpio->line);
354
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
355
  palEnableLineEvent(moduleSsspGpioS.gpio->line, APAL2CH_EDGE(moduleSsspGpioS.meta.edge));
356
#if (AMIROOS_CFG_SSSP_MSI == true)
357
#if (AMIROOS_CFG_SSSP_STACK_START != true)
358
  palSetLineCallback(moduleSsspGpioDN.gpio->line, aosSysGetStdGpioCallback(), &moduleSsspGpioDN.gpio->line);
359
  palEnableLineEvent(moduleSsspGpioDN.gpio->line, APAL2CH_EDGE(moduleSsspGpioDN.meta.edge));
360
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
361
#if (AMIROOS_CFG_SSSP_STACK_END != true)
362
  palSetLineCallback(moduleSsspGpioUP.gpio->line, aosSysGetStdGpioCallback(), &moduleSsspGpioUP.gpio->line);
363
  palEnableLineEvent(moduleSsspGpioUP.gpio->line, APAL2CH_EDGE(moduleSsspGpioUP.meta.edge));
364
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
365
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
366

    
367
  return;
368
}
369

    
370
/**
371
 * @brief   Proceed to the next SSSP stage.
372
 * @note    Calling this function when in operation phase is invalid.
373
 *          The function aosSsspShutdownInit() must be used instead.
374
 *
375
 * @param[in]   listener  An optional listener, in case the system has to wait for a specific event.
376
 * @param[in]   flags     The relevant flags to wait for (mandatory if a listener is given).
377
 * @param[in]   mask      If the stage transition involves a timer event (handled internally by this function),
378
 *                        the given input value of this parameter defines the mask to be used for that event.
379
 *                        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.
380
 * @param[out]  received  Output variable to store the received event mask to.
381
 *
382
 * @return  Status, indicating whether proceeding in the protocol was successful.
383
 */
384
aos_status_t aosSsspProceed(event_listener_t* listener, eventflags_t flags, eventmask_t mask, eventmask_t* received)
385
{
386
  // local variables
387
#pragma GCC diagnostic push
388
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
389
  static aos_ssspstage_t laststage = AOS_SSSP_STAGE_UNDEFINED;
390
#pragma GCC diagnostic pop
391
  aos_ssspstage_t nextstage = aos.sssp.stage;
392

    
393
  // behaviour depends on current SSSP stage of the system
394
  switch (aos.sssp.stage) {
395
#if (AMIROOS_CFG_SSSP_STARTUP == true)
396

    
397
    /*
398
     * Deactivate S signal.
399
     * Master node delays execution by one period AOS_SSSP_DELAY.
400
     */
401
    case AOS_SSSP_STAGE_STARTUP_1_1:
402
    {
403
#if (AMIROOS_CFG_SSSP_MASTER == true)
404
      aosDbgCheck(listener == NULL);
405
      aosDbgCheck(flags == 0);
406
      aosDbgCheck(mask != 0);
407
      aosDbgCheck(received != NULL);
408

    
409
      // delay execution and deactivate the S signal on success
410
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_1), received) == AOS_SUCCESS) {
411
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
412
        nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
413
      }
414
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
415
      aosDbgCheck(listener == NULL);
416
      aosDbgCheck(flags == 0);
417
      aosDbgCheck(mask == 0);
418
      aosDbgCheck(received == NULL);
419

    
420
      // deactivate S signal and proceed
421
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
422
      nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
423
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
424
      break;
425
    }
426

    
427
    /*
428
     * Wait for the S signal to become inactive.
429
     */
430
    case AOS_SSSP_STAGE_STARTUP_1_2:
431
    {
432
      aosDbgCheck(listener != NULL);
433
      aosDbgCheck(flags != 0);
434
      aosDbgCheck(mask == listener->events);
435
      aosDbgCheck(received != NULL);
436

    
437
      // wait for the S signal to become inactive and proceed on success
438
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
439
        nextstage = AOS_SSSP_STAGE_STARTUP_1_3;
440
      }
441
      break;
442
    }
443

    
444
    /*
445
     * Wait for the S signal to be activated by the master module.
446
     * The master delays execution by one period AOS_SSSP_DELAY.
447
     */
448
    case AOS_SSSP_STAGE_STARTUP_1_3:
449
    {
450
#if (AMIROOS_CFG_SSSP_MASTER == true)
451
      aosDbgCheck(listener == NULL);
452
      aosDbgCheck(flags == 0);
453
      aosDbgCheck(mask != 0);
454
      aosDbgCheck(received != NULL);
455

    
456
      // delay execution and deactivate the S signal on success
457
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_3), received) == AOS_SUCCESS) {
458
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
459
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
460
      }
461
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
462
      aosDbgCheck(listener != NULL);
463
      aosDbgCheck(flags != 0);
464
      aosDbgCheck(mask == listener->events);
465
      aosDbgCheck(received != NULL);
466

    
467
      // wait for the S signal to become active and activate it as well and proceed on success
468
      if (_waitForS(listener, flags, APAL_GPIO_ON, received) == AOS_SUCCESS) {
469
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
470
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
471
      }
472
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
473
      break;
474
    }
475

    
476
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
477

    
478
    /*
479
     * Deactivate S signal.
480
     * Master node delays execution by one period AOS_SSSP_DELAY
481
     */
482
    case AOS_SSSP_STAGE_STARTUP_2_1:
483
    {
484
#if (AMIROOS_CFG_SSSP_MASTER == true)
485
      aosDbgCheck(listener == NULL);
486
      aosDbgCheck(flags == 0);
487
      aosDbgCheck(mask != 0);
488
      aosDbgCheck(received != NULL);
489

    
490
      // delay execution and deactivate the S signal on success
491
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_2_1), received) == AOS_SUCCESS) {
492
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
493
        nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
494
      }
495
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
496
      aosDbgCheck(listener == NULL);
497
      aosDbgCheck(flags == 0);
498
      aosDbgCheck(mask == 0);
499
      aosDbgCheck(received == NULL);
500

    
501
      // deactivate S signal and proceed
502
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
503
      nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
504
      mask = 0;
505
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
506
      break;
507
    }
508

    
509
    /*
510
     * Wait for the S signal to become inactive.
511
     * When proceeding to operation phase, the master starts toggling S for synchronization
512
     */
513
    case AOS_SSSP_STAGE_STARTUP_2_2:
514
    {
515
      aosDbgCheck(listener != NULL);
516
      aosDbgCheck(flags != 0);
517
      aosDbgCheck(mask == listener->events);
518
      aosDbgCheck(received != NULL);
519

    
520
      // wait for the S signal to become inactive and proceed on success
521
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
522
#if (AMIROOS_CFG_SSSP_MSI == true)
523
        nextstage = AOS_SSSP_STAGE_STARTUP_3;
524
#else /* (AMIROOS_CFG_SSSP_MSI == true) */
525
        chSysLock();
526
        // start the internal uptime aggregation
527
        aosSysStartUptimeS();
528
#if (AMIROOS_CFG_SSSP_MASTER == true)
529
        // start toggling S for system synchronization
530
        _syncTimerStartS();
531
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
532
        chSysUnlock();
533
        nextstage = AOS_SSSP_STAGE_OPERATION;
534
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
535
      }
536
      break;
537
    }
538

    
539
#if (AMIROOS_CFG_SSSP_MSI == true)
540

    
541
      //TODO
542

    
543
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
544

    
545
    /*
546
     * Invalid operation.
547
     * Shutdon must be initiatid via the aosSsspShutdownInit() function.
548
     */
549
    case AOS_SSSP_STAGE_OPERATION:
550
    {
551
      aosDbgAssertMsg(false, "in order to exit operation phase, aosSsspShutdownInit() function must be used");
552
      break;
553
    }
554

    
555
    /*
556
     * Delay execution by one perion AOS_SSSP_DELAY, deactivate the SYNC signal and proceed.
557
     * NOTE: Actually only the module that initiated the shutdown has to delay execution here.
558
     *       For the sake of simlicity though, each module delays execution.
559
     */
560
    case AOS_SSSP_STAGE_SHUTDOWN_1_2:
561
    {
562
      aosDbgCheck(listener == NULL);
563
      aosDbgCheck(flags == 0);
564
      aosDbgCheck(mask == 0);
565
      aosDbgCheck(received == NULL);
566

    
567
      // delay execution
568
      aosThdUSleep(AOS_SSSP_DELAY);
569
      // deactivate S signal
570
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
571
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_1_3;
572
      break;
573
    }
574

    
575
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true)
576

    
577
    /*
578
     * Wait for S signal to become inactive.
579
     */
580
    case AOS_SSSP_STAGE_SHUTDOWN_1_3:
581
    {
582
      aosDbgCheck(listener != NULL);
583
      aosDbgCheck(flags != 0);
584
      aosDbgCheck(mask == listener->events);
585
      aosDbgCheck(received != NULL);
586

    
587
      // wait for the S signal to become inactive and proceed on success
588
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
589
        nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_1;
590
      }
591
      break;
592
    }
593

    
594
    /*
595
     * Evaluate PD signal to determine whether a shutdown or restart is requested.
596
     * The logical state is returned by the function call.
597
     */
598
    case AOS_SSSP_STAGE_SHUTDOWN_2_1:
599
    {
600
      aosDbgCheck(listener == NULL);
601
      aosDbgCheck(flags == 0);
602
      aosDbgCheck(mask == 0);
603
      aosDbgCheck(received != NULL);
604

    
605
      // evaluate PD signal
606
      apalControlGpioState_t pd;
607
      apalControlGpioGet(&moduleSsspGpioPD, &pd);
608
      *received = (pd == APAL_GPIO_ON) ? (eventmask_t)~0 : 0;
609
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_2;
610
      break;
611
    }
612

    
613
    /*
614
     * Proceed with no further actions required.
615
     */
616
    case AOS_SSSP_STAGE_SHUTDOWN_2_2:
617
    {
618
      aosDbgCheck(listener == NULL);
619
      aosDbgCheck(flags == 0);
620
      aosDbgCheck(mask == 0);
621
      aosDbgCheck(received == NULL);
622

    
623
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_3;
624
      break;
625
    }
626

    
627
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
628

    
629
    default:
630
    {
631
      // this case must never occur!
632
      aosDbgAssertMsg(false, "Invalid SSSP stage tranition occurred");
633
      break;
634
    }
635
  } /* end of switch (aos.sssp.stage) */
636

    
637
  // outro and return
638
  laststage = aos.sssp.stage;
639
  const aos_status_t status = (nextstage > aos.sssp.stage) ? AOS_SUCCESS : AOS_FAILURE;
640
  aos.sssp.stage = nextstage;
641

    
642
  return status;
643
}
644

    
645
/**
646
 * @brief   Leave the operation pahse and initiate or acknowledge shutdown.
647
 *
648
 * @param[in] active  Flag, indicating whether the shutdon shall be activeliy initiated (true) or a passive request was received (false).
649
 */
650
void aosSsspShutdownInit(bool active)
651
{
652
  aosDbgAssert(aos.sssp.stage == AOS_SSSP_STAGE_OPERATION);
653

    
654
  // proceed to shutdown phase
655
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_1;
656

    
657
  // activate PD if this was an active shutdown initiation
658
  if (active) {
659
    apalControlGpioSet(&moduleSsspGpioPD, APAL_GPIO_ON);
660
  }
661

    
662
#if (AMIROOS_CFG_SSSP_MASTER == true)
663
  // stop toggling S
664
  chVTReset(&_synctimer);
665
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
666

    
667
  // activate S
668
  apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
669

    
670
  // proceed in the shutdown phase
671
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_2;
672

    
673
  return;
674
}
675

    
676
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true) || defined(__DOXYGEN__)
677

    
678
/**
679
 * @brief   Sequnetially broadcast an identifier via S to specify a specific way to shutdown or restart.
680
 *
681
 * @param[in] identifier  Identifier to be broadcasted.
682
 */
683
void aosSsspShutdownBroadcastIdentifier(unsigned int identifier)
684
{
685
  // only broadcast anything if a identifier greater than 0 was specified
686
  if (identifier > 0) {
687
    // broadcast identifier
688
    for (unsigned int pulse = 0; pulse < identifier; ++pulse) {
689
      // wait one delay time with S being deactivated
690
      aosThdUSleep(AOS_SSSP_DELAY);
691

    
692
      // activate S for one AOS_SSSP_DELAY.
693
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
694
      aosThdUSleep(AOS_SSSP_DELAY);
695
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
696
    }
697
  }
698

    
699
  // let a timeout pass and return
700
  aosThdUSleep(AOS_SSSP_TIMEOUT);
701

    
702
  return;
703
}
704

    
705
/**
706
 * @brief   Wait for a pulse during the SSSP shutdown disambiguation procedure.
707
 * @details Whenever a pulse is received, the identifier is incremented.
708
 *          Otherwise the internal timer is configured and will eventually trigger a timeout event.
709
 *
710
 * @param[in]     gpiolistener  Event listener for GPIO events.
711
 * @param[in]     sflags        Event flags to be set by edges on the S signal.
712
 * @param[in]     timermask     Event maks to use for the internal timer.
713
 * @param[in,out] identifier    Identifier variable to increment.
714
 *
715
 * @return  The event mask of whatever event was received during this function call.
716
 */
717
eventmask_t aosSsspShutdownWaitForIdentifierPulse(event_listener_t* gpiolistener, const eventflags_t sflags, eventmask_t timermask, unsigned int* identifier)
718
{
719
  aosDbgCheck(gpiolistener != NULL);
720
  aosDbgCheck(sflags != 0);
721
  aosDbgCheck(timermask != 0);
722
  aosDbgCheck(identifier != NULL);
723

    
724
  // local variables
725
  eventmask_t mask;
726
  eventflags_t flags;
727
  apalControlGpioState_t sstate;
728

    
729
  // arm the timer once
730
  if (!chVTIsArmed(&_delayTimer)) {
731
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, timermask);
732
    chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
733
  }
734

    
735
  // wait for any event to occur (do not apply any filters in order not to miss any events)
736
  mask = chEvtWaitOne(ALL_EVENTS);
737

    
738
  // GPIO event
739
  if (mask & gpiolistener->events) {
740
    // retreive flags without clearing them
741
    flags = gpiolistener->flags;
742
    apalControlGpioGet(&moduleSsspGpioS, &sstate);
743
    if (flags == sflags) {
744
      apalControlGpioGet(&moduleSsspGpioS, &sstate);
745
      // if this was the end of a pulse
746
      if (sstate == APAL_GPIO_OFF) {
747
        // restart the timer
748
        chVTReset(&_delayTimer);
749
        chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
750
        // increment the identifier
751
        ++(*identifier);
752
      }
753
    }
754
  }
755
  // timer event
756
  else if (mask & _eventListenerDelay.events) {
757
    // unregister event
758
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
759
  }
760
  // any further events must be handled externally
761

    
762
  return mask;
763
}
764

    
765
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
766

    
767
#if ((AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true)) || defined(__DOXYGEN__)
768

    
769
/**
770
 * @brief   Retreive the offset between local clock and system synchronization signal.
771
 *
772
 * @return
773
 */
774
float aosSsspGetSyncSkew(void)
775
{
776
  return _syncskew;
777
}
778

    
779
#endif /* (AMIROOS_CFG_SSSP_MASTER != true) && (AMIROOS_CFG_PROFILE == true) */
780

    
781
#endif /* (AMIROOS_CFG_SSSP_ENABLE == true) */
782

    
783
/** @} */