BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
peripheral_latency.hpp
Go to the documentation of this file.
1#ifndef BLUETOE_LINK_LAYER_PERIPHERAL_LATENCY_HPP
2#define BLUETOE_LINK_LAYER_PERIPHERAL_LATENCY_HPP
3
4#include <bluetoe/ll_meta_types.hpp>
5#include <bluetoe/connection_events.hpp>
6
20namespace bluetoe {
21namespace link_layer {
22
23 namespace details {
24 struct peripheral_latency_meta_type {};
25 }
26
30 static constexpr auto maximum_link_layer_peripheral_latency = 499;
31
46 {
57
69
75
81
93
99
100 };
101
102 namespace details {
103 template < peripheral_latency >
104 struct wrap_latency_option {};
105
106 template < peripheral_latency RequestedFeature, peripheral_latency ... Options >
107 struct feature_enabled
108 {
109 using type = typename bluetoe::details::select_type<
110 bluetoe::details::has_option< wrap_latency_option< RequestedFeature >, wrap_latency_option< Options >... >::value,
111 std::true_type,
112 std::false_type >::type;
113
114 static constexpr bool value = type::value;
115 };
116 }
117
136 template < peripheral_latency ... Options >
138 {
140 struct meta_type :
141 details::peripheral_latency_meta_type,
142 details::valid_link_layer_option_meta_type {};
144 };
145
172 template < typename ... Configurations >
174 {
175 public:
182 template < typename NewConfig >
184
186 struct meta_type :
187 details::peripheral_latency_meta_type,
188 details::valid_link_layer_option_meta_type {};
190 };
191
203 peripheral_latency::listen_always
204 >;
205
224 peripheral_latency::listen_if_pending_transmit_data,
225 peripheral_latency::listen_if_last_received_had_more_data
226 >;
227
244 peripheral_latency::listen_if_last_received_not_empty,
245 peripheral_latency::listen_if_last_received_had_more_data
246 >;
247
260 peripheral_latency::listen_if_pending_transmit_data,
261 peripheral_latency::listen_if_unacknowledged_data,
262 peripheral_latency::listen_if_last_received_not_empty,
263 peripheral_latency::listen_if_last_transmitted_not_empty,
264 peripheral_latency::listen_if_last_received_had_more_data
265 >;
266
267 namespace details {
268
277 template < class Base >
278 class connection_state_base
279 {
280 public:
284 unsigned current_channel_index() const
285 {
286 return channel_index_;
287 }
288
292 std::uint16_t connection_event_counter() const
293 {
294 return event_counter_;
295 }
296
304 delta_time time_since_last_event() const
305 {
306 return time_since_last_event_;
307 }
308
312 void plan_next_connection_event_after_timeout(
313 delta_time connection_interval )
314 {
315 channel_index_ = ( channel_index_ + 1 ) % channel_map::max_number_of_data_channels;
316 ++event_counter_;
317 time_since_last_event_ += connection_interval;
318 }
319
323 void plan_next_connection_event(
324 std::uint16_t connection_peripheral_latency,
325 connection_event_events last_event_events,
326 delta_time connection_interval,
327 std::pair< bool, std::uint16_t > pending_instance )
328 {
329 if ( ( base().template peripheral_latency_feature< peripheral_latency::listen_if_unacknowledged_data >() and last_event_events.unacknowledged_data )
330 or ( base().template peripheral_latency_feature< peripheral_latency::listen_if_last_received_not_empty >() and last_event_events.last_received_not_empty )
331 or ( base().template peripheral_latency_feature< peripheral_latency::listen_if_last_transmitted_not_empty >() and last_event_events.last_transmitted_not_empty )
332 or ( base().template peripheral_latency_feature< peripheral_latency::listen_if_last_received_had_more_data >() and last_event_events.last_received_had_more_data )
333 or ( base().template peripheral_latency_feature< peripheral_latency::listen_if_pending_transmit_data >() and last_event_events.pending_outgoing_data )
334 or ( base().template peripheral_latency_feature< peripheral_latency::listen_always >() )
335 or ( last_event_events.error_occured )
336 )
337 {
338 connection_peripheral_latency = 0;
339 }
340
341 ++connection_peripheral_latency;
342
343 if ( pending_instance.first )
344 {
345 const std::uint16_t instance_distance = event_counter_ < pending_instance.second
346 ? pending_instance.second - event_counter_
347 : pending_instance.second + ~event_counter_ + 1;
348
349 if ( instance_distance > 0 )
350 connection_peripheral_latency = std::min( connection_peripheral_latency, instance_distance );
351 }
352
353 channel_index_ = ( channel_index_ + connection_peripheral_latency ) % channel_map::max_number_of_data_channels;
354 event_counter_ += connection_peripheral_latency;
355 time_since_last_event_ = connection_peripheral_latency * connection_interval;
356
357 base().disarmable_connection_state_last_latency( connection_peripheral_latency );
358 }
359
371 template < class Radio >
372 bool reschedule_on_pending_data( Radio&, delta_time )
373 {
374 return false;
375 }
376
380 void reset_connection_state()
381 {
382 channel_index_ = 0;
383 event_counter_ = 0;
384 time_since_last_event_ = delta_time();
385 base().disarmable_connection_state_last_latency( 1 );
386 }
387
388 void peripheral_latency_move_connection_event( int count, delta_time connection_iterval )
389 {
390 assert( count <= 0 );
391 assert( -count <= maximum_link_layer_peripheral_latency );
392
393 // to have the left side of the modulo beeing always positiv, the largest necessary multiple of
394 // channel_map::max_number_of_data_channels is added. count is bound by the maximum peripheral
395 // latency.
396 static constexpr unsigned offset = ( maximum_link_layer_peripheral_latency / channel_map::max_number_of_data_channels + 1 ) * channel_map::max_number_of_data_channels;
397
398 channel_index_ = ( channel_index_ + count + offset ) % channel_map::max_number_of_data_channels;
399 event_counter_ += count;
400
401 time_since_last_event_ -= -count * connection_iterval;
402 }
403
404 private:
405 unsigned channel_index_;
406 std::uint16_t event_counter_;
407 delta_time time_since_last_event_;
408
409 Base& base()
410 {
411 return *static_cast< Base* >( this );
412 }
413 };
414
415 template < typename DisarmSupport, typename State >
416 class disarmable_connection_state;
417
418 template < typename State >
419 class disarmable_connection_state< std::true_type, State >
420 {
421 public:
422 void disarmable_connection_state_last_latency( int l )
423 {
424 assert( l > 0 );
425 last_latency_ = l;
426 }
427
428 protected:
429 template < class Radio >
430 bool reschedule_on_pending_data_impl( Radio& radio, delta_time connection_iterval )
431 {
432 // Do not try to reschedule an event, that can not be rescheduled or was already
433 // rescheduled.
434 if ( last_latency_ == 1 )
435 return false;
436
437 const std::pair< bool, bluetoe::link_layer::delta_time > rc = radio.disarm_connection_event();
438
439 if ( rc.first )
440 {
441 assert( !connection_iterval.zero() );
442
443 const unsigned times = std::max( 1u, ( rc.second + connection_iterval - delta_time( 1 ) ) / connection_iterval );
444
445 // only move the connection event further into direction of the current time
446 const int moved = std::min< int >( times, last_latency_ );
447 static_cast< State* >( this )->peripheral_latency_move_connection_event( moved - last_latency_, connection_iterval );
448
449 last_latency_ = 1;
450 }
451
452 return rc.first;
453 }
454
455 private:
456 int last_latency_;
457 };
458
459 template < typename State >
460 class disarmable_connection_state< std::false_type, State >
461 {
462 public:
463 void disarmable_connection_state_last_latency( int ) {}
464
465 protected:
466 template < class Radio >
467 bool reschedule_on_pending_data_impl( Radio&, delta_time )
468 {
469 return false;
470 }
471 };
472
476 template < typename Options >
477 class peripheral_latency_state;
478
479 template < peripheral_latency ... Options >
480 class peripheral_latency_state< peripheral_latency_configuration< Options... > >
481 : public connection_state_base< peripheral_latency_state< peripheral_latency_configuration< Options... > > >
482 , public disarmable_connection_state<
483 typename feature_enabled< peripheral_latency::listen_if_pending_transmit_data, Options... >::type,
484 peripheral_latency_state< peripheral_latency_configuration< Options... > > >
485 {
486 public:
487 static constexpr bool listen_always_given = details::feature_enabled< peripheral_latency::listen_always, Options... >::value;
488 static constexpr bool other_parameters_given = sizeof...( Options ) > 1;
489
490 static_assert( !( listen_always_given and other_parameters_given ),
491 "if `listen_always` is given, there is no point in adding addition options" );
492
504 template < class Radio >
505 bool reschedule_on_pending_data( Radio& radio, delta_time connection_iterval )
506 {
507 return this->reschedule_on_pending_data_impl( radio, connection_iterval );
508 }
509
510 template < peripheral_latency RequestedFeature >
511 constexpr bool peripheral_latency_feature() const
512 {
513 return feature_enabled< RequestedFeature, Options... >::type::value;
514 }
515
516 };
517
518 template < typename ... Configurations >
519 struct listen_if_pending_transmit_data_is_part_of_any_configuration
520 {
521 using type = std::true_type;
522 };
523
524 template < typename ... Configurations >
525 class peripheral_latency_state< peripheral_latency_configuration_set< Configurations... > >
526 : public connection_state_base< peripheral_latency_state< peripheral_latency_configuration_set< Configurations... > > >
527 , public disarmable_connection_state<
528 typename listen_if_pending_transmit_data_is_part_of_any_configuration< Configurations... >::type,
529 peripheral_latency_state< peripheral_latency_configuration_set< Configurations... > > >
530 {
531 public:
532 static_assert( sizeof...(Configurations) > 0, "At least on configuration is required!" );
533
537 peripheral_latency_state()
538 : current_configuration_( 0 )
539 {
540 }
541
542 template < class Radio >
543 bool reschedule_on_pending_data( Radio& radio, delta_time connection_iterval )
544 {
545 return this->reschedule_on_pending_data_impl( radio, connection_iterval );
546 }
547
548 template < typename NewConfig >
549 void change_peripheral_latency()
550 {
551 using new_index = bluetoe::details::index_of< NewConfig, Configurations... >;
552 static_assert( new_index::value < sizeof...( Configurations ), "given NewConfig is not member of Configurations..." );
553
554 current_configuration_ = new_index::value;
555 }
556
557 template < peripheral_latency RequestedFeature >
558 bool peripheral_latency_feature() const;
559
560 private:
561 template < peripheral_latency RequestedFeature >
562 bool runtime_feature() const;
563
564 int current_configuration_;
565 };
566
567 template < peripheral_latency RequestedFeature, typename Configuration >
568 struct has_feature_enabled_impl;
569
570 template < peripheral_latency RequestedFeature, peripheral_latency ...Options >
571 struct has_feature_enabled_impl< RequestedFeature, peripheral_latency_configuration< Options... > >
572 {
573 static constexpr bool value = feature_enabled< RequestedFeature, Options... >::value;
574 };
575
576
577 template < peripheral_latency RequestedFeature, typename ... Configurations >
578 struct is_part_of_all_configurations
579 {
580 template < typename Configuration >
581 using has_feature_enabled = has_feature_enabled_impl< RequestedFeature, Configuration >;
582
583 static constexpr bool value =
584 bluetoe::details::count_if<
585 std::tuple< Configurations... >,
586 has_feature_enabled
587 >::value == sizeof...( Configurations );
588 };
589
590 template < peripheral_latency RequestedFeature, typename ... Configurations >
591 struct is_part_of_no_configurations
592 {
593 template < typename Configuration >
594 using has_feature_enabled = has_feature_enabled_impl< RequestedFeature, Configuration >;
595
596 static constexpr bool value =
597 bluetoe::details::count_if<
598 std::tuple< Configurations... >,
599 has_feature_enabled
600 >::value == 0;
601 };
602
603 template < typename ... Configurations >
604 template < peripheral_latency RequestedFeature >
605 bool peripheral_latency_state< peripheral_latency_configuration_set< Configurations... > >::peripheral_latency_feature() const
606 {
607 // if the feature is set in all or none of the Configurations, the test can be done at compile time
608 // and the compiler has a chance to detect dead code
609 if ( is_part_of_all_configurations< RequestedFeature, Configurations... >::value )
610 return true;
611
612 if ( is_part_of_no_configurations< RequestedFeature, Configurations... >::value )
613 return false;
614
615 return runtime_feature< RequestedFeature >();
616 }
617
618 template < peripheral_latency RequestedFeature >
619 struct peripheral_latency_feature_checker
620 {
621 inline explicit peripheral_latency_feature_checker( int config )
622 : index( config )
623 , result( false )
624 {
625 }
626
627 template< typename Config >
628 void each()
629 {
630 if ( index == 0 && has_feature_enabled_impl< RequestedFeature, Config >::value )
631 result = true;
632
633 --index;
634 }
635
636 int index;
637 bool result;
638 };
639
640 template < typename ... Configurations >
641 template < peripheral_latency RequestedFeature >
642 bool peripheral_latency_state< peripheral_latency_configuration_set< Configurations... > >::runtime_feature() const
643 {
644 peripheral_latency_feature_checker< RequestedFeature > check_feature( current_configuration_ );
645
646 bluetoe::details::for_< Configurations... >::
647 template each< peripheral_latency_feature_checker< RequestedFeature >& >( check_feature );
648
649 return check_feature.result;
650 }
651 }
652}
653}
654
655#endif
wrap< typename select_type_impl< Select >::template f< A, B > > select_type
Select A or B by Select. If Select == true, the result is A; B otherwise.
Definition: meta_tools.hpp:38
peripheral_latency
detailed options for the peripheral latency behavior
Definition: peripheral_latency.hpp:46
@ listen_if_unacknowledged_data
listen if the link layer contains unacknowledged data.
@ listen_if_last_received_not_empty
listen to the next connection event, if the last received PDU was not empty.
@ listen_if_last_transmitted_not_empty
listen to the next connection event, if the last transmitted PDU was not empty.
@ listen_always
listen at all connection events, despite the negotiated peripheral latency value of the current conne...
@ listen_if_last_received_had_more_data
listen to the next connection event, if the last received PDU had the more data (MD) flag set.
@ listen_if_pending_transmit_data
listen at the very next connection event if bluetoe has an available PDU for sending.