BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
bootloader.hpp
Go to the documentation of this file.
1#ifndef BLUETOE_SERVICES_BOOTLOADER_HPP
2#define BLUETOE_SERVICES_BOOTLOADER_HPP
3
4#include <bluetoe/service.hpp>
5#include <bluetoe/mixin.hpp>
6#include <algorithm>
7
17namespace bluetoe
18{
19 namespace bootloader {
20
22 namespace details {
23 struct handler_meta_type {};
24 struct memory_region_meta_type {};
25 struct page_size_meta_type {};
26 }
32 template < std::size_t PageSize >
33 struct page_size
34 {
36 typedef details::page_size_meta_type meta_type;
37 static constexpr std::size_t value = PageSize;
39 };
40
47 template < std::uintptr_t Start, std::uintptr_t End >
48 struct memory_region {};
49
53 template < typename ... Regions >
54 struct white_list;
55
57 template < std::uintptr_t Start, std::uintptr_t End, typename ... Regions >
58 struct white_list< memory_region< Start, End >, Regions ... >
59 {
60 static bool acceptable( std::uintptr_t start, std::uintptr_t end )
61 {
62 return ( start >= Start && end <= End )
64 }
65
66 typedef details::memory_region_meta_type meta_type;
67 };
68
69 template <>
70 struct white_list<>
71 {
72 static bool acceptable( std::uintptr_t, std::uintptr_t )
73 {
74 return false;
75 }
76
77 typedef details::memory_region_meta_type meta_type;
78 };
84 template < typename UserHandler >
85 struct handler {
87 typedef UserHandler user_handler;
88 typedef details::handler_meta_type meta_type;
90 };
91
95 template < class Server >
96 void end_flash( Server& srv )
97 {
98 srv.end_flash( srv );
99 }
100
105
110
115
120
124 enum att_error_codes : std::uint8_t {
125 /*
126 * attempt to write or read data to or from the bootloader, while no operation is in progress
127 */
128 no_operation_in_progress = bluetoe::error_codes::application_error_start,
129 invalid_opcode,
130 invalid_state,
131 buffer_overrun_attempt
132 };
133
137 enum class error_codes : std::uint8_t {
141 success,
142
147 };
148
150 namespace details {
151
156 template < std::size_t PageSize >
157 class flash_buffer
158 {
159 public:
160 flash_buffer()
161 : state_( idle )
162 , ptr_( 0 )
163 {
164 }
165
166 std::uint32_t free_size() const
167 {
168 return state_ == filling
169 ? PageSize - ptr_
170 : 0;
171 }
172
173 template < class Handler >
174 void set_start_address( std::uintptr_t address, Handler& h, std::uint32_t checksum, std::uint16_t cons )
175 {
176 assert( state_ == idle );
177 state_ = filling;
178 ptr_ = address % PageSize;
179 addr_ = address - ptr_;
180 crc_ = checksum;
181 consecutive_ = cons;
182
183 h.read_mem( addr_, ptr_, &buffer_[ 0 ] );
184 }
185
186 template < class Handler >
187 std::size_t write_data( std::size_t write_size, const std::uint8_t* value, Handler& h )
188 {
189 assert( state_ == filling );
190 assert( ptr_ != PageSize );
191
192 const std::size_t copy_size = std::min( PageSize - ptr_, write_size );
193 std::copy( value, value + copy_size, &buffer_[ ptr_ ] );
194 crc_ = h.checksum32( &buffer_[ ptr_ ], copy_size, crc_ );
195
196 ptr_ += copy_size;
197
198 if ( ptr_ == PageSize )
199 {
200 flush( h );
201 }
202
203 return copy_size;
204 }
205
206 template < class Handler >
207 bool flush( Handler& h )
208 {
209 if ( state_ != filling || ptr_ == 0 )
210 return false;
211
212 state_ = flashing;
213
214 // no unnessary calls to user handler
215 if ( PageSize != ptr_ )
216 h.read_mem( addr_ + ptr_, PageSize - ptr_, &buffer_[ ptr_ ] );
217
218 h.start_flash( addr_, &buffer_[ 0 ], PageSize );
219
220 return true;
221 }
222
223 std::uint32_t crc() const
224 {
225 return crc_;
226 }
227
228 void free()
229 {
230 state_ = idle;
231 ptr_ = 0;
232 }
233
234 bool empty() const
235 {
236 return state_ == idle;
237 }
238
239 std::uint16_t consecutive() const
240 {
241 return consecutive_;
242 }
243 private:
244 enum {
245 idle,
246 filling,
247 flashing
248 } state_;
249
250 std::uintptr_t addr_;
251 std::size_t ptr_;
252 std::uint32_t crc_;
253 std::uint8_t buffer_[ PageSize ];
254 std::uint16_t consecutive_;
255 };
256
257 enum opcode : std::uint8_t
258 {
259 opc_get_version,
260 opc_get_crc,
261 opc_get_sizes,
262 opc_start_flash,
263 opc_stop_flash,
264 opc_flush,
265 opc_start,
266 opc_reset,
267 opc_read,
268 undefined_opcode = 0xff
269 };
270
271 enum data_code : std::uint8_t
272 {
273 success = 1,
274 };
275
276 template < typename UserHandler, typename MemRegions, std::size_t PageSize >
277 class controller : public UserHandler
278 {
279 public:
280 controller()
281 : opcode( undefined_opcode )
282 , start_address( 0 )
283 , end_address( 0 )
284 , check_sum( 0 )
285 , in_flash_mode( false )
286 , next_buffer_( 0 )
287 , used_buffer_( 0 )
288 , consecutive_( 0 )
289 {
290 }
291
292 std::uintptr_t read_address( const std::uint8_t* rend )
293 {
294 std::uintptr_t result = 0;
295 for ( const std::uint8_t* rbegin = rend + sizeof( std::uint8_t* ); rbegin != rend; --rbegin )
296 {
297 result = result << 8;
298 result |= *( rbegin -1 );
299 }
300
301 return result;
302 }
303
304 std::pair< std::uint8_t, bool > bootloader_write_control_point( std::size_t write_size, const std::uint8_t* value )
305 {
306 if ( write_size < 1 )
307 return std::make_pair( bluetoe::error_codes::invalid_attribute_value_length, false );
308
309 opcode = *value;
310
311 switch ( opcode )
312 {
313 case opc_get_version:
314 case opc_get_sizes:
315 case opc_stop_flash:
316 {
317 if ( write_size != 1 )
319
320 in_flash_mode = false;
321 next_buffer_ = 0;
322 used_buffer_ = 0;
323
324 for ( auto& buffer : buffers_ )
325 buffer.free();
326 }
327 break;
328 case opc_get_crc:
329 {
330 if ( write_size != 1 + 2 * sizeof( std::uint8_t* ) )
332
333 start_address = read_address( value +1 );
334 const std::uintptr_t end_address = read_address( value +1 + sizeof( std::uint8_t* ) );
335
336 if ( start_address > end_address || !MemRegions::acceptable( start_address,end_address ) )
337 return request_error( bluetoe::error_codes::invalid_offset );
338
339 check_sum = this->public_checksum32( start_address, end_address - start_address );
340 }
341 break;
342 case opc_start_flash:
343 {
344 if ( write_size != 1 + sizeof( std::uint8_t* ) )
346
347 start_address = read_address( value +1 );
348 check_sum = this->checksum32( start_address );
349 consecutive_ = 0;
350 next_buffer_ = 0;
351 used_buffer_ = 0;
352 in_flash_mode = true;
353
354 if ( !MemRegions::acceptable( start_address, start_address ) )
355 return request_error( bluetoe::error_codes::invalid_offset );
356
357 for ( auto& buffer : buffers_ )
358 buffer.free();
359
360 buffers_[next_buffer_].set_start_address( start_address, *this, check_sum, consecutive_ );
361 }
362 break;
363 case opc_flush:
364 {
365 if ( !in_flash_mode )
366 return request_error( invalid_state );
367
368 if ( !buffers_[next_buffer_].flush( *this ) )
369 return request_error( invalid_state );
370
371 return std::pair< std::uint8_t, bool >{ bluetoe::error_codes::success, true };
372 }
373 break;
374 case opc_start:
375 {
376 if ( write_size != 1 + sizeof( std::uint8_t* ) )
378
379 const std::uintptr_t start_address = read_address( value +1 );
380
381 this->run( start_address );
382 }
383 break;
384 case opc_reset:
385 {
386 if ( write_size != 1 )
388
389 this->reset();
390 }
391 break;
392 case opc_read:
393 {
394 error = error_codes::success;
395 start_address = read_address( value +1 );
396 end_address = read_address( value +1 + sizeof( std::uint8_t* ) );
397 check_sum = this->checksum32( start_address );
398
399 if ( start_address > end_address || !MemRegions::acceptable( start_address,end_address ) )
400 return request_error( bluetoe::error_codes::invalid_offset );
401
402 if ( start_address != end_address )
403 {
404 this->data_indication_call_back();
405
406 return std::pair< std::uint8_t, bool >{ bluetoe::error_codes::success, false };
407 }
408 }
409 break;
410 default:
411 return std::pair< std::uint8_t, bool >{ att_error_codes::invalid_opcode, false };
412 }
413
414 return std::pair< std::uint8_t, bool >{ bluetoe::error_codes::success, true };
415 }
416
417 std::uint8_t bootloader_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
418 {
419 assert( read_size >= 20 );
420 *out_buffer = opcode;
421
422 switch ( opcode )
423 {
424 case opc_get_version:
425 {
426 const std::pair< const std::uint8_t*, std::size_t > version = this->get_version();
427 out_size = std::min( read_size, version.second + 1 );
428
429 *out_buffer = opc_get_version;
430 std::copy( version.first, version.first + out_size - 1, out_buffer + 1 );
431 }
432 break;
433 case opc_get_crc:
434 {
435 bluetoe::details::write_32bit( out_buffer + 1, check_sum );
436 out_size = sizeof( std::uint8_t ) + sizeof( std::uint32_t );
437 }
438 break;
439 case opc_get_sizes:
440 {
441 std::uint8_t* out = out_buffer;
442 ++out;
443 *out = sizeof( std::uint8_t* );
444 ++out;
445 out = bluetoe::details::write_32bit( out, PageSize );
446 out = bluetoe::details::write_32bit( out, number_of_concurrent_flashs );
447
448 out_size = out - out_buffer;
449 }
450 break;
451 case opc_start_flash:
452 {
453 std::uint8_t* out = out_buffer;
454 ++out;
455 *out = read_size + 3;
456 ++out;
457 out = bluetoe::details::write_32bit( out, buffers_[next_buffer_].crc() );
458
459 out_size = out - out_buffer;
460 }
461 break;
462 case opc_stop_flash:
463 out_size = 1;
464 break;
465 case opc_flush:
466 {
467 std::uint8_t* out = out_buffer;
468 ++out;
469 out = bluetoe::details::write_32bit( out, buffers_[next_buffer_].crc() );
470 out = bluetoe::details::write_16bit( out, buffers_[next_buffer_].consecutive() );
471
472 out_size = out - out_buffer;
473 }
474 break;
475 case opc_read:
476 {
477 std::uint8_t* out = out_buffer;
478 ++out;
479 out = bluetoe::details::write_32bit( out, check_sum );
480 *out = static_cast< std::uint8_t >( error );
481 ++out;
482
483 out_size = out - out_buffer;
484 }
485 break;
486 }
487
489 }
490
491 std::uint8_t bootloader_write_data( std::size_t write_size, const std::uint8_t* value )
492 {
493 if ( !in_flash_mode )
494 return no_operation_in_progress;
495
496 if ( write_size == 0 )
498
499 if ( buffers_[ next_buffer_ ].free_size() == 0 && !find_next_buffer( start_address ) )
500 return buffer_overrun_attempt;
501
502 while ( write_size )
503 {
504 const std::size_t moved = buffers_[ next_buffer_ ].write_data( write_size, value, *this );
505
506 value += moved;
507 write_size -= moved;
508 start_address += moved;
509
510 if ( write_size && !find_next_buffer( start_address ) )
511 return buffer_overrun_attempt;
512 }
513
515 }
516
517 std::uint8_t bootloader_read_data( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
518 {
519 if ( opcode == opc_read )
520 {
521 out_size = std::min( read_size, end_address - start_address );
522
523 error = this->public_read_mem( start_address, out_size, out_buffer );
524
525 if ( error == error_codes::success )
526 {
527 check_sum = this->checksum32( out_buffer, out_size, check_sum );
528 start_address += out_size;
529 }
530 else
531 {
532 out_size = 0;
533 }
534
535 if ( start_address == end_address || error != error_codes::success )
536 {
537 this->control_point_notification_call_back();
538 }
539 else
540 {
541 this->data_indication_call_back();
542 }
543 }
544
546 }
547
548 std::uint8_t bootloader_progress_data( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
549 {
550 buffers_[used_buffer_].free();
551 out_size = 7;
552 assert( read_size >= out_size );
553
554 out_buffer = bluetoe::details::write_32bit( out_buffer, buffers_[used_buffer_].crc() );
555 out_buffer = bluetoe::details::write_16bit( out_buffer, buffers_[used_buffer_].consecutive() );
556
557 // trick: that's the link layers MTU size:
558 *out_buffer = read_size + 3;
559 ++out_buffer;
560
561 used_buffer_ = ( used_buffer_ + 1 ) % number_of_concurrent_flashs;
562
564 }
565
566 template < class Server >
567 void end_flash( Server& server )
568 {
569 server.template notify< progress_uuid >();
570 }
571
572 template < class Server >
573 void bootloader_control_point_notification( Server& server )
574 {
575 server.template notify< control_point_uuid >();
576 }
577
578 template < class Server >
579 void bootloader_data_indication( Server& server )
580 {
581 server.template indicate< data_uuid >();
582 }
583
584 private:
585 std::uint32_t free_size() const
586 {
587 std::uint32_t result = 0;
588
589 for ( const auto& b : buffers_ )
590 result += b.free_size();
591
592 return result;
593 }
594
595 bool find_next_buffer( std::size_t start_address )
596 {
597 const auto next = ( next_buffer_ + 1 ) % number_of_concurrent_flashs;
598
599 if ( buffers_[ next ].empty() )
600 {
601 ++consecutive_;
602 buffers_[ next ].set_start_address( start_address, *this, buffers_[ next_buffer_ ].crc(), consecutive_ );
603 next_buffer_ = next;
604
605 return true;
606 }
607
608 return false;
609 }
610
611 std::pair< std::uint8_t, bool > request_error( std::uint8_t code )
612 {
613 opcode = undefined_opcode;
614 in_flash_mode = false;
615
616 return std::pair< std::uint8_t, bool >{ code, false };
617 }
618
619 std::uint8_t opcode;
620 std::uintptr_t start_address;
621 std::uintptr_t end_address;
622 error_codes error;
623 std::uint32_t check_sum;
624 bool in_flash_mode;
625
626 static constexpr std::size_t number_of_concurrent_flashs = 2;
627 unsigned next_buffer_;
628 unsigned used_buffer_;
629 std::uint16_t consecutive_;
630 flash_buffer< PageSize > buffers_[number_of_concurrent_flashs];
631 };
632
633 template < typename ... Options >
634 struct calculate_service {
635 using page_size = typename bluetoe::details::find_by_meta_type< page_size_meta_type, Options... >::type;
636
637 static_assert( !std::is_same< bluetoe::details::no_such_type, page_size >::value,
638 "Please use page_size<> to define the block size of the flash." );
639
640 using mem_regions = typename bluetoe::details::find_by_meta_type< memory_region_meta_type, Options... >::type;
641
642 static_assert( !std::is_same< bluetoe::details::no_such_type, mem_regions >::value,
643 "Please use white_list or black_list, to define accessable memory regions." );
644
645 using user_handler = typename bluetoe::details::find_by_meta_type< handler_meta_type, Options... >::type;
646
647 static_assert( !std::is_same< bluetoe::details::no_such_type, user_handler >::value,
648 "To use the bootloader, please provide a handler<> that fullfiles the requirements documented with bootloader_handler_prototype." );
649
650 using implementation = controller< typename user_handler::user_handler, mem_regions, page_size::value >;
651
652 using type = bluetoe::service<
657 implementation, &implementation::bootloader_write_control_point, bluetoe::bootloader::control_point_uuid
658 >,
660 implementation, &implementation::bootloader_read_control_point
661 >,
665 >,
669 implementation, &implementation::bootloader_write_data
670 >,
672 implementation, &implementation::bootloader_read_data
673 >,
677 >,
681 implementation, &implementation::bootloader_progress_data
682 >,
685 >,
687 >;
688 };
689 }
690
691 template < typename UserHandler, typename MemRegions, std::size_t PageSize >
692 using controller = details::controller< UserHandler, MemRegions, PageSize >;
700 {
701 public:
707 bootloader::error_codes start_flash( std::uintptr_t address, const std::uint8_t* values, std::size_t size );
708
712 bootloader::error_codes run( std::uintptr_t start_addr );
713
718
723 std::pair< const std::uint8_t*, std::size_t > get_version();
724
728 void read_mem( std::uintptr_t address, std::size_t size, std::uint8_t* destination );
729
733 std::uint32_t checksum32( std::uintptr_t start_addr, std::size_t size );
734
738 std::uint32_t checksum32( const std::uint8_t* start_addr, std::size_t size, std::uint32_t old_crc );
739
743 std::uint32_t checksum32( std::uintptr_t start_addr );
744
753 bootloader::error_codes public_read_mem( std::uintptr_t address, std::size_t size, std::uint8_t* destination );
754
763 std::uint32_t public_checksum32( std::uintptr_t start_addr, std::size_t size );
764
769
774 };
775 }
776
780 template < typename ... Options >
781 using bootloader_service = typename bootloader::details::calculate_service< Options... >::type;
782
783}
784
785#endif
void end_flash(Server &srv)
inform the bootloader, that an asynchrone flash operations is finished
Definition: bootloader.hpp:96
error_codes
range of error codes that can be used by the user handler to indicate the cause of an error
Definition: bootloader.hpp:137
att_error_codes
error codes that are used by the bootloader on the ATT layer
Definition: bootloader.hpp:124
typename bootloader::details::calculate_service< Options... >::type bootloader_service
Implementation of a bootloader service.
Definition: bootloader.hpp:781
Prototype for a handler, that adapts the bootloader service to the actual hardware.
Definition: bootloader.hpp:700
void data_indication_call_back()
technical required function, that have to call bootloader_data_indication(), with the instance of the...
std::uint32_t public_checksum32(std::uintptr_t start_addr, std::size_t size)
version of checksum function, that will be directly called by the execution of the Get CRC procedure.
void control_point_notification_call_back()
technical required function, that have to call bootloader_control_point_notification(),...
std::uint32_t checksum32(std::uintptr_t start_addr)
std::pair< const std::uint8_t *, std::size_t > get_version()
bootloader::error_codes start_flash(std::uintptr_t address, const std::uint8_t *values, std::size_t size)
bootloader::error_codes run(std::uintptr_t start_addr)
std::uint32_t checksum32(const std::uint8_t *start_addr, std::size_t size, std::uint32_t old_crc)
std::uint32_t checksum32(std::uintptr_t start_addr, std::size_t size)
bootloader::error_codes public_read_mem(std::uintptr_t address, std::size_t size, std::uint8_t *destination)
void read_mem(std::uintptr_t address, std::size_t size, std::uint8_t *destination)
A characteristic is a typed value that is accessable by a GATT client hosted by a GATT server.
Definition: characteristic.hpp:160
a 128-Bit UUID used to identify a service.
Definition: service.hpp:57
a service with zero or more characteristics
Definition: service.hpp:150
@ invalid_offset
Definition: codes.hpp:189
@ success
Definition: codes.hpp:154
@ application_error_start
Definition: codes.hpp:245
@ invalid_attribute_value_length
Definition: codes.hpp:219
requireded parameter to define, how the bootloader can access memory and flash
Definition: bootloader.hpp:85
denoting a memory range with it's start address and endaddress (exklusive)
Definition: bootloader.hpp:48
required parameter to define the size of a page
Definition: bootloader.hpp:34
a list of all memory regions that are flashable by the bootloader
Definition: bootloader.hpp:54
a 128-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:62
adds the ability to indicate this characteristic.
Definition: characteristic_value.hpp:110
Definition: characteristic_value.hpp:1071
Definition: characteristic_value.hpp:1093
characteristic value binding for a control point
Definition: characteristic_value.hpp:1198
class to be mixed into the server instance
Definition: mixin.hpp:78
if added as option to a characteristic, read access is removed from the characteristic
Definition: characteristic_value.hpp:47
adds the ability to notify this characteristic.
Definition: characteristic_value.hpp:89
sets the Write Without Response Characteristic Property bit.
Definition: characteristic_value.hpp:190