BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
attribute.hpp
1#ifndef BLUETOE_ATTRIBUTE_HPP
2#define BLUETOE_ATTRIBUTE_HPP
3
5#include <bluetoe/client_characteristic_configuration.hpp>
6#include <bluetoe/pairing_status.hpp>
7#include <bluetoe/attribute_handle.hpp>
8
9#include <cstdint>
10#include <cstddef>
11#include <cassert>
12
13namespace bluetoe {
14namespace details {
15
16 /*
17 * Attribute and accessing an attribute
18 */
19
20 enum class attribute_access_result : std::int_fast16_t {
21 // Accessing the attribute was successfully
22 success = 0x00,
23
24 // here goes the ATT return codes
25 invalid_offset = 0x07,
27 read_not_permitted = 0x02,
29 attribute_not_long = 0x0b,
33
34 // returned when access type is compare_128bit_uuid and the attribute contains a 128bit uuid and
35 // the buffer in attribute_access_arguments is equal to the contained uuid.
36 uuid_equal = 0x100,
37 value_equal
38 };
39
40 enum class attribute_access_type {
41 read,
42 write,
43 compare_128bit_uuid,
44 compare_value
45 };
46
47 struct attribute_access_arguments
48 {
49 attribute_access_type type;
50 std::uint8_t* buffer;
51 std::size_t buffer_size;
52 std::size_t buffer_offset;
53 client_characteristic_configuration client_config;
54 connection_security_attributes connection_security;
55 void* server;
56
57 template < std::size_t N >
58 static constexpr attribute_access_arguments read(
59 std::uint8_t(&buffer)[N],
60 std::size_t offset,
61 const client_characteristic_configuration& cc = client_characteristic_configuration(),
62 const connection_security_attributes& cs = connection_security_attributes() )
63 {
64 return attribute_access_arguments{
65 attribute_access_type::read,
66 &buffer[ 0 ],
67 N,
68 offset,
69 cc,
70 cs,
71 nullptr
72 };
73 }
74
75 static attribute_access_arguments read(
76 std::uint8_t* begin, std::uint8_t* end, std::size_t offset,
77 const client_characteristic_configuration& cc,
78 const connection_security_attributes& cs,
79 void* server )
80 {
81 assert( end >= begin );
82
83 return attribute_access_arguments{
84 attribute_access_type::read,
85 begin,
86 static_cast< std::size_t >( end - begin ),
87 offset,
88 cc,
89 cs,
90 server
91 };
92 }
93
94 template < std::size_t N >
95 static constexpr attribute_access_arguments write( const std::uint8_t(&buffer)[N], std::size_t offset = 0,
96 const client_characteristic_configuration& cc = client_characteristic_configuration(),
97 const connection_security_attributes& cs = connection_security_attributes() )
98 {
99 return attribute_access_arguments{
100 attribute_access_type::write,
101 const_cast< std::uint8_t* >( &buffer[ 0 ] ),
102 N,
103 offset,
104 cc,
105 cs,
106 nullptr
107 };
108 }
109
110 static attribute_access_arguments write( const std::uint8_t* begin, const std::uint8_t* end, std::size_t offset,
111 const client_characteristic_configuration& cc,
112 const connection_security_attributes& cs,
113 void* server )
114 {
115 assert( end >= begin );
116
117 return attribute_access_arguments{
118 attribute_access_type::write,
119 const_cast< std::uint8_t* >( begin ),
120 static_cast< std::size_t >( end - begin ),
121 offset,
122 cc,
123 cs,
124 server
125 };
126 }
127
128 static constexpr attribute_access_arguments check_write( void* server )
129 {
130 return attribute_access_arguments{
131 attribute_access_type::write,
132 0,
133 0,
134 0,
135 client_characteristic_configuration(),
136 connection_security_attributes(),
137 server
138 };
139 }
140
141 static constexpr attribute_access_arguments compare_128bit_uuid( const std::uint8_t* uuid )
142 {
143 return attribute_access_arguments{
144 attribute_access_type::compare_128bit_uuid,
145 const_cast< std::uint8_t* >( uuid ),
146 16u,
147 0,
148 client_characteristic_configuration(),
149 connection_security_attributes(),
150 nullptr
151 };
152 }
153
154 static attribute_access_arguments compare_value( const std::uint8_t* begin, const std::uint8_t* end, void* )
155 {
156 assert( end >= begin );
157
158 return attribute_access_arguments{
159 attribute_access_type::compare_value,
160 const_cast< std::uint8_t* >( begin ),
161 static_cast< std::size_t >( end - begin ),
162 0,
163 client_characteristic_configuration(),
164 connection_security_attributes(),
165 nullptr
166 };
167 }
168 };
169
170 typedef attribute_access_result ( *attribute_access )( attribute_access_arguments&, std::size_t attribute_index );
171
172 /*
173 * An attribute is an uuid combined with a mean of how to access the attributes
174 *
175 * design decisions to _not_ use pointer to staticaly allocated virtual objects:
176 * - makeing access a pointer to a virtual base class would result in storing a pointer that points to a pointer (vtable) to list with a bunch of functions.
177 * - it's most likely that most attributes will not have any mutable data.
178 * attribute contains only one function that takes a attribute_access_type to save memory and in the expectation that there are only a few different combination of
179 * access functions.
180 */
181 struct attribute {
182 // all uuids used by GATT are 16 bit UUIDs (except for Characteristic Value Declaration for which the value internal_128bit_uuid is used, if the UUID is 128bit long)
183 std::uint16_t uuid;
184 attribute_access access;
185 };
186
187 /*
188 * Given that T is a tuple with elements that implement attribute_at< std::size_t, Service >() and number_of_attributes, the type implements
189 * attribute_at() for a list of attribute lists.
190 */
191 template < typename T, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server >
192 struct attribute_at_list;
193
194 template < typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename Service, typename Server >
195 struct attribute_at_list< std::tuple<>, CCCDIndices, ClientCharacteristicIndex, Service, Server >
196 {
197 static details::attribute attribute_at( std::size_t )
198 {
199 assert( !"index out of bound" );
200 return details::attribute();
201 }
202 };
203
204 template <
205 typename T,
206 typename ...Ts,
207 typename CCCDIndices,
208 std::size_t ClientCharacteristicIndex,
209 typename Service,
210 typename Server >
211 struct attribute_at_list< std::tuple< T, Ts... >, CCCDIndices, ClientCharacteristicIndex, Service, Server >
212 {
213 static details::attribute attribute_at( std::size_t index )
214 {
215 if ( index < T::number_of_attributes )
216 return T::template attribute_at< CCCDIndices, ClientCharacteristicIndex, Service, Server >( index );
217
218 typedef details::attribute_at_list< std::tuple< Ts... >, CCCDIndices, ClientCharacteristicIndex + T::number_of_client_configs, Service, Server > remaining_characteristics;
219
220 return remaining_characteristics::attribute_at( index - T::number_of_attributes );
221 }
222 };
223
224 /*
225 * Iterating the list of services is the same, but needs less parameters
226 */
227 template < typename Services, typename Server, typename CCCDIndices, std::size_t ClientCharacteristicIndex = 0, typename AllServices = Services >
228 struct attribute_from_service_list;
229
230 template < typename Server, typename CCCDIndices, std::size_t ClientCharacteristicIndex, typename AllServices >
231 struct attribute_from_service_list< std::tuple<>, Server, CCCDIndices, ClientCharacteristicIndex, AllServices >
232 {
233 static details::attribute attribute_at( std::size_t )
234 {
235 assert( !"index out of bound" );
236 return details::attribute();
237 }
238 };
239
240 template <
241 typename T,
242 typename ...Ts,
243 typename Server,
244 typename CCCDIndices,
245 std::size_t ClientCharacteristicIndex,
246 typename AllServices >
247 struct attribute_from_service_list< std::tuple< T, Ts... >, Server, CCCDIndices, ClientCharacteristicIndex, AllServices >
248 {
249 static details::attribute attribute_at( std::size_t index )
250 {
251 if ( index < T::number_of_attributes )
252 return T::template attribute_at< CCCDIndices, ClientCharacteristicIndex, AllServices, Server >( index );
253
254 typedef details::attribute_from_service_list<
255 std::tuple< Ts... >,
256 Server,
257 CCCDIndices,
258 ClientCharacteristicIndex + T::number_of_client_configs,
259 AllServices > remaining_characteristics;
260
261 return remaining_characteristics::attribute_at( index - T::number_of_attributes );
262 }
263 };
264
268 enum class notification_type {
269 notification,
270 indication,
271 confirmation
272 };
273
277 class notification_data
278 {
279 public:
280 notification_data()
281 : attribute_table_index_( details::invalid_attribute_index )
282 , client_characteristic_configuration_index_( 0 )
283 {
284 assert( !valid() );
285 }
286
287 notification_data( std::size_t value_attribute_index, std::size_t client_characteristic_configuration_index )
288 : attribute_table_index_( value_attribute_index )
289 , client_characteristic_configuration_index_( client_characteristic_configuration_index )
290 {
291 assert( valid() );
292 }
293
294 bool valid() const
295 {
296 return attribute_table_index_ != details::invalid_attribute_index;
297 }
298
302 std::size_t attribute_table_index() const
303 {
304 return attribute_table_index_;
305 }
306
310 std::size_t client_characteristic_configuration_index() const
311 {
312 return client_characteristic_configuration_index_;
313 }
314
315 /*
316 * For testing
317 */
318 void clear()
319 {
320 attribute_table_index_ = details::invalid_attribute_index;
321 client_characteristic_configuration_index_ = 0;
322 }
323
324 private:
325 std::size_t attribute_table_index_;
326 std::size_t client_characteristic_configuration_index_;
327 };
328
329 /*
330 * Given a list of characteristics, find the data required for notification
331 */
332 template < typename CharacteristicList, typename UUID >
333 struct find_characteristic_data_by_uuid_in_characteristic_list;
334
335 template < typename UUID >
336 struct find_characteristic_data_by_uuid_in_characteristic_list< std::tuple<>, UUID >
337 {
338 typedef details::no_such_type type;
339 };
340
341 template <
342 typename Characteristic,
343 typename ... Characteristics,
344 typename UUID >
345 struct find_characteristic_data_by_uuid_in_characteristic_list< std::tuple< Characteristic, Characteristics...>, UUID >
346 {
347 using found = std::is_same< typename Characteristic::configured_uuid, UUID >;
348
349 using next = typename find_characteristic_data_by_uuid_in_characteristic_list<
350 std::tuple< Characteristics... >,
351 UUID >::type;
352
353 struct result
354 {
355 static constexpr bool has_indication = Characteristic::value_type::has_indication;
356 static constexpr bool has_notification = Characteristic::value_type::has_notification;
357 using characteristic_t = Characteristic;
358 };
359
360 typedef typename details::select_type<
361 found::value,
362 result,
363 next
364 >::type type;
365 };
366
367 /*
368 * Given a list of services and a UUID, find the data required for notification
369 */
370 template < typename ServiceList, typename UUID >
371 struct find_characteristic_data_by_uuid_in_service_list;
372
373 template < typename UUID >
374 struct find_characteristic_data_by_uuid_in_service_list< std::tuple<>, UUID >
375 {
376 typedef details::no_such_type type;
377 };
378
379 struct wrap_search_in_service_list {
380 template <
381 typename UUID,
382 typename ... Services >
383 using f = typename find_characteristic_data_by_uuid_in_service_list<
384 std::tuple< Services... >, UUID >::type;
385 };
386
387 template < typename Result >
388 struct wrap_result_found_in_service {
389 template <
390 typename UUID,
391 typename ... Services >
392 using f = Result;
393 };
394
395 template <
396 typename Service,
397 typename ... Services,
398 typename UUID >
399 struct find_characteristic_data_by_uuid_in_service_list< std::tuple< Service, Services...>, UUID >
400 {
401 // The searched UUID is either in first Service or in the remaining service. There is no point in searching through
402 // the remaining service, if the first service contains the characteristic already
403 using c_type = typename find_characteristic_data_by_uuid_in_characteristic_list<
404 typename Service::characteristics, UUID >::type;
405
406 using next_path =
407 typename select_type<
408 std::is_same< c_type, no_such_type >::value,
409 wrap_search_in_service_list,
410 wrap_result_found_in_service< c_type >
411 >::type;
412
413 using type = typename next_path::template f<
414 UUID,
415 Services... >;
416 };
417
418
419 inline attribute_access_result attribute_value_read_access( attribute_access_arguments& args, const std::uint8_t* memory, std::size_t size )
420 {
421 if ( args.type == attribute_access_type::compare_value )
422 {
423 return size == args.buffer_size && std::equal( memory, memory + size, args.buffer )
424 ? attribute_access_result::value_equal
425 : attribute_access_result::read_not_permitted;
426 }
427
428 if ( args.buffer_offset > size )
429 return details::attribute_access_result::invalid_offset;
430
431 args.buffer_size = std::min< std::size_t >( args.buffer_size, size - args.buffer_offset );
432 const std::uint8_t* const ptr = memory + args.buffer_offset;
433
434 std::copy( ptr, ptr + args.buffer_size, args.buffer );
435
436 return details::attribute_access_result::success;
437 }
438
439 inline attribute_access_result attribute_value_read_only_access( attribute_access_arguments& args, const std::uint8_t* memory, std::size_t size )
440 {
441 if ( args.type != attribute_access_type::read && args.type != attribute_access_type::compare_value )
442 return attribute_access_result::write_not_permitted;
443
444 return attribute_value_read_access( args, memory, size );
445 }
446
447}
448}
449
450#endif
wrap< typename select_type_impl< Select >::template f< A, B > > select_type
Select A or B by Select. If Select == true, the result is A; B otherwise.
Definition: meta_tools.hpp:38
@ insufficient_authentication
Definition: codes.hpp:179
@ request_not_supported
Definition: codes.hpp:184
@ invalid_offset
Definition: codes.hpp:189
@ success
Definition: codes.hpp:154
@ write_not_permitted
Definition: codes.hpp:169
@ attribute_not_long
Definition: codes.hpp:209
@ invalid_attribute_value_length
Definition: codes.hpp:219
@ read_not_permitted
Definition: codes.hpp:164
@ insufficient_encryption
Definition: codes.hpp:230