1#ifndef BLUETOE_SERVICES_BOOTLOADER_HPP
2#define BLUETOE_SERVICES_BOOTLOADER_HPP
4#include <bluetoe/service.hpp>
5#include <bluetoe/mixin.hpp>
19 namespace bootloader {
23 struct handler_meta_type {};
24 struct memory_region_meta_type {};
25 struct page_size_meta_type {};
32 template < std::
size_t PageSize >
36 typedef details::page_size_meta_type meta_type;
37 static constexpr std::size_t value = PageSize;
47 template < std::u
intptr_t Start, std::u
intptr_t End >
53 template <
typename ... Regions >
57 template < std::uintptr_t Start, std::uintptr_t End,
typename ... Regions >
60 static bool acceptable( std::uintptr_t start, std::uintptr_t end )
62 return ( start >= Start && end <= End )
66 typedef details::memory_region_meta_type meta_type;
72 static bool acceptable( std::uintptr_t, std::uintptr_t )
77 typedef details::memory_region_meta_type meta_type;
84 template <
typename UserHandler >
87 typedef UserHandler user_handler;
88 typedef details::handler_meta_type meta_type;
95 template <
class Server >
131 buffer_overrun_attempt
156 template < std::
size_t PageSize >
166 std::uint32_t free_size()
const
168 return state_ == filling
173 template <
class Handler >
174 void set_start_address( std::uintptr_t address, Handler& h, std::uint32_t checksum, std::uint16_t cons )
176 assert( state_ == idle );
178 ptr_ = address % PageSize;
179 addr_ = address - ptr_;
183 h.read_mem( addr_, ptr_, &buffer_[ 0 ] );
186 template <
class Handler >
187 std::size_t write_data( std::size_t write_size,
const std::uint8_t* value, Handler& h )
189 assert( state_ == filling );
190 assert( ptr_ != PageSize );
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_ );
198 if ( ptr_ == PageSize )
206 template <
class Handler >
207 bool flush( Handler& h )
209 if ( state_ != filling || ptr_ == 0 )
215 if ( PageSize != ptr_ )
216 h.read_mem( addr_ + ptr_, PageSize - ptr_, &buffer_[ ptr_ ] );
218 h.start_flash( addr_, &buffer_[ 0 ], PageSize );
223 std::uint32_t crc()
const
236 return state_ == idle;
239 std::uint16_t consecutive()
const
250 std::uintptr_t addr_;
253 std::uint8_t buffer_[ PageSize ];
254 std::uint16_t consecutive_;
257 enum opcode : std::uint8_t
268 undefined_opcode = 0xff
271 enum data_code : std::uint8_t
276 template <
typename UserHandler,
typename MemRegions, std::
size_t PageSize >
277 class controller :
public UserHandler
281 : opcode( undefined_opcode )
285 , in_flash_mode( false )
292 std::uintptr_t read_address(
const std::uint8_t* rend )
294 std::uintptr_t result = 0;
295 for (
const std::uint8_t* rbegin = rend +
sizeof( std::uint8_t* ); rbegin != rend; --rbegin )
297 result = result << 8;
298 result |= *( rbegin -1 );
304 std::pair< std::uint8_t, bool > bootloader_write_control_point( std::size_t write_size,
const std::uint8_t* value )
306 if ( write_size < 1 )
313 case opc_get_version:
317 if ( write_size != 1 )
320 in_flash_mode =
false;
324 for (
auto& buffer : buffers_ )
330 if ( write_size != 1 + 2 *
sizeof( std::uint8_t* ) )
333 start_address = read_address( value +1 );
334 const std::uintptr_t end_address = read_address( value +1 +
sizeof( std::uint8_t* ) );
336 if ( start_address > end_address || !MemRegions::acceptable( start_address,end_address ) )
339 check_sum = this->public_checksum32( start_address, end_address - start_address );
342 case opc_start_flash:
344 if ( write_size != 1 +
sizeof( std::uint8_t* ) )
347 start_address = read_address( value +1 );
348 check_sum = this->checksum32( start_address );
352 in_flash_mode =
true;
354 if ( !MemRegions::acceptable( start_address, start_address ) )
357 for (
auto& buffer : buffers_ )
360 buffers_[next_buffer_].set_start_address( start_address, *
this, check_sum, consecutive_ );
365 if ( !in_flash_mode )
366 return request_error( invalid_state );
368 if ( !buffers_[next_buffer_].flush( *
this ) )
369 return request_error( invalid_state );
376 if ( write_size != 1 +
sizeof( std::uint8_t* ) )
379 const std::uintptr_t start_address = read_address( value +1 );
381 this->run( start_address );
386 if ( write_size != 1 )
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 );
399 if ( start_address > end_address || !MemRegions::acceptable( start_address,end_address ) )
402 if ( start_address != end_address )
404 this->data_indication_call_back();
411 return std::pair< std::uint8_t, bool >{ att_error_codes::invalid_opcode,
false };
417 std::uint8_t bootloader_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
419 assert( read_size >= 20 );
420 *out_buffer = opcode;
424 case opc_get_version:
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 );
429 *out_buffer = opc_get_version;
430 std::copy( version.first, version.first + out_size - 1, out_buffer + 1 );
435 bluetoe::details::write_32bit( out_buffer + 1, check_sum );
436 out_size =
sizeof( std::uint8_t ) +
sizeof( std::uint32_t );
441 std::uint8_t* out = out_buffer;
443 *out =
sizeof( std::uint8_t* );
445 out = bluetoe::details::write_32bit( out, PageSize );
446 out = bluetoe::details::write_32bit( out, number_of_concurrent_flashs );
448 out_size = out - out_buffer;
451 case opc_start_flash:
453 std::uint8_t* out = out_buffer;
455 *out = read_size + 3;
457 out = bluetoe::details::write_32bit( out, buffers_[next_buffer_].crc() );
459 out_size = out - out_buffer;
467 std::uint8_t* out = out_buffer;
469 out = bluetoe::details::write_32bit( out, buffers_[next_buffer_].crc() );
470 out = bluetoe::details::write_16bit( out, buffers_[next_buffer_].consecutive() );
472 out_size = out - out_buffer;
477 std::uint8_t* out = out_buffer;
479 out = bluetoe::details::write_32bit( out, check_sum );
480 *out =
static_cast< std::uint8_t
>( error );
483 out_size = out - out_buffer;
491 std::uint8_t bootloader_write_data( std::size_t write_size,
const std::uint8_t* value )
493 if ( !in_flash_mode )
494 return no_operation_in_progress;
496 if ( write_size == 0 )
499 if ( buffers_[ next_buffer_ ].free_size() == 0 && !find_next_buffer( start_address ) )
500 return buffer_overrun_attempt;
504 const std::size_t moved = buffers_[ next_buffer_ ].write_data( write_size, value, *
this );
508 start_address += moved;
510 if ( write_size && !find_next_buffer( start_address ) )
511 return buffer_overrun_attempt;
517 std::uint8_t bootloader_read_data( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
519 if ( opcode == opc_read )
521 out_size = std::min( read_size, end_address - start_address );
523 error = this->public_read_mem( start_address, out_size, out_buffer );
525 if ( error == error_codes::success )
527 check_sum = this->checksum32( out_buffer, out_size, check_sum );
528 start_address += out_size;
535 if ( start_address == end_address || error != error_codes::success )
537 this->control_point_notification_call_back();
541 this->data_indication_call_back();
548 std::uint8_t bootloader_progress_data( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
550 buffers_[used_buffer_].free();
552 assert( read_size >= out_size );
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() );
558 *out_buffer = read_size + 3;
561 used_buffer_ = ( used_buffer_ + 1 ) % number_of_concurrent_flashs;
566 template <
class Server >
569 server.template notify< progress_uuid >();
572 template <
class Server >
573 void bootloader_control_point_notification( Server& server )
575 server.template notify< control_point_uuid >();
578 template <
class Server >
579 void bootloader_data_indication( Server& server )
581 server.template indicate< data_uuid >();
585 std::uint32_t free_size()
const
587 std::uint32_t result = 0;
589 for (
const auto& b : buffers_ )
590 result += b.free_size();
595 bool find_next_buffer( std::size_t start_address )
597 const auto next = ( next_buffer_ + 1 ) % number_of_concurrent_flashs;
599 if ( buffers_[ next ].empty() )
602 buffers_[ next ].set_start_address( start_address, *
this, buffers_[ next_buffer_ ].crc(), consecutive_ );
611 std::pair< std::uint8_t, bool > request_error( std::uint8_t code )
613 opcode = undefined_opcode;
614 in_flash_mode =
false;
616 return std::pair< std::uint8_t, bool >{ code,
false };
620 std::uintptr_t start_address;
621 std::uintptr_t end_address;
623 std::uint32_t check_sum;
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];
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;
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." );
640 using mem_regions =
typename bluetoe::details::find_by_meta_type< memory_region_meta_type, Options... >::type;
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." );
645 using user_handler =
typename bluetoe::details::find_by_meta_type< handler_meta_type, Options... >::type;
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." );
650 using implementation = controller< typename user_handler::user_handler, mem_regions, page_size::value >;
660 implementation, &implementation::bootloader_read_control_point
669 implementation, &implementation::bootloader_write_data
672 implementation, &implementation::bootloader_read_data
681 implementation, &implementation::bootloader_progress_data
691 template <
typename UserHandler,
typename MemRegions, std::
size_t PageSize >
692 using controller = details::controller< UserHandler, MemRegions, PageSize >;
728 void read_mem( std::uintptr_t address, std::size_t size, std::uint8_t* destination );
733 std::uint32_t
checksum32( std::uintptr_t start_addr, std::size_t size );
738 std::uint32_t
checksum32(
const std::uint8_t* start_addr, std::size_t size, std::uint32_t old_crc );
780 template <
typename ... Options >
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)
bootloader::error_codes reset()
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