BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
notification_queue.hpp
1#ifndef BLUETOE_NOTIFICATION_QUEUE_HPP
2#define BLUETOE_NOTIFICATION_QUEUE_HPP
3
4#include <cstdint>
5#include <cstdlib>
6#include <utility>
7#include <cassert>
8#include <algorithm>
9#include <iterator>
10
11namespace bluetoe {
12
13 namespace details {
17 enum class notification_queue_entry_type {
19 empty,
21 notification,
23 indication
24 };
25
26 template < typename Size, int C >
27 class notification_queue_impl_base;
28
29 static constexpr std::size_t no_outstanding_indicaton = ~std::size_t{ 0 };
30 }
31
45 template < typename Sizes, class Mixin >
46 class notification_queue : public Mixin, details::notification_queue_impl_base< Sizes, 0 >
47 {
48 public:
50 using entry_type = details::notification_queue_entry_type;
58 template < class ... Args >
59 notification_queue( Args... mixin_arguments );
60
75 bool queue_notification( std::size_t index );
76
92 bool queue_indication( std::size_t index );
93
101
110 std::pair< details::notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation();
111
116
117 private:
118 using impl = details::notification_queue_impl_base< Sizes, 0 >;
119 std::size_t outstanding_confirmation_index_;
120 };
121
122 // impl
123 template < typename Sizes, class Mixin >
124 template < class ... Args >
126 : Mixin( mixin_arguments... )
127 , outstanding_confirmation_index_( details::no_outstanding_indicaton )
128 {
129 }
130
131 template < typename Sizes, class Mixin >
133 {
134 return impl::queue_notification( index );
135 }
136
137 template < typename Sizes, class Mixin >
139 {
140 return impl::queue_indication( index );
141 }
142
143 template < typename Sizes, class Mixin >
145 {
146 outstanding_confirmation_index_ = details::no_outstanding_indicaton;
147 }
148
149 template < typename Sizes, class Mixin >
150 std::pair< details::notification_queue_entry_type, std::size_t > notification_queue< Sizes, Mixin >::dequeue_indication_or_confirmation()
151 {
152 const auto result = impl::dequeue_indication_or_confirmation( 0, outstanding_confirmation_index_ );
153
154 return result;
155 }
156
157 template < typename Sizes, class Mixin >
159 {
160 outstanding_confirmation_index_ = details::no_outstanding_indicaton;
161 impl::clear_indications_and_confirmations();
162 }
163
164 namespace details
165 {
166 // C is introduced to make baseclasses with the very same Size not ambiguous
167 template < int Size, int C >
168 class notification_queue_impl
169 {
170 public:
171 notification_queue_impl()
172 {
173 clear_indications_and_confirmations();
174 }
175
176 bool queue_notification( std::size_t index )
177 {
178 assert( index < Size );
179 return add( index, notification_bit );
180 }
181
182 bool queue_indication( std::size_t index )
183 {
184 assert( index < Size );
185
186 return add( index, indication_bit );
187 }
188
189 std::pair< notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation( std::size_t offset, std::size_t& outstanding_confirmation )
190 {
191 bool ignore_first = true;
192
193 // loop over all entries in a circle
194 for ( std::size_t i = next_; ignore_first || i != next_; i = ( i + 1 ) % Size )
195 {
196 ignore_first = false;
197 auto entry = at( i );
198
199 if ( entry & indication_bit && outstanding_confirmation == no_outstanding_indicaton )
200 {
201 outstanding_confirmation = i + offset;
202 next_ = ( i + 1 ) % Size;
203 remove( i, indication_bit );
204 return { notification_queue_entry_type::indication, i + offset };
205 }
206 else if ( entry & notification_bit )
207 {
208 next_ = ( i + 1 ) % Size;
209 remove( i, notification_bit );
210 return { notification_queue_entry_type::notification, i + offset };
211 }
212
213 }
214
215 return { notification_queue_entry_type::empty, 0 };
216 }
217
218 void clear_indications_and_confirmations()
219 {
220 next_ = 0;
221 std::fill( std::begin( queue_ ), std::end( queue_ ), 0 );
222 }
223
224 private:
225 int at( std::size_t index )
226 {
227 const auto bit_offset = ( index * bits_per_characteristc ) % 8;
228 const auto byte_offset = index * bits_per_characteristc / 8;
229 assert( byte_offset < sizeof( queue_ ) / sizeof( queue_[ 0 ] ) );
230
231 return ( queue_[ byte_offset ] >> bit_offset ) & 0x03;
232 }
233
234 bool add( std::size_t index, int bits )
235 {
236 assert( bits & ( ( 1 << bits_per_characteristc ) -1 ) );
237 const auto bit_offset = ( index * bits_per_characteristc ) % 8;
238 const auto byte_offset = index * bits_per_characteristc / 8;
239 assert( byte_offset < sizeof( queue_ ) / sizeof( queue_[ 0 ] ) );
240
241 const bool result = ( queue_[ byte_offset ] & ( bits << bit_offset ) ) == 0;
242 queue_[ byte_offset ] |= bits << bit_offset;
243
244 return result;
245 }
246
247 void remove( std::size_t index, int bits )
248 {
249 assert( bits & ( ( 1 << bits_per_characteristc ) -1 ) );
250 const auto bit_offset = ( index * bits_per_characteristc ) % 8;
251 const auto byte_offset = index * bits_per_characteristc / 8;
252 assert( byte_offset < sizeof( queue_ ) / sizeof( queue_[ 0 ] ) );
253
254 queue_[ byte_offset ] &= ~( bits << bit_offset );
255 }
256
257 static constexpr std::size_t bits_per_characteristc = 2;
258
259 enum char_bits {
260 notification_bit = 0x01,
261 indication_bit = 0x02
262 };
263
264 std::size_t next_;
265 std::uint8_t queue_[ ( Size * bits_per_characteristc + 7 ) / 8 ];
266 };
267
271 template < int C >
272 class notification_queue_impl< 1, C >
273 {
274 public:
275 notification_queue_impl()
276 : state_( notification_queue_entry_type::empty )
277 {
278 }
279
280 bool queue_notification( std::size_t idx )
281 {
282 static_cast< void >( idx );
283 assert( idx == 0 );
284
285 const bool result = state_ == notification_queue_entry_type::empty;
286
287 if ( result )
288 state_ = notification_queue_entry_type::notification;
289
290 return result;
291 }
292
293 bool queue_indication( std::size_t idx )
294 {
295 static_cast< void >( idx );
296 assert( idx == 0 );
297 const bool result = state_ == notification_queue_entry_type::empty;
298
299 if ( result )
300 state_ = notification_queue_entry_type::indication;
301
302 return result;
303 }
304
305 std::pair< notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation( std::size_t offset, std::size_t& outstanding_confirmation )
306 {
307 const auto result = state_ == notification_queue_entry_type::notification || ( state_ == notification_queue_entry_type::indication && outstanding_confirmation == details::no_outstanding_indicaton )
308 ? std::pair< notification_queue_entry_type, std::size_t >{ static_cast< notification_queue_entry_type >( state_ ), offset }
309 : std::pair< notification_queue_entry_type, std::size_t >{ notification_queue_entry_type::empty, 0 };
310
311 if ( result.first == notification_queue_entry_type::indication )
312 outstanding_confirmation = offset;
313
314 if ( result.first != notification_queue_entry_type::empty )
315 state_ = notification_queue_entry_type::empty;
316
317 return result;
318 }
319
320 void clear_indications_and_confirmations()
321 {
322 state_ = notification_queue_entry_type::empty;
323 }
324 private:
325 notification_queue_entry_type state_;
326 };
327
328 template < int C >
329 class notification_queue_impl_base< std::tuple<>, C >
330 {
331 public:
332 bool queue_notification( std::size_t ) { return false; }
333 bool queue_indication( std::size_t ) { return false; }
334
335 std::pair< notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation( std::size_t, std::size_t )
336 {
337 return { notification_queue_entry_type::empty, 0 };
338 }
339
340 void clear_indications_and_confirmations() {}
341 };
342
343 template < int Size, class ...Ts, int C >
344 class notification_queue_impl_base< std::tuple< std::integral_constant< int, Size >, Ts... >, C >
345 : public notification_queue_impl_base< std::tuple< Ts... >, C + 1 >
346 , private notification_queue_impl< Size, C >
347 {
348 public:
349 using base = notification_queue_impl_base< std::tuple< Ts... >, C + 1 >;
350 using impl = notification_queue_impl< Size, C >;
351
352 bool queue_notification( std::size_t idx )
353 {
354 return idx < Size
355 ? impl::queue_notification( idx )
356 : base::queue_notification( idx - Size );
357 }
358
359 bool queue_indication( std::size_t idx )
360 {
361 return idx < Size
362 ? impl::queue_indication( idx )
363 : base::queue_indication( idx - Size );
364 }
365
366 std::pair< notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation( std::size_t offset, std::size_t& outstanding_confirmation )
367 {
368 auto result = impl::dequeue_indication_or_confirmation( offset, outstanding_confirmation );
369
370 if ( result.first != notification_queue_entry_type::empty )
371 {
372 return result;
373 }
374
375 result = base::dequeue_indication_or_confirmation( offset + Size, outstanding_confirmation );
376
377 return result;
378 }
379
380 void clear_indications_and_confirmations()
381 {
382 impl::clear_indications_and_confirmations();
383 base::clear_indications_and_confirmations();
384 }
385 };
386
387 } // namespace details
388
389} // namespace bluetoe
390
391#endif // include guard
class responsible to keep track of those characteristics that have outstanding notifications or indic...
Definition: notification_queue.hpp:47
std::pair< details::notification_queue_entry_type, std::size_t > dequeue_indication_or_confirmation()
return a next notification or indication to be send.
Definition: notification_queue.hpp:150
bool queue_indication(std::size_t index)
queue the indexed characteristic for indication
Definition: notification_queue.hpp:138
void clear_indications_and_confirmations()
removes all entries from the queue
Definition: notification_queue.hpp:158
notification_queue(Args... mixin_arguments)
constructs an empty notification_queue_impl
Definition: notification_queue.hpp:125
bool queue_notification(std::size_t index)
queue the indexed characteristic for notification
Definition: notification_queue.hpp:132
void indication_confirmed()
to be called, when a ATT Handle Value Confirmation was received.
Definition: notification_queue.hpp:144