BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
characteristic.hpp
1#ifndef BLUETOE_CHARACTERISTIC_HPP
2#define BLUETOE_CHARACTERISTIC_HPP
3
4#include <bluetoe/characteristic_value.hpp>
5#include <bluetoe/attribute.hpp>
6#include <bluetoe/attribute_handle.hpp>
7#include <bluetoe/codes.hpp>
8#include <bluetoe/uuid.hpp>
10#include <bluetoe/bits.hpp>
11#include <bluetoe/scattered_access.hpp>
12#include <bluetoe/service_uuid.hpp>
13#include <bluetoe/attribute_generator.hpp>
14#include <bluetoe/server_meta_type.hpp>
15#include <bluetoe/meta_types.hpp>
16#include <bluetoe/encryption.hpp>
17#include <bluetoe/descriptor.hpp>
18
19#include <cstddef>
20#include <cassert>
21#include <cstring>
22#include <algorithm>
23#include <type_traits>
24
25namespace bluetoe {
26
27 namespace details {
28 struct characteristic_uuid_meta_type {};
29
30 struct characteristic_declaration_parameter {};
31 struct characteristic_user_description_parameter {};
32
33 template < typename CCCDIndices, typename ... Options >
34 struct generate_characteristic_attributes;
35
36 template < typename ... Options >
37 struct count_characteristic_attributes;
38
39 template < typename Characteristic >
40 struct sum_by_attributes;
41
42 template < typename Characteristic >
43 struct sum_by_client_configs;
44 }
45
55 template <
56 std::uint64_t A,
57 std::uint64_t B,
58 std::uint64_t C,
59 std::uint64_t D,
60 std::uint64_t E >
61 struct characteristic_uuid : details::uuid< A, B, C, D, E >
62 {
64 struct meta_type :
65 details::characteristic_uuid_meta_type,
66 details::characteristic_value_declaration_parameter,
67 details::characteristic_declaration_parameter,
68 details::valid_characteristic_option_meta_type {};
70 };
71
76 template <
77 std::uint64_t UUID >
78 struct characteristic_uuid16 : details::uuid16< UUID >
79 {
81 struct meta_type :
82 details::characteristic_uuid_meta_type,
83 details::characteristic_value_declaration_parameter,
84 details::characteristic_declaration_parameter,
85 details::valid_characteristic_option_meta_type {};
86 static constexpr bool is_128bit = false;
88 };
89
158 template < typename ... Options >
160 {
161 public:
164 using attribute_numbers = details::count_characteristic_attributes< Options... >;
165
169 static constexpr std::size_t number_of_attributes = attribute_numbers::number_of_attributes;
170 static constexpr std::size_t number_of_client_configs = attribute_numbers::number_of_client_configs;
171
172 struct meta_type :
173 details::characteristic_meta_type,
174 details::valid_service_option_meta_type {};
175
176
177 // this is just the configured UUID, if auto uuids are used, this will be no_such_type
178 typedef typename details::find_by_meta_type< details::characteristic_uuid_meta_type, Options... >::type configured_uuid;
179
184 template < typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server >
185 static details::attribute attribute_at( std::size_t index );
186
187 typedef typename details::find_by_meta_type< details::characteristic_value_meta_type, Options... >::type base_value_type;
188
189 static_assert( !std::is_same< base_value_type, details::no_such_type >::value,
190 "please make sure, that every characteristic defines some kind of value (bind_characteristic_value<> for example)" );
191
192 typedef typename base_value_type::template value_impl< Options... > value_type;
193
194 static_assert(
195 std::is_same<
196 typename details::find_by_not_meta_type<
197 details::valid_characteristic_option_meta_type,
198 Options...
199 >::type, details::no_such_type >::value,
200 "Parameter passed to a characteristic that is not a valid characteristic option!" );
202 private:
203 // the first two attributes are always the declaration, followed by the value
204 static constexpr std::size_t characteristic_declaration_index = 0;
205 static constexpr std::size_t characteristic_value_index = 1;
206 };
207
225 template < const char* const >
227 {
229 struct meta_type :
230 details::characteristic_parameter_meta_type,
231 details::characteristic_user_description_parameter,
232 details::characteristic_declaration_parameter,
233 details::valid_characteristic_option_meta_type {};
235 };
236
237 // implementation
240 template < typename ... Options >
241 template < typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server >
242 details::attribute characteristic< Options... >::attribute_at( std::size_t index )
243 {
244 assert( index < number_of_attributes );
245
246 using characteristic_descriptor_declarations = typename details::generate_characteristic_attributes< CCCDIndices, Options... >;
247 return characteristic_descriptor_declarations::template attribute_at< ClientCharacteristicIndex, Service, Server >( index );
248 }
249
250 namespace details {
251 template < typename ServiceUUID, typename ... Options >
252 struct characteristic_or_service_uuid
253 {
254 using char_uuid = typename find_by_meta_type< characteristic_uuid_meta_type, Options... >::type;
255 using uuid = typename or_type< no_such_type, char_uuid, ServiceUUID >::type;
256
257 static_assert( !std::is_same< uuid, no_such_type >::value, "If instanciating a characteristic<> for testing, please provide a UUID." );
258
259 static constexpr bool auto_generated_uuid = std::is_same< char_uuid, no_such_type >::value;
260 static constexpr bool serice_uuid_is_16_bit = count_by_meta_type< details::service_uuid_16_meta_type, ServiceUUID >::count;
261
262 static_assert( !( auto_generated_uuid && serice_uuid_is_16_bit ), "No support for automatic generated characteristic UUIDs, if the containing service has not 128 bit UUID" );
263 };
264
265 /*
266 * Characteristic declaration
267 */
268 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename ... ServiceOptions, typename Server, typename ... Options >
269 struct generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions...>, Server, Options... >
270 {
271 using characteristic_or_service_uuid_t = characteristic_or_service_uuid< typename service< ServiceOptions... >::uuid, Options... >;
272 using uuid = typename characteristic_or_service_uuid_t::uuid;
273 using value_type = typename characteristic< Options... >::value_type;
274
275 static void fixup_auto_uuid( details::attribute_access_arguments& args )
276 {
277 using characteristics_t = typename find_all_by_meta_type<
278 details::characteristic_meta_type,
279 ServiceOptions... >::type;
280
281 std::uint16_t char_index = index_of< characteristic< Options... >, characteristics_t >::value + 1;
282
283 static constexpr std::size_t uuid_offset = 3;
284
285 int index_low = static_cast< int >( args.buffer_offset ) - static_cast< int >( uuid_offset );
286 int index_high = static_cast< int >( args.buffer_offset ) - static_cast< int >( uuid_offset + 1 );
287
288 if ( index_low >= 0 && index_low < static_cast< int >( args.buffer_size ) )
289 args.buffer[ index_low ] ^= ( char_index & 0xff );
290
291 if ( index_high >= 0 && index_high < static_cast< int >( args.buffer_size ) )
292 args.buffer[ index_high ] ^= ( char_index >> 8 );
293 }
294
295 /*
296 * the characteristic decalarion consists of 3 parts: a Properties byte, two bytes value handle and 2 or 16 bytes UUID
297 */
298 static details::attribute_access_result char_declaration_access( details::attribute_access_arguments& args, std::size_t attribute_index )
299 {
300 if ( args.type != details::attribute_access_type::read )
301 return details::attribute_access_result::write_not_permitted;
302
303 static constexpr bool has_write_attribute_ =
304 value_type::has_write_access && !value_type::has_only_write_without_response;
305 static constexpr bool has_write_without_response_attribute =
306 value_type::has_only_write_without_response || value_type::has_write_without_response;
307
308 const std::uint8_t properties[] = {
309 static_cast< std::uint8_t >(
310 ( value_type::has_read_access ? bits( details::gatt_characteristic_properties::read ) : 0 ) |
311 ( has_write_attribute_ ? bits( details::gatt_characteristic_properties::write ) : 0 ) |
312 ( has_write_without_response_attribute ? bits( details::gatt_characteristic_properties::write_without_response ) : 0 ) |
313 ( value_type::has_notification ? bits( details::gatt_characteristic_properties::notify ) : 0 ) |
314 ( value_type::has_indication ? bits( details::gatt_characteristic_properties::indicate ) : 0 ) )
315 };
316
317 const std::uint16_t value_attribute_handle = handle_index_mapping< Server >::handle_by_index( attribute_index + 1 );
318 assert( value_attribute_handle != details::invalid_attribute_handle );
319
320 const std::uint8_t value_handle[] = {
321 static_cast< std::uint8_t >( value_attribute_handle & 0xff ),
322 static_cast< std::uint8_t >( value_attribute_handle >> 8 )
323 };
324
325 static constexpr auto data_size = sizeof( properties ) + sizeof( value_handle ) + sizeof( uuid::bytes );
326
327 if ( args.buffer_offset > data_size )
328 return details::attribute_access_result::invalid_offset;
329
330 details::scattered_read_access( args.buffer_offset, properties, value_handle, uuid::bytes, args.buffer, args.buffer_size );
331
332 if ( characteristic_or_service_uuid_t::auto_generated_uuid )
333 fixup_auto_uuid( args );
334
335 args.buffer_size = std::min< std::size_t >( data_size - args.buffer_offset, args.buffer_size );
336
337 return details::attribute_access_result::success;
338 }
339
340 static const attribute attr;
341 };
342
343 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename ... ServiceOptions, typename Server, typename ... Options >
344 const attribute generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions... > , Server, Options... >::attr {
345 bits( details::gatt_uuids::characteristic ),
346 &generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions... >, Server, Options... >::char_declaration_access
347 };
348
349 /*
350 * Characteristic Value
351 */
352 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
353 struct generate_attribute< std::tuple< characteristic_value_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
354 {
355 // the characterist value has two configurable aspects: the uuid and the value. The value is defined in the charcteristic
356 typedef typename characteristic_or_service_uuid< typename Service::uuid, Options... >::uuid uuid;
357 using char_t = characteristic< Options... >;
358 static constexpr bool requires_encryption = characteristic_requires_encryption< char_t, Service, Server >::value;
359
360 static const attribute attr;
361 };
362
363 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
364 const attribute generate_attribute< std::tuple< characteristic_value_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
365 uuid::is_128bit
366 ? bits( details::gatt_uuids::internal_128bit_uuid )
367 : uuid::as_16bit(),
368 &characteristic< Options... >::value_type::template characteristic_value_access< Server, ClientCharacteristicIndex, requires_encryption >
369 };
370
371 /*
372 * Characteristic User Description
373 */
374 template < const char* const Name, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
375 struct generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
376 {
377 static const attribute attr;
378
379 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
380 {
381 const std::size_t str_len = std::strlen( Name );
382
383 if ( args.buffer_offset > str_len )
384 return details::attribute_access_result::invalid_offset;
385
386 details::attribute_access_result result = attribute_access_result::write_not_permitted;
387
388 if ( args.type == attribute_access_type::read )
389 {
390 const std::size_t read_size = std::min( args.buffer_size, str_len - args.buffer_offset );
391
392 std::copy( Name + args.buffer_offset, Name + args.buffer_offset + read_size, args.buffer );
393
394 result = attribute_access_result::success;
395
396 args.buffer_size = read_size;
397 }
398
399 return result;
400 }
401
402 };
403
404 template < const char* const Name, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
405 const attribute generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
406 bits( gatt_uuids::characteristic_user_description ),
407 &generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
408 };
409
410 /*
411 * Client Characteristic Configuration Descriptor (CCCD)
412 */
413 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
414 struct generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
415 {
416 static const attribute attr;
417 using uuid = typename characteristic_or_service_uuid< typename Service::uuid, Options... >::uuid;
418
419 using char_t = characteristic< Options... >;
420 static constexpr bool requires_encryption = characteristic_requires_encryption< char_t, Service, Server >::value;
421
422 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
423 {
424 const auto security_result = details::encryption_requirements< requires_encryption >::check( args.connection_security );
425
426 if ( security_result != details::attribute_access_result::success )
427 return security_result;
428
429 static constexpr std::size_t flags_size = 2;
430 std::uint8_t buffer[ flags_size ];
431
432 if ( args.buffer_offset > flags_size )
433 return details::attribute_access_result::invalid_offset;
434
435 details::attribute_access_result result = attribute_access_result::write_not_permitted;
436
437 // currently, a lot of test code supplies an empty CCCDIndices list. In this case, use the ClientCharacteristicIndex
438 const std::size_t cccd_position_index = index_of< std::integral_constant< std::size_t, ClientCharacteristicIndex >, CCCDIndices >::value;
439 const std::size_t cccd_position = std::tuple_size< CCCDIndices >::value == 0
440 ? ClientCharacteristicIndex
441 : cccd_position_index;
442
443 if ( args.type == attribute_access_type::read )
444 {
445 write_16bit( &buffer[ 0 ], args.client_config.flags( cccd_position ) );
446
447 const std::size_t read_size = std::min( args.buffer_size, flags_size - args.buffer_offset );
448
449 std::copy( &buffer[ args.buffer_offset ], &buffer[ args.buffer_offset + read_size ], args.buffer );
450
451 result = attribute_access_result::success;
452
453 args.buffer_size = read_size;
454 }
455 else if ( args.type == attribute_access_type::write )
456 {
457 if ( args.buffer_size + args.buffer_offset > flags_size )
458 return details::attribute_access_result::invalid_attribute_value_length;
459
460 if ( args.buffer_offset == 0 )
461 {
462 const std::uint16_t old_config = args.client_config.flags( cccd_position );
463 std::uint8_t serialized_value[ flags_size ];
464 write_16bit( &serialized_value[ 0 ], old_config );
465
466 const std::size_t write_size = std::min( args.buffer_size, flags_size - args.buffer_offset );
467 std::copy( &args.buffer[ args.buffer_offset ], &args.buffer[ args.buffer_offset + write_size ], serialized_value );
468
469 args.client_config.flags( cccd_position, read_16bit( &serialized_value[ 0 ] ) );
470
471 using subscription_callback =
472 typename find_by_meta_type<
473 characteristic_subscription_call_back_meta_type,
474 Options..., default_on_characteristic_subscription >::type;
475
476 subscription_callback::template on_subscription< uuid >(
477 args.client_config.flags( cccd_position ),
478 *static_cast< Server* >( args.server ) );
479
480 if ( old_config != args.client_config.flags( cccd_position ) )
481 static_cast< Server* >( args.server )->notification_subscription_changed( args.client_config );
482 }
483
484 result = attribute_access_result::success;
485 }
486
487 return result;
488 }
489 };
490
491 template < typename ... AttrOptions, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
492 constexpr attribute generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
493 bits( gatt_uuids::client_characteristic_configuration ),
494 &generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
495 };
496
497 /*
498 * User Defined Descriptors
499 */
500 template < std::uint16_t UUID, const std::uint8_t* const Value, std::size_t Size, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
501 struct generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
502 {
503 static const attribute attr;
504
505 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
506 {
507 if ( args.type != attribute_access_type::read )
508 return attribute_access_result::write_not_permitted;
509
510 if ( args.buffer_offset > Size )
511 return details::attribute_access_result::invalid_offset;
512
513 const std::size_t read_size = std::min( args.buffer_size, Size - args.buffer_offset );
514
515 std::copy( Value + args.buffer_offset, Value + args.buffer_offset + read_size, args.buffer );
516 args.buffer_size = read_size;
517
518 return attribute_access_result::success;
519 }
520
521 };
522
523 template < std::uint16_t UUID, const std::uint8_t* const Value, std::size_t Size, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server, typename ... Options >
524 constexpr attribute generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
525 UUID,
526 &generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
527 };
528
529 template < typename CCCDIndices, typename ... Options >
530 struct generate_characteristic_attributes : generate_attributes<
531 std::tuple< Options... >,
532 std::tuple<
533 // The order of this list defines the order of the attributes in the characteristic
534 // Other attribute_handle<> and attribute_handles<> depend on this order.
535 characteristic_declaration_parameter,
536 characteristic_value_declaration_parameter,
537 client_characteristic_configuration_parameter,
538 characteristic_user_description_parameter,
539 descriptor_parameter
540 >,
541 CCCDIndices,
542 // force the existens of an characteristic declaration, even without Options with this meta_type
543 std::tuple< empty_meta_type< characteristic_declaration_parameter > >
544 > {};
545
546 template < typename ... Options >
547 struct count_characteristic_attributes
548 {
549 enum { number_of_client_configs =
550 count_by_meta_type< client_characteristic_configuration_parameter, Options... >::count != 0 ? 1 : 0 };
551
552 enum { number_of_user_descriptions =
553 count_by_meta_type< characteristic_user_description_parameter, Options... >::count != 0 ? 1 : 0 };
554
555 enum { number_of_descriptors =
556 count_by_meta_type< descriptor_parameter, Options...>::count };
557
558 enum { number_of_attributes = 2 + number_of_client_configs + number_of_user_descriptions + number_of_descriptors };
559 };
560
561 template < typename Characteristic >
562 struct sum_by_attributes
563 {
564 enum { value = Characteristic::number_of_attributes };
565 };
566
567 template < typename Characteristic >
568 struct sum_by_client_configs
569 {
570 enum { value = Characteristic::number_of_client_configs };
571 };
572
574 }
575}
576
577#endif
A characteristic is a typed value that is accessable by a GATT client hosted by a GATT server.
Definition: characteristic.hpp:160
adds a name to characteristic
Definition: characteristic.hpp:227
a 16-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:79
a 128-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:62