BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
ll_l2cap_sdu_buffer.hpp
1#ifndef BLUETOE_LINK_LAYER_L_L2CAP_SDU_BUFFER_HPP
2#define BLUETOE_LINK_LAYER_L_L2CAP_SDU_BUFFER_HPP
3
4#include <bluetoe/buffer.hpp>
5#include <bluetoe/codes.hpp>
6#include <bluetoe/bits.hpp>
7
8namespace bluetoe {
9namespace link_layer {
10
17 template < class BufferedRadio, std::size_t MTUSize >
18 class ll_l2cap_sdu_buffer : public BufferedRadio
19 {
20 public:
22
32 read_buffer allocate_l2cap_transmit_buffer( std::size_t payload_size );
33
41 read_buffer allocate_ll_transmit_buffer( std::size_t payload_size );
42
49
56
72
79
83 using layout = typename BufferedRadio::layout;
84
85 private:
86 static constexpr std::uint16_t pdu_type_mask = 0x0003;
87 static constexpr std::uint16_t pdu_type_link_layer = 0x0003;
88 static constexpr std::uint16_t pdu_type_start = 0x0002;
89 static constexpr std::uint16_t pdu_type_continuation = 0x0001;
90
91 static constexpr std::size_t header_size = BufferedRadio::header_size;
92 static constexpr std::size_t layout_overhead = BufferedRadio::layout_overhead;
93 static constexpr std::size_t l2cap_header_size = 4u;
94 static constexpr std::size_t overall_overhead = header_size + layout_overhead + l2cap_header_size;
95 static constexpr std::size_t ll_overhead = header_size + layout_overhead;
96
97 void add_to_receive_buffer( const std::uint8_t*, const std::uint8_t* );
98 void try_send_pdus();
99
100 std::uint8_t receive_buffer_[ MTUSize + overall_overhead ];
101 std::uint16_t receive_size_;
102 std::size_t receive_buffer_used_;
103
104 // when transmitting L2CAP PDUs, we need room for at least the first LL header. Otherwise,
105 // it would not be possible to transparently replace commit_l2cap_transmit_buffer()
106 // transparently with commit_transmit_buffer() for the case that fragmentation is not
107 // used.
108 std::uint8_t transmit_buffer_[ MTUSize + overall_overhead ];
109 std::uint16_t transmit_size_;
110 std::size_t transmit_buffer_used_;
111 };
112
117 template < class BufferedRadio >
118 class ll_l2cap_sdu_buffer< BufferedRadio, bluetoe::details::default_att_mtu_size > : public BufferedRadio
119 {
120 public:
122 read_buffer allocate_ll_transmit_buffer( std::size_t size );
127 private:
128 static constexpr std::size_t header_size = BufferedRadio::header_size;
129 static constexpr std::size_t layout_overhead = BufferedRadio::layout_overhead;
130 static constexpr std::size_t l2cap_header_size = 4u;
131 static constexpr std::size_t overall_overhead = header_size + layout_overhead + l2cap_header_size;
132 static constexpr std::size_t ll_overhead = header_size + layout_overhead;
133 };
134
135 // implementation
136 template < class BufferedRadio, std::size_t MTUSize >
137 ll_l2cap_sdu_buffer< BufferedRadio, MTUSize >::ll_l2cap_sdu_buffer()
138 : receive_size_( 0 )
139 , receive_buffer_used_( 0 )
140 , transmit_size_( 0 )
141 , transmit_buffer_used_( 0 )
142 {
143 }
144
145 template < class BufferedRadio, std::size_t MTUSize >
147 {
148 assert( payload_size <= MTUSize );
149
150 if ( transmit_buffer_used_ != 0 || transmit_size_ != 0 )
151 return { nullptr, 0 };
152
153 return { transmit_buffer_, payload_size + overall_overhead };
154 }
155
156 template < class BufferedRadio, std::size_t MTUSize >
158 {
159 try_send_pdus();
160
161 return this->allocate_transmit_buffer( payload_size + ll_overhead );
162 }
163
164 template < class BufferedRadio, std::size_t MTUSize >
166 {
167 const auto body = layout::body( buffer );
168 const std::size_t size = bluetoe::details::read_16bit( body.first ) + overall_overhead;
169
170 transmit_buffer_used_ = 0;
171 transmit_size_ = size;
172
173 try_send_pdus();
174 }
175
176 template < class BufferedRadio, std::size_t MTUSize >
178 {
179 this->commit_transmit_buffer( buffer );
180 }
181
182 template < class BufferedRadio, std::size_t MTUSize >
184 {
185 try_send_pdus();
186
187 // is there already a defragmented L2CAP SDU?
188 if ( receive_buffer_used_ != 0 && receive_size_ == 0 )
189 return { receive_buffer_, receive_buffer_used_ };
190
191 for ( auto pdu = this->next_received(); pdu.size; pdu = this->next_received() )
192 {
193 const std::uint16_t header = layout::header( pdu );
194 const std::uint16_t type = header & pdu_type_mask;
195
196 // link layer control PDUs are not fragmented by default
197 if ( type == pdu_type_link_layer )
198 return pdu;
199
200 // l2cap message
201 const auto body = layout::body( pdu );
202 const std::size_t body_size = body.second - body.first;
203
204 if ( type == pdu_type_start )
205 {
206 if ( body_size >= l2cap_header_size )
207 {
208 const std::uint16_t l2cap_size = bluetoe::details::read_16bit( body.first );
209
210 // short l2cap PDU that is not fragmented
211 if ( l2cap_size + l2cap_header_size == body_size )
212 return pdu;
213
214 if ( l2cap_size <= MTUSize )
215 {
216 receive_size_ = l2cap_size + overall_overhead;
217 add_to_receive_buffer( pdu.buffer, pdu.buffer + pdu.size );
218 }
219 }
220 }
221 else
222 {
223 add_to_receive_buffer( body.first, body.second );
224 }
225
226 // consume LL PDU
227 this->free_received();
228
229 // is there now a defragmented L2CAP SDU?
230 if ( receive_buffer_used_ != 0 && receive_size_ == 0 )
231 return { receive_buffer_, receive_buffer_used_ };
232 }
233
234 return { nullptr, 0 };
235 }
236
237 template < class BufferedRadio, std::size_t MTUSize >
238 void ll_l2cap_sdu_buffer< BufferedRadio, MTUSize >::add_to_receive_buffer( const std::uint8_t* begin, const std::uint8_t* end )
239 {
240 const std::size_t copy_size = std::min< std::size_t >( receive_size_, end - begin );
241
242 std::copy( begin, end, &receive_buffer_[ receive_buffer_used_ ] );
243 receive_buffer_used_ += copy_size;
244 receive_size_ -= copy_size;
245 }
246
247 template < class BufferedRadio, std::size_t MTUSize >
248 void ll_l2cap_sdu_buffer< BufferedRadio, MTUSize >::try_send_pdus()
249 {
250 while ( transmit_size_ )
251 {
252 const bool first_fragment = transmit_buffer_used_ == 0;
253
254 // for the first PDU, the header overhead is already taken into account. For all additonal fragments,
255 // an additional header has to be allocated.
256 const std::size_t overhead = first_fragment ? 0 : ll_overhead;
257 const auto buffer = this->allocate_transmit_buffer( std::min( transmit_size_ + overhead, this->max_tx_size() ) );
258
259 if ( buffer.size == 0 )
260 return;
261
262 if ( first_fragment )
263 {
264 // The first fragment contains the original LL header
265 const auto copy_size = std::min< std::size_t >( buffer.size, transmit_size_ );
266
267 std::copy( &transmit_buffer_[ 0 ], &transmit_buffer_[ copy_size ], buffer.buffer );
268 layout::header( buffer, pdu_type_start | ( ( copy_size - ll_overhead ) << 8 ) );
269
270 transmit_size_ -= copy_size;
271 transmit_buffer_used_ += copy_size;
272 }
273 else
274 {
275 // for every additional fragment, an additional header has to be generated
276 const auto body = layout::body( buffer );
277 const auto copy_size = std::min< std::size_t >( std::distance( body.first, body.second ), transmit_size_ );
278
279 std::copy( &transmit_buffer_[ transmit_buffer_used_ ], &transmit_buffer_[ transmit_buffer_used_+ copy_size ], body.first );
280 layout::header( buffer, pdu_type_continuation | ( copy_size << 8 ) );
281
282 transmit_size_ -= copy_size;
283 transmit_buffer_used_ += copy_size;
284 }
285
286 this->commit_transmit_buffer( buffer );
287 }
288
289 transmit_buffer_used_ = 0;
290 }
291
292 template < class BufferedRadio, std::size_t MTUSize >
294 {
295 if (receive_buffer_used_)
296 {
297 receive_buffer_used_ = 0;
298 receive_size_ = 0;
299 }
300 else
301 {
302 this->free_received();
303 }
304 }
305
306
307 // implementation
308 template < class BufferedRadio >
310 {
311 return this->allocate_transmit_buffer( size + overall_overhead );
312 }
313
314 template < class BufferedRadio >
316 {
317 return this->allocate_transmit_buffer( size + ll_overhead );
318 }
319
320 template < class BufferedRadio >
322 {
323 return this->commit_transmit_buffer( buffer );
324 }
325
326 template < class BufferedRadio >
328 {
329 return this->commit_transmit_buffer( buffer );
330 }
331
332 template < class BufferedRadio >
334 {
335 return this->next_received();
336 }
337
338 template < class BufferedRadio >
340 {
341 return this->free_received();
342 }
343}
344}
345
346#endif