BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
connection_event_callback.hpp
1#ifndef BLUETOE_LINK_LAYER_CONNECTION_EVENT_CALLBACK_HPP
2#define BLUETOE_LINK_LAYER_CONNECTION_EVENT_CALLBACK_HPP
3
4#include <bluetoe/ll_meta_types.hpp>
5#include <bluetoe/delta_time.hpp>
6
7namespace bluetoe {
8namespace link_layer {
9
10 namespace details {
11 struct connection_event_callback_meta_type : details::valid_link_layer_option_meta_type {};
12 struct synchronized_connection_event_callback_meta_type : details::valid_link_layer_option_meta_type {};
13 struct check_synchronized_connection_event_callback_meta_type : details::valid_link_layer_option_meta_type {};
14
15 struct default_connection_event_callback
16 {
17 static void call_connection_event_callback( const delta_time& )
18 {
19 }
20
21 typedef connection_event_callback_meta_type meta_type;
22 };
23 }
24
40 template < typename T, T& Obj, unsigned RequiredTimeMS = 0 >
42 {
44 static void call_connection_event_callback( const delta_time& time_till_next_event )
45 {
46 if ( RequiredTimeMS == 0 || time_till_next_event >= delta_time::msec( RequiredTimeMS ) )
47 Obj.ll_connection_event_happend();
48 }
49
50 typedef details::connection_event_callback_meta_type meta_type;
52 };
53
149 template < typename T, T& Obj, unsigned MaximumPeriodUS, int PhaseShiftUS, unsigned MaximumExecutionTimeUS = 100 >
151 {
156
163
169
171 template < typename LinkLayer >
172 struct impl {
173 impl()
174 : stopped_( false )
175 , force_( false )
176 {
177 static_assert( LinkLayer::hardware_supports_synchronized_user_timer, "choosen binding does not support the use of user timer!" );
178 }
179
180 void stop_synchronized_connection_event_callbacks()
181 {
182 stopped_ = true;
183 force_ = false;
184
185 link_layer().cancel_synchronized_user_timer();
186 }
187
188 void restart_synchronized_connection_event_callbacks()
189 {
190 instance_ = 0;
191 latency_ = 0;
192 num_intervals_since_anchor_moved_ = 0;
193
194 stopped_ = false;
195 link_layer().restart_user_timer();
196 }
197
198 void force_synchronized_connection_event_callback()
199 {
200 force_ = true;
201 }
202
203 void synchronized_connection_event_callback_new_connection( delta_time connection_interval )
204 {
205 if ( stopped_ )
206 return;
207
208 connection_value_ = typename T::connection();
209 instance_ = PhaseShiftUS > 0 ? 1 : 0;
210 latency_ = 0;
211
212 calculate_effective_period( connection_interval );
213 call_ll_connect< T >( connection_interval, connection_value_ );
214 link_layer().cancel_synchronized_user_timer();
215 setup_timer( first_timeout() );
216 }
217
218 void synchronized_connection_event_callback_start_changing_connection()
219 {
220 link_layer().cancel_synchronized_user_timer();
221 }
222
223 void synchronized_connection_event_callback_connection_changed( delta_time connection_interval )
224 {
225 if ( stopped_ )
226 return;
227
228 instance_ = PhaseShiftUS > 0 ? 1 : 0;
229 latency_ = 0;
230
231 calculate_effective_period( connection_interval );
232 call_ll_update< T >( connection_interval );
233 setup_timer( first_timeout() );
234 }
235
236 void synchronized_connection_event_callback_disconnect()
237 {
238 call_ll_disconnect< T >( 0 );
239 link_layer().cancel_synchronized_user_timer();
240 }
241
242 void synchronized_connection_event_callback_timeout( bool anchor_moved )
243 {
244 if ( stopped_ )
245 return;
246
247 if ( force_ )
248 {
249 force_ = false;
250 latency_ = 0;
251 }
252
253 latency_ = latency_ == 0
254 ? Obj.ll_synchronized_callback( instance_, connection_value_ )
255 : latency_ - 1;
256
257 if ( num_calls_ )
258 instance_ = ( instance_ + 1 ) % num_calls_;
259
260 // if the PhaseShiftUS is negative, the first callback is already first_timeout()
261 // away from the anchor
262 static constexpr unsigned first_interval_after_anchor = ( PhaseShiftUS < 0 )
263 ? 1
264 : 0;
265
266 num_intervals_since_anchor_moved_ = anchor_moved
267 ? first_interval_after_anchor
268 : num_intervals_since_anchor_moved_ + 1;
269
270 setup_timer( first_timeout() + num_intervals_since_anchor_moved_ * effective_period_ );
271 }
272
273 private:
274 template < class TT >
275 auto call_ll_connect( bluetoe::link_layer::delta_time connection_interval, typename TT::connection& con )
276 -> decltype(&TT::ll_synchronized_callback_connect)
277 {
278 con = Obj.ll_synchronized_callback_connect( connection_interval, num_calls_ );
279
280 return 0;
281 }
282
283 template < class TT >
284 void call_ll_connect( ... )
285 {
286 }
287
288 template < class TT >
289 auto call_ll_disconnect( int ) -> decltype(&TT::ll_synchronized_callback_disconnect)
290 {
291 Obj.ll_synchronized_callback_disconnect( connection_value_ );
292 return 0;
293 }
294
295 template < class TT >
296 void call_ll_disconnect( ... )
297 {
298 }
299
300 template < class TT >
301 auto call_ll_update(
302 bluetoe::link_layer::delta_time connection_interval ) -> decltype(&TT::ll_synchronized_callback_period_update)
303 {
304 Obj.ll_synchronized_callback_period_update( connection_interval, num_calls_, connection_value_ );
305
306 return 0;
307 }
308
309 template < class TT >
310 void call_ll_update( ... )
311 {
312 }
313
314 void setup_timer( delta_time dt )
315 {
316 const bool setup = link_layer().schedule_synchronized_user_timer( dt, delta_time( MaximumExecutionTimeUS ) );
317 static_cast< void >( setup );
318 assert( setup );
319 }
320
321 LinkLayer& link_layer()
322 {
323 return *static_cast< LinkLayer* >( this );
324 }
325
326 void calculate_effective_period( delta_time connection_interval )
327 {
328 const auto interval_us = connection_interval.usec();
329
330 if ( interval_us > MaximumPeriodUS )
331 {
332 num_calls_ = ( interval_us + MaximumPeriodUS - 1 ) / MaximumPeriodUS;
333 num_intervals_ = 0;
334 effective_period_ = delta_time( interval_us / num_calls_ );
335 }
336 else
337 {
338 num_calls_ = 0;
339 num_intervals_ = MaximumPeriodUS / interval_us;
340 effective_period_ = delta_time( num_intervals_ * interval_us );
341 }
342
343 num_intervals_since_anchor_moved_ = 0;
344
345 assert( effective_period_.usec() <= MaximumPeriodUS );
346 }
347
348 delta_time first_timeout() const
349 {
350 return PhaseShiftUS < 0
351 ? effective_period_ - delta_time::usec( -PhaseShiftUS )
352 : effective_period_ + delta_time::usec( PhaseShiftUS );
353 }
354
355 delta_time effective_period_;
356
357 unsigned instance_;
358 unsigned latency_;
359
360 // > 1 if there are more than 1 call to the CB at each interval
361 unsigned num_calls_;
362
363 // > 1 if there are more that 1 interval between two CB calls
364 unsigned num_intervals_;
365
366 unsigned num_intervals_since_anchor_moved_;
367
368 typename T::connection connection_value_;
369
370 volatile bool stopped_;
371 volatile bool force_;
372 };
373
374 typedef details::synchronized_connection_event_callback_meta_type meta_type;
376 };
377
384 {
386 template < typename LinkLayer >
387 struct impl {
388 void synchronized_connection_event_callback_new_connection( delta_time )
389 {
390 }
391
392 void synchronized_connection_event_callback_start_changing_connection()
393 {
394 }
395
396 void synchronized_connection_event_callback_connection_changed( delta_time )
397 {
398 }
399
400 void synchronized_connection_event_callback_timeout( bool )
401 {
402 }
403
404 void synchronized_connection_event_callback_disconnect()
405 {
406 }
407 };
408
409 typedef details::synchronized_connection_event_callback_meta_type meta_type;
411 };
412
426 template < unsigned ExpectedMinimumInterval_US >
428 {
430 template < typename Radio >
431 static void check( const no_synchronized_connection_event_callback& )
432 {
433 }
434
435 template < typename Radio, typename T, T& Obj, unsigned MaximumPeriodUS, int PhaseShiftUS, unsigned MaximumExecutionTimeUS >
437 {
438 static_assert( MaximumPeriodUS <= ExpectedMinimumInterval_US,
439 "To guaranty the timely execution of the callback, the MaximumPeriodUS must be small or equal to the minimum connection interval!" );
440
441 static_assert( PhaseShiftUS < 0,
442 "In order to make sure, that the callback is finished when the connection event starts, the callback must start prior to the connection event!" );
443
444 static_assert( -PhaseShiftUS >= Radio::connection_event_setup_time_us,
445 "PhaseShiftUS must start before the radios setup time." );
446
447 static_assert( -PhaseShiftUS >= Radio::connection_event_setup_time_us + MaximumExecutionTimeUS,
448 "Callback execution must end before setup time of the configured radio." );
449
450 static_assert( MaximumPeriodUS / 2 >= MaximumExecutionTimeUS,
451 "In worst case, the effective period of the callback invocation is half the requested period and thus must still be large enough for the expected runtimer of the callback." );
452
453 static_assert( -PhaseShiftUS < MaximumPeriodUS / 2,
454 "In worst case, the effective period of the callback invocation is half the requested period and thus must still be small than the phase shift." );
455 }
456
457 using meta_type = details::check_synchronized_connection_event_callback_meta_type;
459 };
460
463
470 {
472 template < typename Radio, class CBs >
473 static void check( const CBs& )
474 {
475 }
476
477 using meta_type = details::check_synchronized_connection_event_callback_meta_type;
479 };
480
481}
482}
483#endif