BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
nrf51.hpp
1#ifndef BLUETOE_BINDINGS_NRF51_HPP
2#define BLUETOE_BINDINGS_NRF51_HPP
3
4#include <bluetoe/link_layer.hpp>
5#include <bluetoe/ll_data_pdu_buffer.hpp>
6#include <bluetoe/nrf.hpp>
7#include <cstdint>
8
9extern "C" void RADIO_IRQHandler(void);
10extern "C" void TIMER0_IRQHandler(void);
11
12namespace bluetoe
13{
14 namespace nrf51_details
15 {
16 /* Counter used for CCM */
17 struct counter {
18 std::uint32_t low;
19 std::uint8_t high;
20
21 // set to zero
22 counter();
23
24 void increment();
25 void copy_to( std::uint8_t* target ) const;
26 };
27
28 // map compile time callbacks to runtime callbacks for faster development cycles.
29 class adv_callbacks
30 {
31 public:
32 virtual void adv_received( const link_layer::read_buffer& receive ) = 0;
33 virtual void adv_timeout() = 0;
34 virtual void timeout() = 0;
35 virtual void end_event() = 0;
36
37 virtual link_layer::write_buffer received_data( const link_layer::read_buffer& ) = 0;
38 virtual link_layer::write_buffer next_transmit() = 0;
39 virtual link_layer::read_buffer allocate_receive_buffer() = 0;
40 virtual void load_transmit_counter() = 0;
41
42 virtual bool is_scan_request_in_filter_callback( const link_layer::device_address& ) const = 0;
43 };
44
45 class scheduled_radio_base
46 {
47 public:
48 class lock_guard
49 {
50 public:
51 lock_guard();
52 ~lock_guard();
53
54 lock_guard( const lock_guard& ) = delete;
55 lock_guard& operator=( const lock_guard& ) = delete;
56 private:
57 const std::uint32_t context_;
58 };
59
60 scheduled_radio_base( adv_callbacks&, std::uint32_t encrypted_area );
61 explicit scheduled_radio_base( adv_callbacks& );
62
63 void schedule_advertisment(
64 unsigned channel,
65 const link_layer::write_buffer& advertising_data,
66 const link_layer::write_buffer& response_data,
67 link_layer::delta_time when,
68 const link_layer::read_buffer& receive );
69
70 void set_access_address_and_crc_init( std::uint32_t access_address, std::uint32_t crc_init );
71
72 void run();
73
74 void wake_up();
75
76 std::uint32_t static_random_address_seed() const;
77
78 // no native white list implementation atm
79 static constexpr std::size_t radio_maximum_white_list_entries = 0;
80
81 static constexpr bool hardware_supports_encryption = false;
82
83 void increment_receive_packet_counter()
84 {
85 }
86
87 void increment_transmit_packet_counter()
88 {
89 }
90
96 void nrf_flash_memory_access_begin();
97 void nrf_flash_memory_access_end();
98
99 protected:
100
101 bluetoe::link_layer::delta_time start_connection_event_impl(
102 unsigned channel,
105 const link_layer::read_buffer& receive_buffer );
106
107 void configure_encryption( bool receive, bool transmit );
108
109 private:
110 friend void ::RADIO_IRQHandler(void);
111 friend void ::TIMER0_IRQHandler(void);
112
113 bool is_valid_scan_request() const;
114 void stop_radio();
115
116 void adv_radio_interrupt();
117 void adv_timer_interrupt();
118 void evt_radio_interrupt();
119 void evt_timer_interrupt();
120 void radio_interrupt();
121 void timer_interrupt();
122
123 unsigned frequency_from_channel( unsigned channel ) const;
124
125 adv_callbacks& callbacks_;
126 volatile bool timeout_;
127 volatile bool received_;
128 volatile bool evt_timeout_;
129 volatile bool end_evt_;
130 volatile int wake_up_;
131
132 static constexpr unsigned connection_event_type_base = 100;
133
134 enum class state {
135 idle,
136 // timeout while receiving, stopping the radio, waiting for the radio to become disabled
137 adv_transmitting,
138 adv_receiving,
139 adv_transmitting_response,
140 // connection event
141 evt_wait_connect = connection_event_type_base,
142 evt_transmiting_closing,
143 };
144
145 volatile state state_;
146
147 bluetoe::link_layer::delta_time anchor_offset_;
148
149 link_layer::read_buffer receive_buffer_;
150 link_layer::write_buffer response_data_;
151 std::uint8_t empty_receive_[ 3 ];
152 bool receive_encrypted_;
153 bool transmit_encrypted_;
154 std::uint32_t encrypted_area_;
155 };
156
157 class scheduled_radio_base_with_encryption_base : public scheduled_radio_base
158 {
159 protected:
160 scheduled_radio_base_with_encryption_base( adv_callbacks& cbs, std::uint32_t scratch_area, std::uint32_t encrypted_area );
161
162 void load_transmit_packet_counter();
163
164 bluetoe::link_layer::delta_time start_connection_event(
165 unsigned channel,
168 const link_layer::read_buffer& receive_buffer )
169 {
170 load_receive_packet_counter();
171 return start_connection_event_impl( channel, start_receive, end_receive, receive_buffer );
172 }
173
174 public:
175 static constexpr bool hardware_supports_lesc_pairing = true;
176 static constexpr bool hardware_supports_legacy_pairing = true;
177 static constexpr bool hardware_supports_encryption = hardware_supports_lesc_pairing || hardware_supports_legacy_pairing;
178
182 bluetoe::details::uint128_t create_srand();
183
184 bluetoe::details::longterm_key_t create_long_term_key();
185
186 bluetoe::details::uint128_t c1(
187 const bluetoe::details::uint128_t& temp_key,
188 const bluetoe::details::uint128_t& rand,
189 const bluetoe::details::uint128_t& p1,
190 const bluetoe::details::uint128_t& p2 ) const;
191
192 bluetoe::details::uint128_t s1(
193 const bluetoe::details::uint128_t& temp_key,
194 const bluetoe::details::uint128_t& srand,
195 const bluetoe::details::uint128_t& mrand );
196
200 bool is_valid_public_key( const std::uint8_t* public_key ) const;
201
202 std::pair< bluetoe::details::ecdh_public_key_t, bluetoe::details::ecdh_private_key_t > generate_keys();
203
204 bluetoe::details::uint128_t select_random_nonce();
205
206 bluetoe::details::ecdh_shared_secret_t p256( const std::uint8_t* private_key, const std::uint8_t* public_key );
207
208 bluetoe::details::uint128_t f4( const std::uint8_t* u, const std::uint8_t* v, const std::array< std::uint8_t, 16 >& k, std::uint8_t z );
209
210 std::pair< bluetoe::details::uint128_t, bluetoe::details::uint128_t > f5(
211 const bluetoe::details::ecdh_shared_secret_t dh_key,
212 const bluetoe::details::uint128_t& nonce_central,
213 const bluetoe::details::uint128_t& nonce_periperal,
214 const bluetoe::link_layer::device_address& addr_controller,
215 const bluetoe::link_layer::device_address& addr_peripheral );
216
217 bluetoe::details::uint128_t f6(
218 const bluetoe::details::uint128_t& key,
219 const bluetoe::details::uint128_t& n1,
220 const bluetoe::details::uint128_t& n2,
221 const bluetoe::details::uint128_t& r,
222 const bluetoe::details::io_capabilities_t& io_caps,
223 const bluetoe::link_layer::device_address& addr_controller,
224 const bluetoe::link_layer::device_address& addr_peripheral );
225
226 std::uint32_t g2(
227 const std::uint8_t* u,
228 const std::uint8_t* v,
229 const bluetoe::details::uint128_t& x,
230 const bluetoe::details::uint128_t& y );
231
235 bluetoe::details::uint128_t create_passkey();
236
240 std::pair< std::uint64_t, std::uint32_t > setup_encryption( bluetoe::details::uint128_t key, std::uint64_t skdm, std::uint32_t ivm );
241
242 void start_receive_encrypted();
243 void start_transmit_encrypted();
244 void stop_receive_encrypted();
245 void stop_transmit_encrypted();
246
247 void increment_receive_packet_counter()
248 {
249 rx_counter_.increment();
250 }
251
252 void increment_transmit_packet_counter()
253 {
254 tx_counter_.increment();
255 }
256
263 void set_identity_resolving_key( const details::identity_resolving_key_t& irk );
264
265 private:
266 void load_receive_packet_counter();
267
268 counter tx_counter_;
269 counter rx_counter_;
270 };
271
272 /*
273 * There are some data structures where the size depends on the configuration of the link layer (namely the
274 * maximum MTU size).
275 */
276 template < typename ... Options >
277 class scheduled_radio_base_with_encryption : public scheduled_radio_base_with_encryption_base
278 {
279 protected:
280 scheduled_radio_base_with_encryption( adv_callbacks& cbs )
281 : scheduled_radio_base_with_encryption_base( cbs,
282 reinterpret_cast< std::uintptr_t >( &scratch_area_.data[ 0 ] ),
283 reinterpret_cast< std::uintptr_t >( &encrypted_message_.data[ 0 ] ) )
284 {
285 }
286
287 private:
288 // the value MAXPACKETSIZE from the documentation seems to be the maximum value, the size field can store,
289 // and is independent from the MTU size (https://devzone.nordicsemi.com/f/nordic-q-a/13123/what-is-actual-size-required-for-scratch-area-for-ccm-on-nrf52/50031#50031)
290 static constexpr std::size_t scratch_size = 267;
291
292 struct alignas( 4 ) scratch_area_t {
293 std::uint8_t data[ scratch_size ];
294 } scratch_area_;
295
296 // TODO should be calculated with more accuracy base on the configuration of:
297 // - l2cap MAX MTU
298 // - implementation of Data Length Update procedure
299 struct alignas( 4 ) encrypted_message_t {
300 std::uint8_t data[ 260 ];
301 } encrypted_message_;
302 };
303
304 class scheduled_radio_without_encryption_base : public scheduled_radio_base
305 {
306 protected:
307 bluetoe::link_layer::delta_time start_connection_event(
308 unsigned channel,
311 const link_layer::read_buffer& receive_buffer )
312 {
313 return start_connection_event_impl( channel, start_receive, end_receive, receive_buffer );
314 }
315
316 scheduled_radio_without_encryption_base( adv_callbacks& cbs ) : scheduled_radio_base( cbs, 0 )
317 {
318 }
319
320 void load_transmit_packet_counter()
321 {
322 }
323 };
324
325 template < std::size_t TransmitSize, std::size_t ReceiveSize, typename CallBack, typename Base >
326 class scheduled_radio :
327 public bluetoe::link_layer::ll_data_pdu_buffer< TransmitSize, ReceiveSize, scheduled_radio< TransmitSize, ReceiveSize, CallBack, Base > >,
328 private adv_callbacks,
329 public Base
330 {
331 public:
332 scheduled_radio() : Base( static_cast< adv_callbacks& >( *this ) )
333 {
334 }
335
336 bluetoe::link_layer::delta_time schedule_connection_event(
337 unsigned channel,
340 bluetoe::link_layer::delta_time /* connection_interval */ )
341 {
342 link_layer::read_buffer read;
343 {
344 class Base::lock_guard lock;
346 }
347
348 return this->start_connection_event( channel, start_receive, end_receive, read );
349 }
350 private:
352
353 void adv_received( const link_layer::read_buffer& receive ) override
354 {
355 static_cast< CallBack* >( this )->adv_received( receive );
356 }
357
358 void adv_timeout() override
359 {
360 static_cast< CallBack* >( this )->adv_timeout();
361 }
362
363 void timeout() override
364 {
365 static_cast< CallBack* >( this )->timeout();
366 }
367
368 void end_event() override
369 {
370 static_cast< CallBack* >( this )->end_event();
371 }
372
373 link_layer::write_buffer received_data( const link_layer::read_buffer& b ) override
374 {
375 // this function is called within an ISR context, so no need to disable interrupts
376 return this->received( b );
377 }
378
379 link_layer::write_buffer next_transmit() override
380 {
381 // this function is called within an ISR context, so no need to disable interrupts
382 return buffer::next_transmit();
383 }
384
385 link_layer::read_buffer allocate_receive_buffer() override
386 {
388 }
389
390 void load_transmit_counter() override
391 {
392 this->load_transmit_packet_counter();
393 }
394
395 bool is_scan_request_in_filter_callback( const link_layer::device_address& addr ) const override
396 {
397 return static_cast< const CallBack* >( this )->is_scan_request_in_filter( addr );
398 }
399 };
400
401 template < typename Base >
402 struct scheduled_radio_factory
403 {
404 template < std::size_t TransmitSize, std::size_t ReceiveSize, typename CallBack >
405 using scheduled_radio = nrf51_details::scheduled_radio< TransmitSize, ReceiveSize, CallBack, Base >;
406 };
407 } // namespace nrf51_details
408
409 /*
410 * nrf51 without encryption
411 */
412 template < class Server, typename ... Options >
413 using nrf51_without_encryption = link_layer::link_layer<
414 Server,
415 nrf51_details::template scheduled_radio_factory<
416 nrf51_details::scheduled_radio_without_encryption_base
417 >::scheduled_radio,
418 Options... >;
419
420 /*
421 * nrf51 with encryption
422 */
423 template < class Server, typename ... Options >
424 using nrf51 = link_layer::link_layer<
425 Server,
426 nrf51_details::template scheduled_radio_factory<
427 nrf51_details::scheduled_radio_base_with_encryption< Options... >
428 >::template scheduled_radio,
429 Options... >;
430
431 namespace link_layer {
432
434 /*
435 * specialize pdu_layout_by_radio<> for the radio that supports encryption to change the PDU layout
436 * to have that extra byte between header and body
437 */
438 template < std::size_t TransmitSize, std::size_t ReceiveSize, typename CallBack, typename ... Options >
439 struct pdu_layout_by_radio<
440 nrf51_details::scheduled_radio< TransmitSize, ReceiveSize, CallBack, nrf51_details::scheduled_radio_base_with_encryption< Options... > > >
441 {
446 using pdu_layout = bluetoe::nrf_details::encrypted_pdu_layout;
447 };
449 }
450
451} // namespace bluetoe
452
453#endif // include guard