1#ifndef BLUETOE_SERVER_HPP
2#define BLUETOE_SERVER_HPP
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>
35 template <
typename ... Options >
36 using selected_advertising_data_source =
37 typename details::find_by_meta_type<
38 details::advertising_data_meta_type,
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,
48 auto_scan_response_data
79 template <
typename ... Options >
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 ... >
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 ... >;
93 using gap_service_definition =
typename details::find_by_meta_type< details::gap_service_definition_meta_type,
95 using services =
typename gap_service_definition::template add_service< services_without_gap, Options... >::type;
97 static constexpr std::size_t number_of_client_configs = details::sum_by< services, details::sum_by_client_configs >::value;
99 using write_queue_type =
typename details::find_by_meta_type< details::write_queue_meta_type, Options... >::type;
101 using notification_priority =
typename details::find_by_meta_type< details::outgoing_priority_meta_type, Options...,
higher_outgoing_priority<> >::type;
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!" );
106 using cccd_indices =
typename details::find_notification_data_in_list< notification_priority, services >::cccd_indices;
108 using server_t =
server< Options... >;
109 using handle_mapping = details::handle_index_mapping< server_t >;
121 :
public details::client_characteristic_configurations< number_of_client_configs >
125 : client_mtu_( details::default_att_mtu_size )
146 assert( mtu >= details::default_att_mtu_size );
166 return maximum_channel_mtu_size;
171 std::uint16_t client_mtu_;
249 template <
class CharacteristicUUID >
273 template <
class CharacteristicUUID >
279 template <
class CharacteristicUUID >
285 template <
class CharacteristicUUID >
291 template <
class CharacteristicUUID >
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& );
302 template <
typename ConnectionData >
303 void l2cap_output( std::uint8_t*, std::size_t& out_size, ConnectionData& );
308 std::size_t advertising_data( std::uint8_t* buffer, std::size_t buffer_size )
const;
314 std::size_t scan_response_data( std::uint8_t* buffer, std::size_t buffer_size )
const;
319 bool advertising_or_scan_response_data_has_been_changed();
321 typedef bool (*lcap_notification_callback_t)(
const details::notification_data& item,
void* usr_arg, details::notification_type type );
330 void notification_callback( lcap_notification_callback_t,
void* usr_arg );
335 template <
typename Connection >
336 void client_disconnected( Connection& );
338 typedef details::server_meta_type meta_type;
340 static details::attribute attribute_at( std::size_t index );
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,
350 template <
class PreviousData = details::no_such_type >
353 typename notification_priority::template numbers< services >::type,
359 void notification_subscription_changed(
const details::client_characteristic_configuration& );
363 static constexpr std::size_t number_of_attributes = details::sum_by< services, details::sum_by_attributes >::value;
365 static_assert( std::tuple_size< services >::value > 0,
"A server should at least contain one service." );
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 );
370 static details::att_error_codes access_result_to_att_code( details::attribute_access_result, details::att_error_codes default_att_code );
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 );
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 );
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& );
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& );
412 template <
class Iterator,
class Filter = details::all_uu
id_filter >
413 void all_attributes( std::uint16_t starting_handle, std::uint16_t ending_handle, Iterator&,
const Filter& filter = details::all_uuid_filter() );
415 template <
class Iterator,
class Filter = details::all_uu
id_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() );
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 );
420 static void write_128bit_uuid( std::uint8_t* out,
const details::attribute& char_declaration );
423 std::size_t last_handle_index( std::uint16_t ending_handle );
425 std::size_t advertising_data_impl( std::uint8_t* buffer, std::size_t buffer_size,
const auto_advertising_data& )
const;
428 std::size_t advertising_data_impl( std::uint8_t* buffer, std::size_t buffer_size,
const T& )
const;
430 std::size_t scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size,
const auto_scan_response_data& )
const;
433 std::size_t scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size,
const T& )
const;
436 lcap_notification_callback_t l2cap_cb_;
441 typename details::find_by_not_meta_type<
442 details::valid_server_option_meta_type,
444 >::type, details::no_such_type
445 >::value,
"Option passed to a server that is not a valid server option." );
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;
479 template <
typename Server,
typename ... Options >
482 template <
typename ... ServerOptions,
typename ... Options >
491 template <
typename ... Options >
493 : l2cap_cb_( nullptr )
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 )
503 out_size = std::min< std::size_t >( out_size, connection.negotiated_mtu() );
505 assert( in_size != 0 );
506 assert( out_size >= details::default_att_mtu_size );
508 const details::att_opcodes opcode =
static_cast< details::att_opcodes
>( input[ 0 ] );
513 case details::att_opcodes::error_response:
517 case details::att_opcodes::exchange_mtu_request:
518 handle_exchange_mtu_request( input, in_size, output, out_size, connection );
520 case details::att_opcodes::find_information_request:
521 handle_find_information_request( input, in_size, output, out_size );
523 case details::att_opcodes::find_by_type_value_request:
524 handle_find_by_type_value_request( input, in_size, output, out_size );
526 case details::att_opcodes::read_by_type_request:
527 handle_read_by_type_request( input, in_size, output, out_size, connection );
529 case details::att_opcodes::read_request:
530 handle_read_request( input, in_size, output, out_size, connection );
532 case details::att_opcodes::read_blob_request:
533 handle_read_blob_request( input, in_size, output, out_size, connection );
535 case details::att_opcodes::read_by_group_type_request:
536 handle_read_by_group_type_request( input, in_size, output, out_size );
538 case details::att_opcodes::read_multiple_request:
539 handle_read_multiple_request( input, in_size, output, out_size, connection );
541 case details::att_opcodes::write_request:
542 handle_write_request( input, in_size, output, out_size, connection );
544 case details::att_opcodes::write_command:
545 handle_write_command( input, in_size, output, out_size, connection );
547 case details::att_opcodes::prepare_write_request:
548 handle_prepair_write_request( input, in_size, output, out_size, connection, write_queue_type() );
550 case details::att_opcodes::execute_write_request:
551 handle_execute_write_request( input, in_size, output, out_size, connection, write_queue_type() );
553 case details::att_opcodes::confirmation:
554 handle_value_confirmation( input, in_size, output, out_size, connection );
557 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
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 )
566 const auto pending = connection.dequeue_indication_or_confirmation();
568 if ( pending.first != details::notification_queue_entry_type::empty )
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;
574 const auto data = find_notification_data_by_index( pending.second );
576 if ( connection.client_configurations().flags( data.client_characteristic_configuration_index() ) & required_flag &&
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() );
583 if ( rc == details::attribute_access_result::success )
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() ) );
590 out_size = 3 + read.buffer_size;
604 static std::uint8_t* impl( std::uint8_t* begin, std::uint8_t* end,
const char*
const name )
606 if ( ( end - begin ) <= 2 )
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 );
612 if ( name_length > 0 )
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 );
619 std::copy( name + 0, name + max_name_len, &begin[ 2 ] );
620 begin += max_name_len + 2;
628 struct copy_name< false >
630 static std::uint8_t* impl( std::uint8_t* begin, std::uint8_t*,
const char*
const )
638 template <
typename ... Options >
639 std::size_t server< Options... >::advertising_data( std::uint8_t* begin, std::size_t buffer_size )
const
641 return advertising_data_impl( begin, buffer_size, selected_advertising_data_t() );
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
648 return static_cast< const Advertiser&
>( *this ).advertising_data( begin, buffer_size );
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
654 std::uint8_t*
const end = begin + buffer_size;
656 if ( buffer_size >= 3 )
659 begin[ 1 ] = bits( details::gap_types::flags );
666 using device_appearance =
typename details::find_by_meta_type<
667 details::device_appearance_meta_type,
672 using appearance_advertising_config =
typename details::find_by_meta_type<
673 details::advertise_appearance_meta_type,
675 no_advertise_appearance >::type;
677 begin = appearance_advertising_config::template advertising_data< device_appearance >( begin, end );
679 typedef typename details::find_by_meta_type< details::server_name_meta_type, Options..., server_name< nullptr > >::type name;
681 begin = details::copy_name< name::name != nullptr >::impl( begin, end, name::name );
683 typedef typename details::find_by_meta_type<
684 details::list_of_16_bit_service_uuids_tag,
686 details::default_list_of_16_bit_service_uuids< services >
687 >::type service_list_uuid16;
689 begin = service_list_uuid16::advertising_data( begin, end );
691 typedef typename details::find_by_meta_type<
692 details::list_of_128_bit_service_uuids_tag,
694 details::default_list_of_128_bit_service_uuids< services >
695 >::type service_list_uuid128;
697 begin = service_list_uuid128::advertising_data( begin, end );
699 typedef typename details::find_by_meta_type<
700 details::peripheral_connection_interval_range_meta_type,
702 details::no_peripheral_connection_interval_range
703 >::type peripheral_connection_interval_range_ad;
705 begin = peripheral_connection_interval_range_ad::advertising_data( begin, end );
708 if (
static_cast< unsigned >( end - begin ) >= 2u )
716 return buffer_size - ( end - begin );
719 template <
typename ... Options >
720 std::size_t server< Options... >::scan_response_data( std::uint8_t* buffer, std::size_t buffer_size )
const
722 return scan_response_data_impl( buffer, buffer_size, selected_scan_response_data_t() );
725 template <
typename ... Options >
726 std::size_t server< Options... >::scan_response_data_impl( std::uint8_t* buffer, std::size_t ,
const auto_scan_response_data& )
const
736 template <
typename ... Options >
738 std::size_t server< Options... >::scan_response_data_impl( std::uint8_t* buffer, std::size_t buffer_size,
const T& )
const
740 return static_cast< const T&
>( *this ).scan_response_data( buffer, buffer_size );
743 template <
typename ... Options >
744 bool server< Options... >::advertising_or_scan_response_data_has_been_changed()
746 const bool advertising_changed = this->advertising_data_dirty();
747 const bool scan_response_changed = this->scan_response_data_dirty();
749 return advertising_changed || scan_response_changed;
752 template <
typename ... Options >
756 static_assert( number_of_client_configs != 0,
"there is no characteristic that is configured for notification or indication" );
758 const details::notification_data data = find_notification_data( &value );
759 assert( data.valid() );
762 return l2cap_cb_( data, l2cap_arg_, details::notification_type::notification );
767 template <
typename ... Options >
768 template <
class CharacteristicUUID >
771 static_assert( number_of_client_configs != 0,
"there is no characteristic that is configured for notification or indication" );
773 using characteristic =
typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
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!" );
778 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
781 return l2cap_cb_( data, l2cap_arg_, details::notification_type::notification );
786 template <
typename ... Options >
790 static_assert( number_of_client_configs != 0,
"there is no characteristic that is configured for notification or indication" );
792 const details::notification_data data = find_notification_data( &value );
793 assert( data.valid() );
796 return l2cap_cb_( data, l2cap_arg_, details::notification_type::indication );
801 template <
typename ... Options >
802 template <
class CharacteristicUUID >
805 static_assert( number_of_client_configs != 0,
"there is no characteristic that is configured for notification or indication" );
807 using characteristic =
typename details::find_characteristic_data_by_uuid_in_service_list< services, CharacteristicUUID >::type;
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!" );
812 const auto data = details::find_notification_by_uuid< notification_priority, services, typename characteristic::characteristic_t >::data();
815 return l2cap_cb_( data, l2cap_arg_, details::notification_type::indication );
820 template <
typename ... Options >
821 template <
class CharacteristicUUID >
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();
827 return connection.flags( data.client_characteristic_configuration_index() ) & details::client_characteristic_configuration_indication_enabled;
830 template <
typename ... Options >
831 template <
class CharacteristicUUID >
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();
837 return connection.flags( data.client_characteristic_configuration_index() ) & details::client_characteristic_configuration_notification_enabled;
840 template <
typename ... Options >
841 template <
class CharacteristicUUID >
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();
847 static const auto both = ( details::client_characteristic_configuration_notification_enabled | details::client_characteristic_configuration_indication_enabled );
849 return connection.flags( data.client_characteristic_configuration_index() ) & both;
852 template <
typename ... Options >
853 void server< Options... >::notification_callback( lcap_notification_callback_t cb,
void* usr_arg )
856 l2cap_arg_ = usr_arg;
859 template <
typename ... Options >
860 template <
typename Connection >
861 void server< Options... >::client_disconnected( Connection& client )
863 this->free_write_queue( client );
866 template <
typename ... Options >
867 details::attribute server< Options... >::attribute_at( std::size_t index )
869 return details::attribute_from_service_list< services, server< Options... >, cccd_indices >::attribute_at( index );
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 )
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 );
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 )
893 error_response( opcode, error_code, 0, output, out_size );
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 )
901 const details::att_error_codes result =
static_cast< details::att_error_codes
>( access_code );
903 return static_cast< details::attribute_access_result
>( result ) == access_code
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 )
913 if ( in_size != A && in_size != B )
915 error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
919 starting_handle = details::read_handle( &input[ 1 ] );
920 ending_handle = details::read_handle( &input[ 3 ] );
922 if ( starting_handle == 0 || starting_handle > ending_handle )
924 error_response( *input, details::att_error_codes::invalid_handle, starting_handle, output, out_size );
928 if ( handle_mapping::first_index_by_handle( starting_handle ) == details::invalid_attribute_index )
930 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
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 )
941 if ( in_size != A && in_size != B )
943 error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
947 return check_handle( input, in_size, output, out_size, handle, index );
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 )
953 handle = details::read_handle( &input[ 1 ] );
957 error_response( *input, details::att_error_codes::invalid_handle, handle, output, out_size );
961 index = handle_mapping::index_by_handle( handle );
963 if ( index == details::invalid_attribute_index )
965 error_response( *input, details::att_error_codes::invalid_handle, handle, output, out_size );
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 )
977 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
979 const std::uint16_t mtu = details::read_16bit( input + 1 );
981 if ( mtu < details::default_att_mtu_size )
982 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
984 connection.client_mtu( mtu );
986 *output = bits( details::att_opcodes::exchange_mtu_response );
987 details::write_16bit( output + 1, connection.server_mtu() );
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 )
995 std::uint16_t starting_handle, ending_handle;
997 if ( !check_size_and_handle_range< 5u >( input, in_size, output, out_size, starting_handle, ending_handle ) )
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 );
1003 std::size_t ending_index = handle_mapping::first_index_by_handle( ending_handle );
1006 if ( ending_index != details::invalid_attribute_index && handle_mapping::handle_by_index( ending_index ) != ending_handle )
1009 std::uint8_t* write_ptr = &output[ 0 ];
1010 std::uint8_t*
const write_end = write_ptr + out_size;
1012 *write_ptr = bits( details::att_opcodes::find_information_response );
1015 if ( write_ptr != write_end )
1019 ? details::att_uuid_format::short_16bit
1020 : details::att_uuid_format::long_128bit );
1026 write_ptr = collect_handle_uuid_tuples( start_index, ending_index, only_16_bit_uuids, write_ptr, write_end );
1028 out_size = write_ptr - &output[ 0 ];
1032 template <
class Server >
1035 value_filter(
const std::uint8_t* begin,
const std::uint8_t* end, Server& s )
1042 bool operator()( std::uint16_t,
const details::attribute& attr )
const
1044 auto read = details::attribute_access_arguments::compare_value( begin_, end_, &server_ );
1045 return attr.access( read, 1 ) == details::attribute_access_result::value_equal;
1048 const std::uint8_t*
const begin_;
1049 const std::uint8_t*
const end_;
1053 struct collect_find_by_type_groups
1055 collect_find_by_type_groups( std::uint8_t* begin, std::uint8_t* end )
1062 template <
typename Service >
1063 bool operator()( std::uint16_t start_handle, std::uint16_t end_handle,
const details::attribute& )
1065 if ( end_ - current_ >= 4 )
1067 current_ = details::write_handle( current_, start_handle );
1068 current_ = details::write_handle( current_, end_handle );
1076 std::uint8_t size()
const
1078 return current_ - begin_;
1081 std::uint8_t*
const begin_;
1082 std::uint8_t*
const end_;
1083 std::uint8_t* current_;
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 )
1090 std::uint16_t starting_handle, ending_handle;
1092 if ( !check_size_and_handle_range< 9u, 23u >( input, in_size, output, out_size, starting_handle, ending_handle ) )
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 );
1099 details::collect_find_by_type_groups iterator( output + 1 , output + out_size );
1101 if ( all_services_by_group( starting_handle, ending_handle, iterator, details::value_filter< server< Options... > >( &input[ 7 ], &input[ in_size ], *
this ) ) )
1103 *output = bits( details::att_opcodes::find_by_type_value_response );
1104 out_size = iterator.size() + 1;
1108 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
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 )
1117 std::uint16_t handle;
1120 if ( !check_size_and_handle< 3 >( input, in_size, output, out_size, handle, index ) )
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 );
1126 if ( rc == details::attribute_access_result::success )
1128 *output = bits( details::att_opcodes::read_response );
1129 out_size = 1 + read.buffer_size;
1133 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
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 )
1141 std::uint16_t handle;
1144 if ( !check_size_and_handle< 5 >( input, in_size, output, out_size, handle, index ) )
1147 const std::uint16_t offset = details::read_16bit( input + 3 );
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 );
1152 if ( rc == details::attribute_access_result::success )
1154 *output = bits( details::att_opcodes::read_blob_response );
1155 out_size = 1 + read.buffer_size;
1159 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
1164 template <
typename Server >
1165 struct collect_attributes
1167 void operator()( std::size_t index,
const details::attribute& attr )
1169 static constexpr std::size_t maximum_pdu_size = 253u;
1170 static constexpr std::size_t header_size = 2u;
1172 if ( end_ - current_ >=
static_cast< std::ptrdiff_t
>( header_size ) )
1174 const std::size_t max_data_size = std::min< std::size_t >( end_ - current_, maximum_pdu_size + header_size ) - header_size;
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 );
1179 if ( rc == details::attribute_access_result::success )
1181 assert( read.buffer_size <= maximum_pdu_size );
1185 size_ = read.buffer_size + header_size;
1189 if ( read.buffer_size + header_size == size_ )
1191 current_ = details::write_handle( current_, handle_index_mapping< Server >::handle_by_index( index ) );
1192 current_ +=
static_cast< std::uint8_t
>( read.buffer_size );
1198 collect_attributes( std::uint8_t* begin, std::uint8_t* end,
1199 const details::client_characteristic_configuration& config,
1200 const connection_security_attributes& security,
1208 , security_( security )
1213 std::uint8_t size()
const
1215 return current_ - begin_;
1218 std::uint8_t data_size()
const
1225 return current_ == begin_;
1228 std::uint8_t* begin_;
1229 std::uint8_t* current_;
1233 details::client_characteristic_configuration config_;
1234 connection_security_attributes security_;
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 )
1243 std::uint16_t starting_handle, ending_handle;
1245 if ( !check_size_and_handle_range< 5 + 2, 5 + 16 >( input, in_size, output, out_size, starting_handle, ending_handle ) )
1248 details::collect_attributes< server< Options... > > iterator( output + 2, output + out_size,
1249 connection.client_configurations(), connection.security_attributes(), *
this );
1251 all_attributes( starting_handle, ending_handle, iterator, details::uuid_filter( input + 5, in_size == 5 + 16 ) );
1253 if ( !iterator.empty() )
1255 output[ 0 ] = bits( details::att_opcodes::read_by_type_response );
1256 output[ 1 ] = iterator.data_size();
1257 out_size = 2 + iterator.size();
1261 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
1266 template <
typename CCCDIndices,
typename ServiceList,
typename Server >
1267 struct collect_primary_services
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 )
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 )
1277 , is_128bit_uuid_( true )
1278 , attribute_data_size_( attribute_data_size )
1283 template<
typename Service >
1287 && ( starting_index_ != details::invalid_attribute_index && starting_index_ <= index_ )
1288 && ( index_ <= ending_index_ || ending_index_ == details::invalid_attribute_index ) )
1292 is_128bit_uuid_ = Service::uuid::is_128bit;
1294 attribute_data_size_ = is_128bit_uuid_ ? 16 + 4 : 2 + 4;
1297 else if ( is_128bit_uuid_ != Service::uuid::is_128bit )
1304 output_ = Service::template read_primary_service_response< CCCDIndices, 0, ServiceList, Server >( output_, end_, index_, is_128bit_uuid_, server_ );
1307 index_ += Service::number_of_attributes;
1310 std::uint8_t*& output_;
1313 const std::size_t starting_index_;
1314 const std::size_t ending_index_;
1317 bool is_128bit_uuid_;
1318 std::uint8_t& attribute_data_size_;
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 )
1326 std::uint16_t starting_handle, ending_handle;
1328 if ( !check_size_and_handle_range< 5 + 2, 5 + 16 >( input, in_size, output, out_size, starting_handle, ending_handle ) )
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 );
1334 std::uint8_t* begin = output;
1335 std::uint8_t*
const end = output + out_size;
1337 begin = details::write_opcode( begin, details::att_opcodes::read_by_group_type_response );
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 ) );
1343 if ( begin == data_begin )
1345 error_response( *input, details::att_error_codes::attribute_not_found, starting_handle, output, out_size );
1349 out_size = begin - output;
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 )
1357 if ( in_size < 5 || in_size % 2 == 0 )
1358 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1360 const std::uint8_t opcode = *input;
1364 std::uint8_t*
const end_output = output + out_size;
1365 std::uint8_t* out_ptr = output;
1367 *out_ptr = bits( details::att_opcodes::read_multiple_response );
1370 for (
const std::uint8_t*
const end_input = input + in_size; input != end_input; input += 2 )
1372 const std::uint16_t handle = details::read_handle( input );
1375 return error_response( opcode, details::att_error_codes::invalid_handle, handle, output, out_size );
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 );
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 );
1384 if ( rc == details::attribute_access_result::success )
1386 out_ptr += read.buffer_size;
1387 assert( out_ptr <= end_output );
1391 return error_response( opcode, access_result_to_att_code( rc, details::att_error_codes::read_not_permitted ), handle, output, out_size );
1395 out_size = out_ptr - output;
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 )
1403 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1405 std::uint16_t handle;
1408 if ( !check_handle( input, in_size, output, out_size, handle, index ) )
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 );
1414 if ( rc == details::attribute_access_result::success )
1416 *output = bits( details::att_opcodes::write_response );
1421 error_response( *input, access_result_to_att_code( rc, details::att_error_codes::write_not_permitted ), handle, output, out_size );
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 )
1430 handle_write_request( input, in_size, output, out_size, cc );
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& )
1440 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
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& )
1448 return error_response( *input, details::att_error_codes::invalid_pdu, output, out_size );
1450 std::uint16_t handle;
1453 if ( !check_handle( input, in_size, output, out_size, handle, index ) )
1456 auto write = details::attribute_access_arguments::check_write(
this );
1457 auto rc = attribute_at( index ).access( write, index );
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 );
1463 std::uint8_t*
const queue_element = this->allocate_from_write_queue( in_size - 1, client );
1465 if ( queue_element ==
nullptr )
1466 return error_response( *input, details::att_error_codes::prepare_queue_full, handle, output, out_size );
1468 std::copy( input + 1, input + in_size, queue_element );
1470 *output = bits( details::att_opcodes::prepare_write_response );
1472 out_size = std::min< std::size_t >( out_size, in_size );
1473 std::copy( input + 1, input + out_size, output + 1 );
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& )
1480 error_response( *input, details::att_error_codes::request_not_supported, output, out_size );
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& )
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 );
1491 details::write_queue_guard< connection_data, details::write_queue< write_queue_type > > queue_guard( client, *
this );
1493 const std::uint8_t execute_flag = input[ 1 ];
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 ) )
1499 const std::uint16_t handle = details::read_handle( queue.first );
1500 const std::uint16_t offset = details::read_16bit( queue.first + 2 );
1502 const std::size_t attribute_index = handle_mapping::index_by_handle( handle );
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 );
1508 if ( rc != details::attribute_access_result::success )
1510 this->free_write_queue( client );
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 );
1515 return error_response( *input, details::att_error_codes::invalid_offset, handle, output, out_size );
1520 this->free_write_queue( client );
1522 *output = bits( details::att_opcodes::execute_write_response );
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& )
1530 return error_response( *input,
static_cast< details::att_error_codes
>( 0x04 ), output, out_size );
1535 l2cap_cb_( details::notification_data(), l2cap_arg_, details::notification_type::confirmation );
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 )
1543 const std::size_t last_index = last_handle_index( ending_handle );
1545 for ( std::size_t index = handle_mapping::first_index_by_handle( starting_handle ); index <= last_index; ++index )
1547 const details::attribute attr = attribute_at( index );
1549 if ( filter( index, attr ) )
1550 iter( index, attr );
1555 template <
class Iterator,
class Filter,
class AllServices,
class Server >
1556 struct services_by_group
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 ) )
1562 , iterator_( iterator )
1567 if ( ending_index_ != details::invalid_attribute_index && details::handle_index_mapping< Server >::handle_by_index( ending_index_ ) != ending_handle )
1573 template<
typename Service >
1576 if ( ( starting_index_ != details::invalid_attribute_index && starting_index_ <= index_ )
1577 && ( index_ <= ending_index_ || ending_index_ == details::invalid_attribute_index ) )
1579 const details::attribute& attr = Server::attribute_at( index_ );
1581 using mapping = details::handle_index_mapping< Server >;
1583 if ( filter_( index_, attr ) )
1585 found_ = iterator_.template operator()< Service >(
1586 mapping::handle_by_index( index_ ),
1587 mapping::handle_by_index( index_ + Service::number_of_attributes - 1 ),
1592 index_ += Service::number_of_attributes;
1595 std::size_t starting_index_;
1596 std::size_t ending_index_;
1598 Iterator& iterator_;
1599 const Filter& filter_;
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 )
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 );
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 )
1618 const std::size_t size_per_tuple = only_16_bit
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 )
1625 const details::attribute attr = attribute_at( start );
1626 const bool is_16_bit_uuids = attr.uuid != bits( details::gatt_uuids::internal_128bit_uuid );
1628 if ( only_16_bit == is_16_bit_uuids )
1630 details::write_handle( out, handle_mapping::handle_by_index( start ) );
1632 if ( is_16_bit_uuids )
1634 details::write_16bit_uuid( out + 2, attr.uuid );
1638 write_128bit_uuid( out + 2, attribute_at( start - 1 ) );
1641 out += size_per_tuple;
1648 template <
typename ... Options >
1649 void server< Options... >::write_128bit_uuid( std::uint8_t* out,
const details::attribute& char_declaration )
1655 assert( char_declaration.uuid == bits( details::gatt_uuids::characteristic ) );
1657 std::uint8_t buffer[ 3 + 16 ];
1658 auto read = details::attribute_access_arguments::read( buffer, 0 );
1659 char_declaration.access( read, 1 );
1661 assert( read.buffer_size ==
sizeof( buffer ) );
1663 std::copy( &read.buffer[ 3 ], &read.buffer[ 3 + 16 ], out );
1666 template <
typename ... Options >
1667 std::size_t server< Options... >::last_handle_index( std::uint16_t ending_handle )
1669 const std::size_t mapped = handle_mapping::first_index_by_handle( ending_handle );
1671 return mapped == details::invalid_attribute_index
1672 ? number_of_attributes - 1
1676 template <
typename ... Options >
1677 details::notification_data server< Options... >::find_notification_data(
const void* value )
const
1679 return details::find_notification_data_in_list< notification_priority, services >::find_notification_data( value );
1682 template <
typename ... Options >
1683 details::notification_data server< Options... >::find_notification_data_by_index( std::size_t client_characteristic_configuration_index )
const
1685 return details::find_notification_data_in_list< notification_priority, services >::find_notification_data_by_index( client_characteristic_configuration_index );
1688 template <
typename ... Options >
1689 void server< Options... >::notification_subscription_changed(
const details::client_characteristic_configuration& data )
1691 using cb_t =
typename details::find_by_meta_type<
1692 details::cccd_callback_meta_type,
1694 no_client_characteristic_configuration_update_callback >::type;
1696 cb_t::client_characteristic_configuration_updated( *
this, data );
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