BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
server.hpp
1#ifndef BLUETOE_SERVER_HPP
2#define BLUETOE_SERVER_HPP
3
4#include <bluetoe/codes.hpp>
5#include <bluetoe/service.hpp>
6#include <bluetoe/bits.hpp>
7#include <bluetoe/filter.hpp>
8#include <bluetoe/gatt_options.hpp>
9#include <bluetoe/server_name.hpp>
10#include <bluetoe/adv_service_list.hpp>
11#include <bluetoe/peripheral_connection_interval_range.hpp>
12#include <bluetoe/server_meta_type.hpp>
13#include <bluetoe/client_characteristic_configuration.hpp>
14#include <bluetoe/write_queue.hpp>
15#include <bluetoe/gap_service.hpp>
16#include <bluetoe/appearance.hpp>
17#include <bluetoe/mixin.hpp>
18#include <bluetoe/find_notification_data.hpp>
19#include <bluetoe/outgoing_priority.hpp>
20#include <bluetoe/link_state.hpp>
21#include <bluetoe/attribute_handle.hpp>
22#include <bluetoe/l2cap_channels.hpp>
23#include <bluetoe/notification_queue.hpp>
24#include <bluetoe/custom_advertising.hpp>
25
26#include <cstdint>
27#include <cstddef>
28#include <algorithm>
29#include <iterator>
30#include <cassert>
31
32namespace bluetoe {
33 namespace details {
34
35 template < typename ... Options >
36 using selected_advertising_data_source =
37 typename details::find_by_meta_type<
38 details::advertising_data_meta_type,
39 Options...,
40 auto_advertising_data
41 >::type;
42
43 template < typename ... Options >
44 using selected_scan_response_data_source =
45 typename details::find_by_meta_type<
46 details::scan_response_data_meta_type,
47 Options...,
48 auto_scan_response_data
49 >::type;
50 }
51
79 template < typename ... Options >
80 class server
81 : private details::write_queue< typename details::find_by_meta_type< details::write_queue_meta_type, Options... >::type >
82 , public details::derive_from< typename details::collect_mixins< Options... >::type >
83 , public details::selected_advertising_data_source< Options ... >
84 , public details::selected_scan_response_data_source< Options ... >
85 {
86 public:
88 using services_without_gap = typename details::find_all_by_meta_type< details::service_meta_type, Options... >::type;
89 using selected_advertising_data_t = details::selected_advertising_data_source< Options ... >;
90 using selected_scan_response_data_t = details::selected_scan_response_data_source< Options ... >;
91
92 // append gap serivce for gatt servers
93 using gap_service_definition = typename details::find_by_meta_type< details::gap_service_definition_meta_type,
94 Options..., gap_service_for_gatt_servers >::type;
95 using services = typename gap_service_definition::template add_service< services_without_gap, Options... >::type;
96
97 static constexpr std::size_t number_of_client_configs = details::sum_by< services, details::sum_by_client_configs >::value;
98
99 using write_queue_type = typename details::find_by_meta_type< details::write_queue_meta_type, Options... >::type;
100
101 using notification_priority = typename details::find_by_meta_type< details::outgoing_priority_meta_type, Options..., higher_outgoing_priority<> >::type;
102
103 static_assert( std::tuple_size< typename details::find_all_by_meta_type< details::outgoing_priority_meta_type, Options... >::type >::value <= 1,
104 "Only one of bluetoe::higher_outgoing_priority<> or bluetoe::lower_outgoing_priority<> per server allowed!" );
105
106 using cccd_indices = typename details::find_notification_data_in_list< notification_priority, services >::cccd_indices;
107
108 using server_t = server< Options... >;
109 using handle_mapping = details::handle_index_mapping< server_t >;
110
121 : public details::client_characteristic_configurations< number_of_client_configs >
122 {
123 public:
125 : client_mtu_( details::default_att_mtu_size )
126 {
127 }
128
132 std::uint16_t negotiated_mtu() const
133 {
134 return std::min( server_mtu(), client_mtu_ );
135 }
136
144 void client_mtu( std::uint16_t mtu )
145 {
146 assert( mtu >= details::default_att_mtu_size );
147 client_mtu_ = mtu;
148 }
149
155 std::uint16_t client_mtu() const
156 {
157 return client_mtu_;
158 }
159
164 std::uint16_t server_mtu() const
165 {
166 return maximum_channel_mtu_size;
167 }
168
169
170 private:
171 std::uint16_t client_mtu_;
172 };
173
178
213 template < class T >
214 bool notify( const T& value );
215
249 template < class CharacteristicUUID >
250 bool notify();
251
261 template < class T >
262 bool indicate( const T& value );
263
273 template < class CharacteristicUUID >
274 bool indicate();
275
279 template < class CharacteristicUUID >
280 bool configured_for_indications( const details::client_characteristic_configuration& ) const;
281
285 template < class CharacteristicUUID >
286 bool configured_for_notifications( const details::client_characteristic_configuration& ) const;
287
291 template < class CharacteristicUUID >
292 bool configured_for_notifications_or_indications( const details::client_characteristic_configuration& ) const;
293
295 // function relevant only for l2cap layers
299 template < typename ConnectionData >
300 void l2cap_input( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
301
302 template < typename ConnectionData >
303 void l2cap_output( std::uint8_t*, std::size_t& out_size, ConnectionData& );
304
308 std::size_t advertising_data( std::uint8_t* buffer, std::size_t buffer_size ) const;
309
310
314 std::size_t scan_response_data( std::uint8_t* buffer, std::size_t buffer_size ) const;
315
319 bool advertising_or_scan_response_data_has_been_changed();
320
321 typedef bool (*lcap_notification_callback_t)( const details::notification_data& item, void* usr_arg, details::notification_type type );
322
330 void notification_callback( lcap_notification_callback_t, void* usr_arg );
331
335 template < typename Connection >
336 void client_disconnected( Connection& );
337
338 typedef details::server_meta_type meta_type;
339
340 static details::attribute attribute_at( std::size_t index );
341
342 static constexpr std::uint16_t channel_id = l2cap_channel_ids::att;
343 static constexpr std::size_t minimum_channel_mtu_size = bluetoe::details::default_att_mtu_size;
344 static constexpr std::size_t maximum_channel_mtu_size = bluetoe::details::find_by_meta_type<
345 details::mtu_size_meta_type,
346 Options...,
348 >::type::mtu;
349
350 template < class PreviousData = details::no_such_type >
351 class channel_data_t
352 : public notification_queue<
353 typename notification_priority::template numbers< services >::type,
354 PreviousData >
355 , public connection_data
356 {
357 };
358
359 void notification_subscription_changed( const details::client_characteristic_configuration& );
362 private:
363 static constexpr std::size_t number_of_attributes = details::sum_by< services, details::sum_by_attributes >::value;
364
365 static_assert( std::tuple_size< services >::value > 0, "A server should at least contain one service." );
366
367 void error_response( std::uint8_t opcode, details::att_error_codes error_code, std::uint16_t handle, std::uint8_t* output, std::size_t& out_size );
368 void error_response( std::uint8_t opcode, details::att_error_codes error_code, std::uint8_t* output, std::size_t& out_size );
369
370 static details::att_error_codes access_result_to_att_code( details::attribute_access_result, details::att_error_codes default_att_code );
371
377 template < std::size_t A, std::size_t B = A >
378 bool check_size_and_handle_range( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, std::uint16_t& starting_handle, std::uint16_t& ending_handle );
379
380 template < std::size_t A, std::size_t B = A >
381 bool check_size_and_handle( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, std::uint16_t& handle, std::size_t& index );
382 bool check_handle( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, std::uint16_t& handle, std::size_t& index );
383
384 template < typename ConnectionData >
385 void handle_exchange_mtu_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
386 void handle_find_information_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size );
387 void handle_find_by_type_value_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size );
388 template < typename ConnectionData >
389 void handle_read_by_type_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
390 template < typename ConnectionData >
391 void handle_read_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
392 template < typename ConnectionData >
393 void handle_read_blob_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
394 void handle_read_by_group_type_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size );
395 template < typename ConnectionData >
396 void handle_read_multiple_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
397 template < typename ConnectionData >
398 void handle_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
399 template < typename ConnectionData >
400 void handle_write_command( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
401
402 template < typename Connection >
403 void handle_prepair_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection&, const details::no_such_type& );
404 template < typename Connection, typename WriteQueue >
405 void handle_prepair_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection&, const WriteQueue& );
406 template < typename Connection >
407 void handle_execute_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection&, const details::no_such_type& );
408 template < typename Connection, typename WriteQueue >
409 void handle_execute_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection&, const WriteQueue& );
410 void handle_value_confirmation( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, connection_data& );
411
412 template < class Iterator, class Filter = details::all_uuid_filter >
413 void all_attributes( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator&, const Filter& filter = details::all_uuid_filter() );
414
415 template < class Iterator, class Filter = details::all_uuid_filter >
416 bool all_services_by_group( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator&, const Filter& filter = details::all_uuid_filter() );
417
418 std::uint8_t* collect_handle_uuid_tuples( std::size_t start, std::size_t end, bool only_16_bit, std::uint8_t* output, std::uint8_t* output_end );
419
420 static void write_128bit_uuid( std::uint8_t* out, const details::attribute& char_declaration );
421
422 // mapping of a last handle to a valid attribute index
423 std::size_t last_handle_index( std::uint16_t ending_handle );
424
425 std::size_t advertising_data_impl( std::uint8_t* buffer, std::size_t buffer_size, const auto_advertising_data& ) const;
426
427 template < class T >
428 std::size_t advertising_data_impl( std::uint8_t* buffer, std::size_t buffer_size, const T& ) const;
429
430 std::size_t scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size, const auto_scan_response_data& ) const;
431
432 template < class T >
433 std::size_t scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size, const T& ) const;
434
435 // data
436 lcap_notification_callback_t l2cap_cb_;
437 void* l2cap_arg_;
438
439 static_assert(
440 std::is_same<
441 typename details::find_by_not_meta_type<
442 details::valid_server_option_meta_type,
443 Options...
444 >::type, details::no_such_type
445 >::value, "Option passed to a server that is not a valid server option." );
446 protected: // for testing
449 details::notification_data find_notification_data( const void* ) const;
450 details::notification_data find_notification_data_by_index( std::size_t client_characteristic_configuration_index ) const;
451
453 };
454
479 template < typename Server, typename ... Options >
481
482 template < typename ... ServerOptions, typename ... Options >
483 struct extend_server< server< ServerOptions... >, Options... > : server< ServerOptions..., Options... >
484 {
485 };
486
487 /*
488 * Implementation
489 */
491 template < typename ... Options >
493 : l2cap_cb_( nullptr )
494 {
495
496 }
497
498 template < typename ... Options >
499 template < typename ConnectionData >
500 void server< Options... >::l2cap_input( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
501 {
502 // clip the output size to the negotiated mtu
503 out_size = std::min< std::size_t >( out_size, connection.negotiated_mtu() );
504
505 assert( in_size != 0 );
506 assert( out_size >= details::default_att_mtu_size );
507
508 const details::att_opcodes opcode = static_cast< details::att_opcodes >( input[ 0 ] );
509
510 switch ( opcode )
511 {
512 // do not respond to an error response:
513 case details::att_opcodes::error_response:
514 out_size = 0;
515 break;
516
517 case details::att_opcodes::exchange_mtu_request:
518 handle_exchange_mtu_request( input, in_size, output, out_size, connection );
519 break;
520 case details::att_opcodes::find_information_request:
521 handle_find_information_request( input, in_size, output, out_size );
522 break;
523 case details::att_opcodes::find_by_type_value_request:
524 handle_find_by_type_value_request( input, in_size, output, out_size );
525 break;
526 case details::att_opcodes::read_by_type_request:
527 handle_read_by_type_request( input, in_size, output, out_size, connection );
528 break;
529 case details::att_opcodes::read_request:
530 handle_read_request( input, in_size, output, out_size, connection );
531 break;
532 case details::att_opcodes::read_blob_request:
533 handle_read_blob_request( input, in_size, output, out_size, connection );
534 break;
535 case details::att_opcodes::read_by_group_type_request:
536 handle_read_by_group_type_request( input, in_size, output, out_size );
537 break;
538 case details::att_opcodes::read_multiple_request:
539 handle_read_multiple_request( input, in_size, output, out_size, connection );
540 break;
541 case details::att_opcodes::write_request:
542 handle_write_request( input, in_size, output, out_size, connection );
543 break;
544 case details::att_opcodes::write_command:
545 handle_write_command( input, in_size, output, out_size, connection );
546 break;
547 case details::att_opcodes::prepare_write_request:
548 handle_prepair_write_request( input, in_size, output, out_size, connection, write_queue_type() );
549 break;
550 case details::att_opcodes::execute_write_request:
551 handle_execute_write_request( input, in_size, output, out_size, connection, write_queue_type() );
552 break;
553 case details::att_opcodes::confirmation:
554 handle_value_confirmation( input, in_size, output, out_size, connection );
555 break;
556 default:
557 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
558 break;
559 }
560 }
561
562 template < typename ... Options >
563 template < typename ConnectionData >
564 void server< Options... >::l2cap_output( std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
565 {
566 const auto pending = connection.dequeue_indication_or_confirmation();
567
568 if ( pending.first != details::notification_queue_entry_type::empty )
569 {
570 const std::uint16_t required_flag = pending.first == details::notification_queue_entry_type::notification
571 ? details::client_characteristic_configuration_notification_enabled
572 : details::client_characteristic_configuration_indication_enabled;
573
574 const auto data = find_notification_data_by_index( pending.second );
575
576 if ( connection.client_configurations().flags( data.client_characteristic_configuration_index() ) & required_flag &&
577 out_size >= 3 )
578 {
579 auto read = details::attribute_access_arguments::read( output + 3, output + out_size, 0, connection.client_configurations(), connection.security_attributes(), this );
580 auto attr = attribute_at( data.attribute_table_index() );
581 auto rc = attr.access( read, data.attribute_table_index() );
582
583 if ( rc == details::attribute_access_result::success )
584 {
585 *output = pending.first == details::notification_queue_entry_type::notification
586 ? bits( details::att_opcodes::notification )
587 : bits( details::att_opcodes::indication );
588 details::write_handle( output +1, handle_mapping::handle_by_index( data.attribute_table_index() ) );
589
590 out_size = 3 + read.buffer_size;
591 return;
592 }
593 }
594 }
595
596 out_size = 0;
597 }
598
599 namespace details {
600 // all this hassel to stop gcc from complaining about constant argument to if
601 template < bool >
602 struct copy_name
603 {
604 static std::uint8_t* impl( std::uint8_t* begin, std::uint8_t* end, const char* const name )
605 {
606 if ( ( end - begin ) <= 2 )
607 return begin;
608
609 const std::size_t name_length = std::strlen( name );
610 const std::size_t max_name_len = std::min< std::size_t >( name_length, end - begin - 2 );
611
612 if ( name_length > 0 )
613 {
614 begin[ 0 ] = max_name_len + 1;
615 begin[ 1 ] = max_name_len == name_length
616 ? bits( details::gap_types::complete_local_name )
617 : bits( details::gap_types::shortened_local_name );
618
619 std::copy( name + 0, name + max_name_len, &begin[ 2 ] );
620 begin += max_name_len + 2;
621 }
622
623 return begin;
624 }
625 };
626
627 template <>
628 struct copy_name< false >
629 {
630 static std::uint8_t* impl( std::uint8_t* begin, std::uint8_t*, const char* const )
631 {
632 return begin;
633 }
634 };
635
636 }
637
638 template < typename ... Options >
639 std::size_t server< Options... >::advertising_data( std::uint8_t* begin, std::size_t buffer_size ) const
640 {
641 return advertising_data_impl( begin, buffer_size, selected_advertising_data_t() );
642 }
643
644 template < typename ... Options >
645 template < class Advertiser >
646 std::size_t server< Options... >::advertising_data_impl( std::uint8_t* begin, std::size_t buffer_size, const Advertiser& ) const
647 {
648 return static_cast< const Advertiser& >( *this ).advertising_data( begin, buffer_size );
649 }
650
651 template < typename ... Options >
652 std::size_t server< Options... >::advertising_data_impl( std::uint8_t* begin, std::size_t buffer_size, const auto_advertising_data& ) const
653 {
654 std::uint8_t* const end = begin + buffer_size;
655
656 if ( buffer_size >= 3 )
657 {
658 begin[ 0 ] = 2;
659 begin[ 1 ] = bits( details::gap_types::flags );
660 // LE General Discoverable Mode | BR/EDR Not Supported
661 begin[ 2 ] = 6;
662
663 begin += 3;
664 }
665
666 using device_appearance = typename details::find_by_meta_type<
667 details::device_appearance_meta_type,
668 Options...,
670 >::type;
671
672 using appearance_advertising_config = typename details::find_by_meta_type<
673 details::advertise_appearance_meta_type,
674 Options...,
675 no_advertise_appearance >::type;
676
677 begin = appearance_advertising_config::template advertising_data< device_appearance >( begin, end );
678
679 typedef typename details::find_by_meta_type< details::server_name_meta_type, Options..., server_name< nullptr > >::type name;
680
681 begin = details::copy_name< name::name != nullptr >::impl( begin, end, name::name );
682
683 typedef typename details::find_by_meta_type<
684 details::list_of_16_bit_service_uuids_tag,
685 Options...,
686 details::default_list_of_16_bit_service_uuids< services >
687 >::type service_list_uuid16;
688
689 begin = service_list_uuid16::advertising_data( begin, end );
690
691 typedef typename details::find_by_meta_type<
692 details::list_of_128_bit_service_uuids_tag,
693 Options...,
694 details::default_list_of_128_bit_service_uuids< services >
695 >::type service_list_uuid128;
696
697 begin = service_list_uuid128::advertising_data( begin, end );
698
699 typedef typename details::find_by_meta_type<
700 details::peripheral_connection_interval_range_meta_type,
701 Options...,
702 details::no_peripheral_connection_interval_range
703 >::type peripheral_connection_interval_range_ad;
704
705 begin = peripheral_connection_interval_range_ad::advertising_data( begin, end );
706
707 // add aditional empty AD to be visible to Nordic sniffer
708 if ( static_cast< unsigned >( end - begin ) >= 2u )
709 {
710 *begin = 0;
711 ++begin;
712 *begin = 0;
713 ++begin;
714 }
715
716 return buffer_size - ( end - begin );
717 }
718
719 template < typename ... Options >
720 std::size_t server< Options... >::scan_response_data( std::uint8_t* buffer, std::size_t buffer_size ) const
721 {
722 return scan_response_data_impl( buffer, buffer_size, selected_scan_response_data_t() );
723 }
724
725 template < typename ... Options >
726 std::size_t server< Options... >::scan_response_data_impl( std::uint8_t* buffer, std::size_t /* buffer_size */, const auto_scan_response_data& ) const
727 {
728 // add aditional empty AD to be visible to Nordic sniffer.
729 // Some stacks do not recognize the response without this empty AD.
730 buffer[ 0 ] = 0;
731 buffer[ 1 ] = 0;
732
733 return 2;
734 }
735
736 template < typename ... Options >
737 template < class T >
738 std::size_t server< Options... >::scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size, const T& ) const
739 {
740 return static_cast< const T& >( *this ).scan_response_data( buffer, buffer_size );
741 }
742
743 template < typename ... Options >
744 bool server< Options... >::advertising_or_scan_response_data_has_been_changed()
745 {
746 const bool advertising_changed = this->advertising_data_dirty();
747 const bool scan_response_changed = this->scan_response_data_dirty();
748
749 return advertising_changed || scan_response_changed;
750 }
751
752 template < typename ... Options >
753 template < class T >
754 bool server< Options... >::notify( const T& value )
755 {
756 static_assert( number_of_client_configs != 0, "there is no characteristic that is configured for notification or indication" );
757
758 const details::notification_data data = find_notification_data( &value );
759 assert( data.valid() );
760
761 if ( l2cap_cb_ )
762 return l2cap_cb_( data, l2cap_arg_, details::notification_type::notification );
763
764 return false;
765 }
766
767 template < typename ... Options >
768 template < class CharacteristicUUID >
770 {
771 static_assert( number_of_client_configs != 0, "there is no characteristic that is configured for notification or indication" );
772
773 using characteristic = typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
774
775 static_assert( !std::is_same< characteristic, details::no_such_type >::value, "Notified characteristic not found by UUID." );
776 static_assert( characteristic::has_notification, "Characteristic must be configured for notification!" );
777
778 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
779
780 if ( l2cap_cb_ )
781 return l2cap_cb_( data, l2cap_arg_, details::notification_type::notification );
782
783 return false;
784 }
785
786 template < typename ... Options >
787 template < class T >
788 bool server< Options... >::indicate( const T& value )
789 {
790 static_assert( number_of_client_configs != 0, "there is no characteristic that is configured for notification or indication" );
791
792 const details::notification_data data = find_notification_data( &value );
793 assert( data.valid() );
794
795 if ( l2cap_cb_ )
796 return l2cap_cb_( data, l2cap_arg_, details::notification_type::indication );
797
798 return false;
799 }
800
801 template < typename ... Options >
802 template < class CharacteristicUUID >
804 {
805 static_assert( number_of_client_configs != 0, "there is no characteristic that is configured for notification or indication" );
806
807 using characteristic = typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
808
809 static_assert( !std::is_same< characteristic, details::no_such_type >::value, "Indicated characteristic not found by UUID." );
810 static_assert( characteristic::has_indication, "Characteristic must be configured for indication!" );
811
812 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
813
814 if ( l2cap_cb_ )
815 return l2cap_cb_( data, l2cap_arg_, details::notification_type::indication );
816
817 return false;
818 }
819
820 template < typename ... Options >
821 template < class CharacteristicUUID >
822 bool server< Options... >::configured_for_indications( const details::client_characteristic_configuration& connection ) const
823 {
824 using characteristic = typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
825 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
826
827 return connection.flags( data.client_characteristic_configuration_index() ) & details::client_characteristic_configuration_indication_enabled;
828 }
829
830 template < typename ... Options >
831 template < class CharacteristicUUID >
832 bool server< Options... >::configured_for_notifications( const details::client_characteristic_configuration& connection ) const
833 {
834 using characteristic = typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
835 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
836
837 return connection.flags( data.client_characteristic_configuration_index() ) & details::client_characteristic_configuration_notification_enabled;
838 }
839
840 template < typename ... Options >
841 template < class CharacteristicUUID >
842 bool server< Options... >::configured_for_notifications_or_indications( const details::client_characteristic_configuration& connection ) const
843 {
844 using characteristic = typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
845 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
846
847 static const auto both = ( details::client_characteristic_configuration_notification_enabled | details::client_characteristic_configuration_indication_enabled );
848
849 return connection.flags( data.client_characteristic_configuration_index() ) & both;
850 }
851
852 template < typename ... Options >
853 void server< Options... >::notification_callback( lcap_notification_callback_t cb, void* usr_arg )
854 {
855 l2cap_cb_ = cb;
856 l2cap_arg_ = usr_arg;
857 }
858
859 template < typename ... Options >
860 template < typename Connection >
861 void server< Options... >::client_disconnected( Connection& client )
862 {
863 this->free_write_queue( client );
864 }
865
866 template < typename ... Options >
867 details::attribute server< Options... >::attribute_at( std::size_t index )
868 {
869 return details::attribute_from_service_list< services, server< Options... >, cccd_indices >::attribute_at( index );
870 }
871
872 template < typename ... Options >
873 void server< Options... >::error_response( std::uint8_t opcode, details::att_error_codes error_code, std::uint16_t handle, std::uint8_t* output, std::size_t& out_size )
874 {
875 if ( out_size >= 5 )
876 {
877 output[ 0 ] = bits( details::att_opcodes::error_response );
878 output[ 1 ] = opcode;
879 output[ 2 ] = handle & 0xff;
880 output[ 3 ] = handle >> 8;
881 output[ 4 ] = bits( error_code );
882 out_size = 5;
883 }
884 else
885 {
886 out_size = 0 ;
887 }
888 }
889
890 template < typename ... Options >
891 void server< Options... >::error_response( std::uint8_t opcode, details::att_error_codes error_code, std::uint8_t* output, std::size_t& out_size )
892 {
893 error_response( opcode, error_code, 0, output, out_size );
894 }
895
896
897 template < typename ... Options >
898 details::att_error_codes server< Options... >::access_result_to_att_code( details::attribute_access_result access_code, details::att_error_codes default_att_code )
899 {
900 // if it can be copied lossles into a att_error_codes, it is a att_error_codes
901 const details::att_error_codes result = static_cast< details::att_error_codes >( access_code );
902
903 return static_cast< details::attribute_access_result >( result ) == access_code
904 ? result
905 : default_att_code;
906 }
907
908 template < typename ... Options >
909 template < std::size_t A, std::size_t B >
910 bool server< Options... >::check_size_and_handle_range(
911 const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, std::uint16_t& starting_handle, std::uint16_t& ending_handle )
912 {
913 if ( in_size != A && in_size != B )
914 {
915 error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
916 return false;
917 }
918
919 starting_handle = details::read_handle( &input[ 1 ] );
920 ending_handle = details::read_handle( &input[ 3 ] );
921
922 if ( starting_handle == 0 || starting_handle > ending_handle )
923 {
924 error_response( *input, details::att_error_codes::invalid_handle, starting_handle, output, out_size );
925 return false;
926 }
927
928 if ( handle_mapping::first_index_by_handle( starting_handle ) == details::invalid_attribute_index )
929 {
930 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
931 return false;
932 }
933
934 return true;
935 }
936
937 template < typename ... Options >
938 template < std::size_t A, std::size_t B >
939 bool server< Options... >::check_size_and_handle( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, std::uint16_t& handle, std::size_t& index )
940 {
941 if ( in_size != A && in_size != B )
942 {
943 error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
944 return false;
945 }
946
947 return check_handle( input, in_size, output, out_size, handle, index );
948 }
949
950 template < typename ... Options >
951 bool server< Options... >::check_handle( const std::uint8_t* input, std::size_t, std::uint8_t* output, std::size_t& out_size, std::uint16_t& handle, std::size_t& index )
952 {
953 handle = details::read_handle( &input[ 1 ] );
954
955 if ( handle == 0 )
956 {
957 error_response( *input, details::att_error_codes::invalid_handle, handle, output, out_size );
958 return false;
959 }
960
961 index = handle_mapping::index_by_handle( handle );
962
963 if ( index == details::invalid_attribute_index )
964 {
965 error_response( *input, details::att_error_codes::invalid_handle, handle, output, out_size );
966 return false;
967 }
968
969 return true;
970 }
971
972 template < typename ... Options >
973 template < typename ConnectionData >
974 void server< Options... >::handle_exchange_mtu_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
975 {
976 if ( in_size != 3 )
977 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
978
979 const std::uint16_t mtu = details::read_16bit( input + 1 );
980
981 if ( mtu < details::default_att_mtu_size )
982 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
983
984 connection.client_mtu( mtu );
985
986 *output = bits( details::att_opcodes::exchange_mtu_response );
987 details::write_16bit( output + 1, connection.server_mtu() );
988
989 out_size = 3u;
990 }
991
992 template < typename ... Options >
993 void server< Options... >::handle_find_information_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size )
994 {
995 std::uint16_t starting_handle, ending_handle;
996
997 if ( !check_size_and_handle_range< 5u >( input, in_size, output, out_size, starting_handle, ending_handle ) )
998 return;
999
1000 const std::size_t start_index = handle_mapping::first_index_by_handle( starting_handle );
1001 const bool only_16_bit_uuids = attribute_at( start_index ).uuid != bits( details::gatt_uuids::internal_128bit_uuid );
1002
1003 std::size_t ending_index = handle_mapping::first_index_by_handle( ending_handle );
1004
1005 // if the ending handle points not on an existing attribute, the search will end at the next, lower handle
1006 if ( ending_index != details::invalid_attribute_index && handle_mapping::handle_by_index( ending_index ) != ending_handle )
1007 --ending_index;
1008
1009 std::uint8_t* write_ptr = &output[ 0 ];
1010 std::uint8_t* const write_end = write_ptr + out_size;
1011
1012 *write_ptr = bits( details::att_opcodes::find_information_response );
1013 ++write_ptr;
1014
1015 if ( write_ptr != write_end )
1016 {
1017 *write_ptr = bits(
1018 only_16_bit_uuids
1019 ? details::att_uuid_format::short_16bit
1020 : details::att_uuid_format::long_128bit );
1021
1022 ++write_ptr;
1023
1024 }
1025
1026 write_ptr = collect_handle_uuid_tuples( start_index, ending_index, only_16_bit_uuids, write_ptr, write_end );
1027
1028 out_size = write_ptr - &output[ 0 ];
1029 }
1030
1031 namespace details {
1032 template < class Server >
1033 struct value_filter
1034 {
1035 value_filter( const std::uint8_t* begin, const std::uint8_t* end, Server& s )
1036 : begin_( begin )
1037 , end_( end )
1038 , server_( s )
1039 {
1040 }
1041
1042 bool operator()( std::uint16_t, const details::attribute& attr ) const
1043 {
1044 auto read = details::attribute_access_arguments::compare_value( begin_, end_, &server_ );
1045 return attr.access( read, 1 ) == details::attribute_access_result::value_equal;
1046 }
1047
1048 const std::uint8_t* const begin_;
1049 const std::uint8_t* const end_;
1050 Server& server_;
1051 };
1052
1053 struct collect_find_by_type_groups
1054 {
1055 collect_find_by_type_groups( std::uint8_t* begin, std::uint8_t* end )
1056 : begin_( begin )
1057 , end_( end )
1058 , current_( begin )
1059 {
1060 }
1061
1062 template < typename Service >
1063 bool operator()( std::uint16_t start_handle, std::uint16_t end_handle, const details::attribute& )
1064 {
1065 if ( end_ - current_ >= 4 )
1066 {
1067 current_ = details::write_handle( current_, start_handle );
1068 current_ = details::write_handle( current_, end_handle );
1069
1070 return true;
1071 }
1072
1073 return false;
1074 }
1075
1076 std::uint8_t size() const
1077 {
1078 return current_ - begin_;
1079 }
1080
1081 std::uint8_t* const begin_;
1082 std::uint8_t* const end_;
1083 std::uint8_t* current_;
1084 };
1085 }
1086
1087 template < typename ... Options >
1088 void server< Options... >::handle_find_by_type_value_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size )
1089 {
1090 std::uint16_t starting_handle, ending_handle;
1091
1092 if ( !check_size_and_handle_range< 9u, 23u >( input, in_size, output, out_size, starting_handle, ending_handle ) )
1093 return;
1094
1095 // the spec (v4.2) doesn't define, what to return in this case, but this seems to be a resonable response
1096 if ( details::read_handle( &input[ 5 ] ) != bits( details::gatt_uuids::primary_service ) )
1097 return error_response( *input, details::att_error_codes::unsupported_group_type, starting_handle, output, out_size );
1098
1099 details::collect_find_by_type_groups iterator( output + 1 , output + out_size );
1100
1101 if ( all_services_by_group( starting_handle, ending_handle, iterator, details::value_filter< server< Options... > >( &input[ 7 ], &input[ in_size ], *this ) ) )
1102 {
1103 *output = bits( details::att_opcodes::find_by_type_value_response );
1104 out_size = iterator.size() + 1;
1105 }
1106 else
1107 {
1108 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
1109 }
1110
1111 }
1112
1113 template < typename ... Options >
1114 template < typename ConnectionData >
1115 void server< Options... >::handle_read_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
1116 {
1117 std::uint16_t handle;
1118 std::size_t index;
1119
1120 if ( !check_size_and_handle< 3 >( input, in_size, output, out_size, handle, index ) )
1121 return;
1122
1123 auto read = details::attribute_access_arguments::read( output + 1, output + out_size, 0, connection.client_configurations(), connection.security_attributes(), this );
1124 auto rc = attribute_at( index ).access( read, index );
1125
1126 if ( rc == details::attribute_access_result::success )
1127 {
1128 *output = bits( details::att_opcodes::read_response );
1129 out_size = 1 + read.buffer_size;
1130 }
1131 else
1132 {
1133 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
1134 }
1135 }
1136
1137 template < typename ... Options >
1138 template < typename ConnectionData >
1139 void server< Options... >::handle_read_blob_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
1140 {
1141 std::uint16_t handle;
1142 std::size_t index;
1143
1144 if ( !check_size_and_handle< 5 >( input, in_size, output, out_size, handle, index ) )
1145 return;
1146
1147 const std::uint16_t offset = details::read_16bit( input + 3 );
1148
1149 auto read = details::attribute_access_arguments::read( output + 1, output + out_size, offset, connection.client_configurations(), connection.security_attributes(), this );
1150 auto rc = attribute_at( index ).access( read, index );
1151
1152 if ( rc == details::attribute_access_result::success )
1153 {
1154 *output = bits( details::att_opcodes::read_blob_response );
1155 out_size = 1 + read.buffer_size;
1156 }
1157 else
1158 {
1159 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
1160 }
1161 }
1162
1163 namespace details {
1164 template < typename Server >
1165 struct collect_attributes
1166 {
1167 void operator()( std::size_t index, const details::attribute& attr )
1168 {
1169 static constexpr std::size_t maximum_pdu_size = 253u;
1170 static constexpr std::size_t header_size = 2u;
1171
1172 if ( end_ - current_ >= static_cast< std::ptrdiff_t >( header_size ) )
1173 {
1174 const std::size_t max_data_size = std::min< std::size_t >( end_ - current_, maximum_pdu_size + header_size ) - header_size;
1175
1176 auto read = attribute_access_arguments::read( current_ + header_size, current_ + header_size + max_data_size, 0, config_, security_, &server_ );
1177 auto rc = attr.access( read, index );
1178
1179 if ( rc == details::attribute_access_result::success )
1180 {
1181 assert( read.buffer_size <= maximum_pdu_size );
1182
1183 if ( first_ )
1184 {
1185 size_ = read.buffer_size + header_size;
1186 first_ = false;
1187 }
1188
1189 if ( read.buffer_size + header_size == size_ )
1190 {
1191 current_ = details::write_handle( current_, handle_index_mapping< Server >::handle_by_index( index ) );
1192 current_ += static_cast< std::uint8_t >( read.buffer_size );
1193 }
1194 }
1195 }
1196 }
1197
1198 collect_attributes( std::uint8_t* begin, std::uint8_t* end,
1199 const details::client_characteristic_configuration& config,
1200 const connection_security_attributes& security,
1201 Server& server )
1202 : begin_( begin )
1203 , current_( begin )
1204 , end_( end )
1205 , size_( 0 )
1206 , first_( true )
1207 , config_( config )
1208 , security_( security )
1209 , server_( server )
1210 {
1211 }
1212
1213 std::uint8_t size() const
1214 {
1215 return current_ - begin_;
1216 }
1217
1218 std::uint8_t data_size() const
1219 {
1220 return size_;
1221 }
1222
1223 bool empty() const
1224 {
1225 return current_ == begin_;
1226 }
1227
1228 std::uint8_t* begin_;
1229 std::uint8_t* current_;
1230 std::uint8_t* end_;
1231 std::uint8_t size_;
1232 bool first_;
1233 details::client_characteristic_configuration config_;
1234 connection_security_attributes security_;
1235 Server& server_;
1236 };
1237 }
1238
1239 template < typename ... Options >
1240 template < typename ConnectionData >
1241 void server< Options... >::handle_read_by_type_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
1242 {
1243 std::uint16_t starting_handle, ending_handle;
1244
1245 if ( !check_size_and_handle_range< 5 + 2, 5 + 16 >( input, in_size, output, out_size, starting_handle, ending_handle ) )
1246 return;
1247
1248 details::collect_attributes< server< Options... > > iterator( output + 2, output + out_size,
1249 connection.client_configurations(), connection.security_attributes(), *this );
1250
1251 all_attributes( starting_handle, ending_handle, iterator, details::uuid_filter( input + 5, in_size == 5 + 16 ) );
1252
1253 if ( !iterator.empty() )
1254 {
1255 output[ 0 ] = bits( details::att_opcodes::read_by_type_response );
1256 output[ 1 ] = iterator.data_size();
1257 out_size = 2 + iterator.size();
1258 }
1259 else
1260 {
1261 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
1262 }
1263 }
1264
1265 namespace details {
1266 template < typename CCCDIndices, typename ServiceList, typename Server >
1267 struct collect_primary_services
1268 {
1269 collect_primary_services( std::uint8_t*& output, std::uint8_t* end, std::uint16_t starting_index, std::uint16_t starting_handle, std::uint16_t ending_handle, std::uint8_t& attribute_data_size, Server& server )
1270 : output_( output )
1271 , end_( end )
1272 , index_( details::handle_index_mapping< Server >::first_index_by_handle( starting_index ) )
1273 , starting_index_( details::handle_index_mapping< Server >::first_index_by_handle( starting_handle ) )
1274 , ending_index_( ending_handle )
1275 , stoped_( false )
1276 , first_( true )
1277 , is_128bit_uuid_( true )
1278 , attribute_data_size_( attribute_data_size )
1279 , server_( server )
1280 {
1281 }
1282
1283 template< typename Service >
1284 void each()
1285 {
1286 if ( !stoped_
1287 && ( starting_index_ != details::invalid_attribute_index && starting_index_ <= index_ )
1288 && ( index_ <= ending_index_ || ending_index_ == details::invalid_attribute_index ) )
1289 {
1290 if ( first_ )
1291 {
1292 is_128bit_uuid_ = Service::uuid::is_128bit;
1293 first_ = false;
1294 attribute_data_size_ = is_128bit_uuid_ ? 16 + 4 : 2 + 4;
1295 }
1296 // stop reading once, there is a service with a different UUID size, which would cause a gap
1297 else if ( is_128bit_uuid_ != Service::uuid::is_128bit )
1298 {
1299 stoped_ = true;
1300 }
1301
1304 output_ = Service::template read_primary_service_response< CCCDIndices, 0, ServiceList, Server >( output_, end_, index_, is_128bit_uuid_, server_ );
1305 }
1306
1307 index_ += Service::number_of_attributes;
1308 }
1309
1310 std::uint8_t*& output_;
1311 std::uint8_t* end_;
1312 std::size_t index_;
1313 const std::size_t starting_index_;
1314 const std::size_t ending_index_;
1315 bool stoped_;
1316 bool first_;
1317 bool is_128bit_uuid_;
1318 std::uint8_t& attribute_data_size_;
1319 Server& server_;
1320 };
1321 }
1322
1323 template < typename ... Options >
1324 void server< Options... >::handle_read_by_group_type_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size )
1325 {
1326 std::uint16_t starting_handle, ending_handle;
1327
1328 if ( !check_size_and_handle_range< 5 + 2, 5 + 16 >( input, in_size, output, out_size, starting_handle, ending_handle ) )
1329 return;
1330
1331 if ( in_size == 5 + 16 || details::read_handle( &input[ 5 ] ) != bits( details::gatt_uuids::primary_service ) )
1332 return error_response( *input, details::att_error_codes::unsupported_group_type, starting_handle, output, out_size );
1333
1334 std::uint8_t* begin = output;
1335 std::uint8_t* const end = output + out_size;
1336
1337 begin = details::write_opcode( begin, details::att_opcodes::read_by_group_type_response );
1338 ++begin; // room in the output for the size
1339
1340 std::uint8_t* const data_begin = begin;
1341 details::for_< services >::each( details::collect_primary_services< cccd_indices, services, server< Options... > >( begin, end, 1, starting_handle, ending_handle, *(begin -1 ), *this ) );
1342
1343 if ( begin == data_begin )
1344 {
1345 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
1346 }
1347 else
1348 {
1349 out_size = begin - output;
1350 }
1351 }
1352
1353 template < typename ... Options >
1354 template < typename ConnectionData >
1355 void server< Options... >::handle_read_multiple_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* const output, std::size_t& out_size, ConnectionData& cc )
1356 {
1357 if ( in_size < 5 || in_size % 2 == 0 )
1358 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1359
1360 const std::uint8_t opcode = *input;
1361 ++input;
1362 --in_size;
1363
1364 std::uint8_t* const end_output = output + out_size;
1365 std::uint8_t* out_ptr = output;
1366
1367 *out_ptr = bits( details::att_opcodes::read_multiple_response );
1368 ++out_ptr;
1369
1370 for ( const std::uint8_t* const end_input = input + in_size; input != end_input; input += 2 )
1371 {
1372 const std::uint16_t handle = details::read_handle( input );
1373
1374 if ( handle == 0 )
1375 return error_response( opcode, details::att_error_codes::invalid_handle, handle, output, out_size );
1376
1377 const std::size_t index = handle_mapping::index_by_handle( handle );
1378 if ( index == details::invalid_attribute_index )
1379 return error_response( opcode, details::att_error_codes::invalid_handle, handle, output, out_size );
1380
1381 auto read = details::attribute_access_arguments::read( out_ptr, end_output, 0, cc.client_configurations(), cc.security_attributes(), this );
1382 auto rc = attribute_at( index ).access( read, index );
1383
1384 if ( rc == details::attribute_access_result::success )
1385 {
1386 out_ptr += read.buffer_size;
1387 assert( out_ptr <= end_output );
1388 }
1389 else
1390 {
1391 return error_response( opcode, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
1392 }
1393 }
1394
1395 out_size = out_ptr - output;
1396 }
1397
1398 template < typename ... Options >
1399 template < typename ConnectionData >
1400 void server< Options... >::handle_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& connection )
1401 {
1402 if ( in_size < 3 )
1403 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1404
1405 std::uint16_t handle;
1406 std::size_t index;
1407
1408 if ( !check_handle( input, in_size, output, out_size, handle, index ) )
1409 return;
1410
1411 auto write = details::attribute_access_arguments::write( input + 3, input + in_size, 0, connection.client_configurations(), connection.security_attributes(), this );
1412 auto rc = attribute_at( index ).access( write, index );
1413
1414 if ( rc == details::attribute_access_result::success )
1415 {
1416 *output = bits( details::att_opcodes::write_response );
1417 out_size = 1;
1418 }
1419 else
1420 {
1421 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::write_not_permitted ), handle, output, out_size );
1422 }
1423 }
1424
1425 template < typename ... Options >
1426 template < typename ConnectionData >
1427 void server< Options... >::handle_write_command( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& cc )
1428 {
1429 // just like a write request
1430 handle_write_request( input, in_size, output, out_size, cc );
1431
1432 // but ignore all output
1433 out_size = 0;
1434 }
1435
1436 template < typename ... Options >
1437 template < typename Connection >
1438 void server< Options... >::handle_prepair_write_request( const std::uint8_t* input, std::size_t, std::uint8_t* output, std::size_t& out_size, Connection&, const details::no_such_type& )
1439 {
1440 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
1441 }
1442
1443 template < typename ... Options >
1444 template < typename Connection, typename WriteQueue >
1445 void server< Options... >::handle_prepair_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection& client, const WriteQueue& )
1446 {
1447 if ( in_size < 5 )
1448 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1449
1450 std::uint16_t handle;
1451 std::size_t index;
1452
1453 if ( !check_handle( input, in_size, output, out_size, handle, index ) )
1454 return;
1455
1456 auto write = details::attribute_access_arguments::check_write( this );
1457 auto rc = attribute_at( index ).access( write, index );
1458
1459 if ( rc != details::attribute_access_result::success )
1460 return error_response( *input, access_result_to_att_code( rc, details::att_error_codes::write_not_permitted ), handle, output, out_size );
1461
1462 // find size in the queue to write all but the opcode
1463 std::uint8_t* const queue_element = this->allocate_from_write_queue( in_size - 1, client );
1464
1465 if ( queue_element == nullptr )
1466 return error_response( *input, details::att_error_codes::prepare_queue_full, handle, output, out_size );
1467
1468 std::copy( input + 1, input + in_size, queue_element );
1469
1470 *output = bits( details::att_opcodes::prepare_write_response );
1471
1472 out_size = std::min< std::size_t >( out_size, in_size );
1473 std::copy( input + 1, input + out_size, output + 1 );
1474 }
1475
1476 template < typename ... Options >
1477 template < typename Connection >
1478 void server< Options... >::handle_execute_write_request( const std::uint8_t* input, std::size_t, std::uint8_t* output, std::size_t& out_size, Connection&, const details::no_such_type& )
1479 {
1480 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
1481 }
1482
1483 template < typename ... Options >
1484 template < typename Connection, typename WriteQueue >
1485 void server< Options... >::handle_execute_write_request( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, Connection& client, const WriteQueue& )
1486 {
1487 if ( in_size != 2 || ( input[ 1 ] != 0 && input[ 1 ] != 1 ) )
1488 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1489
1490 // the write queue will be freed in any case
1491 details::write_queue_guard< connection_data, details::write_queue< write_queue_type > > queue_guard( client, *this );
1492
1493 const std::uint8_t execute_flag = input[ 1 ];
1494
1495 if ( execute_flag )
1496 {
1497 for ( std::pair< std::uint8_t*, std::size_t > queue = this->first_write_queue_element( client ); queue.first; queue = this->next_write_queue_element( queue.first, client ) )
1498 {
1499 const std::uint16_t handle = details::read_handle( queue.first );
1500 const std::uint16_t offset = details::read_16bit( queue.first + 2 );
1501
1502 const std::size_t attribute_index = handle_mapping::index_by_handle( handle );
1503
1504 auto write = details::attribute_access_arguments::write( queue.first + 4 , queue.first + queue.second,
1505 offset, client.client_configurations(), client.security_attributes(), this );
1506 auto rc = attribute_at( attribute_index ).access( write, attribute_index );
1507
1508 if ( rc != details::attribute_access_result::success )
1509 {
1510 this->free_write_queue( client );
1511
1512 if ( rc == details::attribute_access_result::invalid_attribute_value_length )
1513 return error_response( *input, details::att_error_codes::invalid_attribute_value_length, handle, output, out_size );
1514
1515 return error_response( *input, details::att_error_codes::invalid_offset, handle, output, out_size );
1516 }
1517 }
1518 }
1519
1520 this->free_write_queue( client );
1521
1522 *output = bits( details::att_opcodes::execute_write_response );
1523 out_size = 1;
1524 }
1525
1526 template < typename ... Options >
1527 void server< Options... >::handle_value_confirmation( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, connection_data& )
1528 {
1529 if ( in_size != 1 )
1530 return error_response( *input, static_cast< details::att_error_codes >( 0x04 ), output, out_size );
1531
1532 out_size = 0;
1533
1534 if ( l2cap_cb_ )
1535 l2cap_cb_( details::notification_data(), l2cap_arg_, details::notification_type::confirmation );
1536 }
1537
1538
1539 template < typename ... Options >
1540 template < class Iterator, class Filter >
1541 void server< Options... >::all_attributes( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator& iter, const Filter& filter )
1542 {
1543 const std::size_t last_index = last_handle_index( ending_handle );
1544
1545 for ( std::size_t index = handle_mapping::first_index_by_handle( starting_handle ); index <= last_index; ++index )
1546 {
1547 const details::attribute attr = attribute_at( index );
1548
1549 if ( filter( index, attr ) )
1550 iter( index, attr );
1551 }
1552 }
1553
1554 namespace details {
1555 template < class Iterator, class Filter, class AllServices, class Server >
1556 struct services_by_group
1557 {
1558 services_by_group( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator& iterator, const Filter& filter, bool& found )
1559 : starting_index_( details::handle_index_mapping< Server >::first_index_by_handle( starting_handle ) )
1560 , ending_index_( details::handle_index_mapping< Server >::first_index_by_handle( ending_handle ) )
1561 , index_( 0 )
1562 , iterator_( iterator )
1563 , filter_( filter )
1564 , found_( found )
1565 {
1566 // if the ending_handle does not point to a specific handle, the last attribute befor that is ment.
1567 if ( ending_index_ != details::invalid_attribute_index && details::handle_index_mapping< Server >::handle_by_index( ending_index_ ) != ending_handle )
1568 {
1569 --ending_index_;
1570 }
1571 }
1572
1573 template< typename Service >
1574 void each()
1575 {
1576 if ( ( starting_index_ != details::invalid_attribute_index && starting_index_ <= index_ )
1577 && ( index_ <= ending_index_ || ending_index_ == details::invalid_attribute_index ) )
1578 {
1579 const details::attribute& attr = Server::attribute_at( index_ );
1580
1581 using mapping = details::handle_index_mapping< Server >;
1582
1583 if ( filter_( index_, attr ) )
1584 {
1585 found_ = iterator_.template operator()< Service >(
1586 mapping::handle_by_index( index_ ),
1587 mapping::handle_by_index( index_ + Service::number_of_attributes - 1 ),
1588 attr ) || found_;
1589 }
1590 }
1591
1592 index_ += Service::number_of_attributes;
1593 }
1594
1595 std::size_t starting_index_;
1596 std::size_t ending_index_;
1597 std::size_t index_;
1598 Iterator& iterator_;
1599 const Filter& filter_;
1600 bool& found_;
1601 };
1602 }
1603
1604 template < typename ... Options >
1605 template < class Iterator, class Filter >
1606 bool server< Options... >::all_services_by_group( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator& iterator, const Filter& filter )
1607 {
1608 bool result = false;
1609 details::services_by_group< Iterator, Filter, services, server< Options... > > service_iterator( starting_handle, ending_handle, iterator, filter, result );
1610 details::for_< services >::each( service_iterator );
1611
1612 return result;
1613 }
1614
1615 template < typename ... Options >
1616 std::uint8_t* server< Options... >::collect_handle_uuid_tuples( std::size_t start, std::size_t end, bool only_16_bit, std::uint8_t* out, std::uint8_t* out_end )
1617 {
1618 const std::size_t size_per_tuple = only_16_bit
1619 ? 2 + 2
1620 : 2 + 16;
1621
1622 for ( ; ( start <= end || end == details::invalid_attribute_index ) && start < number_of_attributes
1623 && static_cast< std::size_t >( out_end - out ) >= size_per_tuple; ++start )
1624 {
1625 const details::attribute attr = attribute_at( start );
1626 const bool is_16_bit_uuids = attr.uuid != bits( details::gatt_uuids::internal_128bit_uuid );
1627
1628 if ( only_16_bit == is_16_bit_uuids )
1629 {
1630 details::write_handle( out, handle_mapping::handle_by_index( start ) );
1631
1632 if ( is_16_bit_uuids )
1633 {
1634 details::write_16bit_uuid( out + 2, attr.uuid );
1635 }
1636 else
1637 {
1638 write_128bit_uuid( out + 2, attribute_at( start - 1 ) );
1639 }
1640
1641 out += size_per_tuple;
1642 }
1643 }
1644
1645 return out;
1646 }
1647
1648 template < typename ... Options >
1649 void server< Options... >::write_128bit_uuid( std::uint8_t* out, const details::attribute& char_declaration )
1650 {
1651 // this is a little bit tricky: To save memory, details::attribute contains only 16 bit uuids at all,
1652 // but the "Characteristic Value Declaration" contain 16 bit uuids. However, as the "Characteristic Value Declaration"
1653 // "is the first Attribute after the characteristic declaration", the attribute just in front of the
1654 // "Characteristic Value Declaration" contains the the 128 bit uuid.
1655 assert( char_declaration.uuid == bits( details::gatt_uuids::characteristic ) );
1656
1657 std::uint8_t buffer[ 3 + 16 ];
1658 auto read = details::attribute_access_arguments::read( buffer, 0 );
1659 char_declaration.access( read, 1 );
1660
1661 assert( read.buffer_size == sizeof( buffer ) );
1662
1663 std::copy( &read.buffer[ 3 ], &read.buffer[ 3 + 16 ], out );
1664 }
1665
1666 template < typename ... Options >
1667 std::size_t server< Options... >::last_handle_index( std::uint16_t ending_handle )
1668 {
1669 const std::size_t mapped = handle_mapping::first_index_by_handle( ending_handle );
1670
1671 return mapped == details::invalid_attribute_index
1672 ? number_of_attributes - 1
1673 : mapped;
1674 }
1675
1676 template < typename ... Options >
1677 details::notification_data server< Options... >::find_notification_data( const void* value ) const
1678 {
1679 return details::find_notification_data_in_list< notification_priority, services >::find_notification_data( value );
1680 }
1681
1682 template < typename ... Options >
1683 details::notification_data server< Options... >::find_notification_data_by_index( std::size_t client_characteristic_configuration_index ) const
1684 {
1685 return details::find_notification_data_in_list< notification_priority, services >::find_notification_data_by_index( client_characteristic_configuration_index );
1686 }
1687
1688 template < typename ... Options >
1689 void server< Options... >::notification_subscription_changed( const details::client_characteristic_configuration& data )
1690 {
1691 using cb_t = typename details::find_by_meta_type<
1692 details::cccd_callback_meta_type,
1693 Options...,
1694 no_client_characteristic_configuration_update_callback >::type;
1695
1696 cb_t::client_characteristic_configuration_updated( *this, data );
1697 }
1698
1700}
1701
1702#endif
class responsible to keep track of those characteristics that have outstanding notifications or indic...
Definition: notification_queue.hpp:47
per connection data
Definition: server.hpp:122
std::uint16_t server_mtu() const
returns the MTU of this server as provided in the c'tor
Definition: server.hpp:164
void client_mtu(std::uint16_t mtu)
sets the MTU size of the connected client.
Definition: server.hpp:144
std::uint16_t client_mtu() const
returns the client MTU
Definition: server.hpp:155
std::uint16_t negotiated_mtu() const
returns the negotiated MTU
Definition: server.hpp:132
Root of the declaration of a GATT server.
Definition: server.hpp:85
bool configured_for_notifications_or_indications(const details::client_characteristic_configuration &) const
returns true, if the given connection is configured to send indications or notifications for the give...
bool configured_for_indications(const details::client_characteristic_configuration &) const
returns true, if the given connection is configured to send indications for the given characteristic
bool indicate()
sends indications to all connceted clients.
bool configured_for_notifications(const details::client_characteristic_configuration &) const
returns true, if the given connection is configured to send notifications for the given characteristi...
bool indicate(const T &value)
sends indications to all connceted clients.
bool notify(const T &value)
notifies all connected clients about this value
server()
a server takes no runtime construction parameters
device_appearance< 0x0000 > unknown
Unknown.
Definition: appearance.hpp:54
Definition: custom_advertising.hpp:39
options, that allows Bluetoe to create the scan response data
Definition: custom_advertising.hpp:148
adds additional options to a given server definition
Definition: server.hpp:480
Used as a parameter to a server, to define that the GATT server will include a GAP service for GATT s...
Definition: gap_service.hpp:51
Defines priorities of notified or indicated characteristics.
Definition: outgoing_priority.hpp:160
define the maximum GATT MTU size to be used
Definition: gatt_options.hpp:24