BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
l2cap.hpp
1#ifndef BLUETOE_L2CAP_HPP
2#define BLUETOE_L2CAP_HPP
3
4#include <cstdint>
5#include <cstddef>
6#include <cassert>
7
8#include <bluetoe/codes.hpp>
10#include <bluetoe/bits.hpp>
11
12/*
13 * Design decisions:
14 * - Link layer should not know nothing about l2cap fragmentation / channels and so on
15 * - L2CAP layer should not know anything about LL PDU layouts
16 * - link_layer::read_buffer / link_layer::read_buffer should be assigned to LL PDUs and their
17 * layout and thus, not be used in the l2cap layer.
18 * - For now, the fragmentation, defragmentation is still done in the ll_l2cap_sdu_buffer
19 * of the link_layer, but should be moved into the l2cap<> class.
20 */
21namespace bluetoe {
22
23namespace details {
24
29 class l2cap_channel
30 {
31 public:
32 static constexpr std::uint16_t channel_id = 42;
33 static constexpr std::size_t minimum_channel_mtu_size = bluetoe::details::default_att_mtu_size;
34 static constexpr std::size_t maximum_channel_mtu_size = bluetoe::details::default_att_mtu_size;
35
36 template < typename ConnectionData >
37 void l2cap_input( const std::uint8_t* input, std::size_t in_size, std::uint8_t* output, std::size_t& out_size, ConnectionData& );
38
39 template < typename ConnectionData >
40 void l2cap_output( std::uint8_t* output, std::size_t& out_size, ConnectionData& );
41
42 template < class PreviousData >
43 using channel_data_t = PreviousData;
44 };
45
46 template < typename CurrentMaximum, typename Channel >
47 struct maximum_min_channel_mtu_size
48 {
49 using type =
50 typename select_type<
51 ( CurrentMaximum::value > Channel::minimum_channel_mtu_size ),
52 CurrentMaximum,
53 std::integral_constant< std::size_t, Channel::minimum_channel_mtu_size >
54 >::type;
55 };
56
57 template < typename CurrentMaximum, typename Channel >
58 struct maximum_max_channel_mtu_size
59 {
60 using type =
61 typename select_type<
62 ( CurrentMaximum::value > Channel::maximum_channel_mtu_size ),
63 CurrentMaximum,
64 std::integral_constant< std::size_t, Channel::maximum_channel_mtu_size >
65 >::type;
66 };
67
68 template < typename ComposedData, typename Channel >
69 struct add_channel_data
70 {
71 using type = typename Channel::template channel_data_t< ComposedData >;
72 };
73
74 static constexpr std::size_t l2cap_layer_header_size = 4u;
75
96 template < class LinkLayer, class ChannelData, class ... Channels >
97 class l2cap : public derive_from< std::tuple< Channels... > >
98 {
99 public:
103 template < class ConnectionDetails >
104 bool handle_l2cap_input( const std::uint8_t* input, std::size_t in_size, ConnectionDetails& connection );
105
110 template < class ConnectionDetails >
111 void transmit_pending_l2cap_output( ConnectionDetails& connection );
112
118 static constexpr std::size_t minimum_mtu_size = fold<
119 std::tuple< Channels... >,
120 maximum_min_channel_mtu_size,
121 std::integral_constant< std::size_t, 0 >
122 >::type::value;
123
124 static constexpr std::size_t maximum_mtu_size = fold<
125 std::tuple< Channels... >,
126 maximum_max_channel_mtu_size,
127 std::integral_constant< std::size_t, 0 >
128 >::type::value;
129
130 static_assert( maximum_mtu_size >= minimum_mtu_size, "maximum_mtu_size have to be greater than minimum_mtu_size" );
131
132 using connection_data_t = typename fold<
133 std::tuple< Channels... >,
134 add_channel_data,
135 ChannelData
136 >::type;
137
138 private:
139 template < class ConnectionDetails >
140 bool transmit_single_pending_l2cap_output( ConnectionDetails& connection );
141
142 template < class ConnectionDetails >
143 struct l2cap_input_handler
144 {
145 l2cap_input_handler( l2cap* t, std::uint16_t ci, const std::uint8_t* i, std::size_t is, std::uint8_t* o, std::size_t os, ConnectionDetails& c )
146 : that( t )
147 , channel_id( ci )
148 , input( i )
149 , in_size( is )
150 , output( o )
151 , out_size( os )
152 , connection( c )
153 , handled( false )
154 {
155 }
156
157 template< typename Channel >
158 void each()
159 {
160 if ( channel_id == Channel::channel_id )
161 {
162 static_cast< Channel& >( *that ).l2cap_input( input, in_size, output, out_size, connection );
163 handled = true;
164 }
165 }
166
167 l2cap* that;
168 std::uint16_t channel_id;
169 const std::uint8_t* input;
170 std::size_t in_size;
171 std::uint8_t* output;
172 std::size_t out_size;
173 ConnectionDetails& connection;
174 bool handled;
175 };
176
177 template < typename ConnectionDetails >
178 class l2cap_output_handler
179 {
180 public:
181 l2cap_output_handler( l2cap* t, std::uint8_t* out, std::size_t s, ConnectionDetails& c )
182 : that( t )
183 , output( out )
184 , size( s )
185 , out_size( 0 )
186 , connection( c )
187 {
188 }
189
190 template< typename Channel >
191 void each()
192 {
193 if ( out_size == 0 )
194 {
195 out_size = size;
196 static_cast< Channel& >( *that ).l2cap_output( output, out_size, connection );
197 channel_id = Channel::channel_id;
198 }
199 }
200
201 l2cap* that;
202 std::uint8_t* output;
203 std::size_t size;
204 std::size_t out_size;
205 std::uint16_t channel_id;
206 ConnectionDetails& connection;
207 };
208
209 LinkLayer& link_layer()
210 {
211 return static_cast< LinkLayer&>( *this );
212 }
213 };
214
215
216 // implemenation
217 template < class LinkLayer, class ChannelData, class ... Channels >
218 template < class ConnectionDetails >
219 bool l2cap< LinkLayer, ChannelData, Channels... >::handle_l2cap_input( const std::uint8_t* input, std::size_t in_size, ConnectionDetails& connection )
220 {
221 // just swallow input, if not resonable
222 if ( in_size < l2cap_layer_header_size )
223 return true;
224
225 const std::uint16_t size = read_16bit( input );
226 const std::uint16_t channel_id = read_16bit( input + 2 );
227
228 if ( in_size != size + l2cap_layer_header_size )
229 return true;
230
231 auto output = link_layer().allocate_l2cap_output_buffer( maximum_mtu_size );
232 if ( output.first == 0 )
233 return false;
234
235 assert( output.second );
236
237 l2cap_input_handler< ConnectionDetails > handler(
238 this, channel_id, input + l2cap_layer_header_size, in_size - l2cap_layer_header_size,
239 output.second + l2cap_layer_header_size, maximum_mtu_size, connection );
240
241 for_< Channels... >::template each< l2cap_input_handler< ConnectionDetails >& >( handler );
242
243 if ( handler.handled && handler.out_size )
244 {
245 write_16bit( output.second, handler.out_size );
246 write_16bit( output.second + 2, channel_id );
247 link_layer().commit_l2cap_output_buffer( { handler.out_size + l2cap_layer_header_size, output.second } );
248 }
249
250 return true;
251 }
252
253 template < class LinkLayer, class ChannelData, class ... Channels >
254 template < class ConnectionDetails >
255 void l2cap< LinkLayer, ChannelData, Channels... >::transmit_pending_l2cap_output( ConnectionDetails& connection )
256 {
257 for ( bool cont = transmit_single_pending_l2cap_output( connection ); cont;
258 cont = transmit_single_pending_l2cap_output( connection ) )
259 ;
260 }
261
262 template < class LinkLayer, class ChannelData, class ... Channels >
263 template < class ConnectionDetails >
264 bool l2cap< LinkLayer, ChannelData, Channels... >::transmit_single_pending_l2cap_output( ConnectionDetails& connection )
265 {
266 auto output = link_layer().allocate_l2cap_output_buffer( maximum_mtu_size );
267 if ( output.first == 0 )
268 return false;
269
270 assert( output.second );
271
272 l2cap_output_handler< ConnectionDetails > handler(
273 this, output.second + l2cap_layer_header_size, output.first - l2cap_layer_header_size, connection );
274
275 for_< Channels... >::template each< l2cap_output_handler< ConnectionDetails >& >( handler );
276
277 if ( handler.out_size )
278 {
279 write_16bit( output.second, handler.out_size );
280 write_16bit( output.second + 2, handler.channel_id );
281 link_layer().commit_l2cap_output_buffer( { handler.out_size + l2cap_layer_header_size, output.second } );
282 }
283
284 return handler.out_size;
285 }
286}
287}
288
289#endif
wrap< typename select_type_impl< Select >::template f< A, B > > select_type
Select A or B by Select. If Select == true, the result is A; B otherwise.
Definition: meta_tools.hpp:38