1 /*
2 * Copyright (C) 2014 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * 4. Any redistribution, use, or modification is done solely for
17 * personal benefit and not for any commercial purpose or for
18 * monetary gain.
19 *
20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * Please inquire about commercial licensing options at
34 * [email protected]
35 *
36 */
37
38 #define BTSTACK_FILE__ "le_credit_based_flow_control_mode_server.c"
39
40 // *****************************************************************************
41 /* EXAMPLE_START(le_credit_based_flow_control_mode_server): LE Credit-Based Flow-Control Mode Server - Receive data over L2CAP
42 *
43 * @text iOS 11 and newer supports L2CAP channels in LE Credit-Based Flow-Control Mode for fast transfer over LE
44 * [https://github.com/bluekitchen/CBL2CAPChannel-Demo](Basic iOS example on GitHub)
45 */
46 // *****************************************************************************
47
48 #include <inttypes.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #include "btstack.h"
55 #include "le_credit_based_flow_control_mode_server.h"
56
57 // #define TEST_STREAM_DATA
58 #define TEST_PACKET_SIZE 1000
59
60 #define REPORT_INTERVAL_MS 3000
61 #define MAX_NR_CONNECTIONS 3
62
63 const uint16_t TSPX_le_psm = 0x25;
64
65 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
66 static void sm_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
67
68 const uint8_t adv_data[] = {
69 // Flags general discoverable, BR/EDR not supported
70 0x02, 0x01, 0x06,
71 // Name
72 0x0E, 0x09, 'L', 'E', ' ', 'C', 'B', 'M', ' ', 'S', 'e', 'r', 'v', 'e', 'r',
73 };
74 const uint8_t adv_data_len = sizeof(adv_data);
75
76 static btstack_packet_callback_registration_t hci_event_callback_registration;
77 static btstack_packet_callback_registration_t l2cap_event_callback_registration;
78 static btstack_packet_callback_registration_t sm_event_callback_registration;
79
80 // support for multiple clients
81 typedef struct {
82 char name;
83 hci_con_handle_t connection_handle;
84 uint16_t cid;
85 int counter;
86 char test_data[TEST_PACKET_SIZE];
87 int test_data_len;
88 uint32_t test_data_sent;
89 uint32_t test_data_start;
90 } le_cbm_connection_t;
91
92 static le_cbm_connection_t le_cbm_connection;
93
94 static uint16_t initial_credits = L2CAP_LE_AUTOMATIC_CREDITS;
95 static uint8_t data_channel_buffer[TEST_PACKET_SIZE];
96
97 /*
98 * @section Track throughput
99 * @text We calculate the throughput by setting a start time and measuring the amount of
100 * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
101 * and reset the counter and start time.
102 */
103
104 /* LISTING_START(tracking): Tracking throughput */
105
test_reset(le_cbm_connection_t * context)106 static void test_reset(le_cbm_connection_t * context){
107 context->test_data_start = btstack_run_loop_get_time_ms();
108 context->test_data_sent = 0;
109 }
110
test_track_data(le_cbm_connection_t * context,int bytes_transferred)111 static void test_track_data(le_cbm_connection_t * context, int bytes_transferred){
112 context->test_data_sent += bytes_transferred;
113 // evaluate
114 uint32_t now = btstack_run_loop_get_time_ms();
115 uint32_t time_passed = now - context->test_data_start;
116 if (time_passed < REPORT_INTERVAL_MS) return;
117 // print speed
118 int bytes_per_second = context->test_data_sent * 1000 / time_passed;
119 printf("%c: %"PRIu32" bytes sent-> %u.%03u kB/s\n", context->name, context->test_data_sent, bytes_per_second / 1000, bytes_per_second % 1000);
120
121 // restart
122 context->test_data_start = now;
123 context->test_data_sent = 0;
124 }
125 /* LISTING_END(tracking): Tracking throughput */
126
127 #ifdef TEST_STREAM_DATA
128 /* LISTING_END */
129 /*
130 * @section Streamer
131 *
132 * @text The streamer function checks if notifications are enabled and if a notification can be sent now.
133 * It creates some test data - a single letter that gets increased every time - and tracks the data sent.
134 */
135
136 /* LISTING_START(streamer): Streaming code */
streamer(void)137 static void streamer(void){
138
139 if (le_cbm_connection.cid == 0) return;
140
141 // create test data
142 le_cbm_connection.counter++;
143 if (le_cbm_connection.counter > 'Z') le_cbm_connection.counter = 'A';
144 memset(le_cbm_connection.test_data, le_cbm_connection.counter, le_cbm_connection.test_data_len);
145
146 // send
147 l2cap_send(le_cbm_connection.cid, (uint8_t *) le_cbm_connection.test_data, le_cbm_connection.test_data_len);
148
149 // track
150 test_track_data(&le_cbm_connection, le_cbm_connection.test_data_len);
151
152 // request another packet
153 l2cap_request_can_send_now_event(le_cbm_connection.cid);
154 }
155 /* LISTING_END */
156 #endif
157
158
159 /*
160 * @section HCI + L2CAP Packet Handler
161 *
162 * @text The packet handler is used to stop the notifications and reset the MTU on connect
163 * It would also be a good place to request the connection parameter update as indicated
164 * in the commented code block.
165 */
166
167 /* LISTING_START(packetHandler): Packet Handler */
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)168 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
169 UNUSED(channel);
170
171 bd_addr_t event_address;
172 uint16_t psm;
173 uint16_t cid;
174 uint16_t conn_interval;
175 hci_con_handle_t handle;
176 uint8_t status;
177
178 switch (packet_type) {
179 case HCI_EVENT_PACKET:
180 switch (hci_event_packet_get_type(packet)) {
181 case BTSTACK_EVENT_STATE:
182 // BTstack activated, get started
183 if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
184 printf("To start streaming, please run the le_credit_based_flow_control_mode_client example on other device.\n");
185 }
186 break;
187 case HCI_EVENT_DISCONNECTION_COMPLETE:
188 // free connection
189 printf("%c: Disconnect, reason 0x%02x\n", le_cbm_connection.name, hci_event_disconnection_complete_get_reason(packet));
190 le_cbm_connection.connection_handle = HCI_CON_HANDLE_INVALID;
191 break;
192 case HCI_EVENT_META_GAP:
193 switch (hci_event_gap_meta_get_subevent_code(packet)) {
194 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
195 // print connection parameters (without using float operations)
196 conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet);
197 printf("%c: Connection Interval: %u.%02u ms\n", le_cbm_connection.name, conn_interval * 125 / 100, 25 * (conn_interval & 3));
198 printf("%c: Connection Latency: %u\n", le_cbm_connection.name, gap_subevent_le_connection_complete_get_conn_latency(packet));
199
200 // min con interval 15 ms - supported from iOS 11
201 gap_request_connection_parameter_update(le_cbm_connection.connection_handle, 12, 12, 4, 0x0048);
202 printf("Connected, requesting conn param update for handle 0x%04x\n", le_cbm_connection.connection_handle);
203 //
204 test_reset(&le_cbm_connection);
205 break;
206 default:
207 break;
208 }
209 break;
210 case HCI_EVENT_LE_META:
211 switch (hci_event_le_meta_get_subevent_code(packet)) {
212 case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
213 // print connection parameters (without using float operations)
214 conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
215 printf("LE Connection Update:\n");
216 printf("%c: Connection Interval: %u.%02u ms\n", le_cbm_connection.name, conn_interval * 125 / 100, 25 * (conn_interval & 3));
217 printf("%c: Connection Latency: %u\n", le_cbm_connection.name, hci_subevent_le_connection_update_complete_get_conn_latency(packet));
218 break;
219 default:
220 break;
221 }
222 break;
223
224 case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
225 printf("L2CAP Connection Parameter Update Complete, result: 0x%02x\n", l2cap_event_connection_parameter_update_response_get_result(packet));
226 break;
227
228 // LE Credit-based Flow-Control Mode
229
230 case L2CAP_EVENT_CBM_INCOMING_CONNECTION:
231 psm = l2cap_event_cbm_incoming_connection_get_psm(packet);
232 cid = l2cap_event_cbm_incoming_connection_get_local_cid(packet);
233 if (psm != TSPX_le_psm) break;
234 printf("L2CAP: Accepting incoming connection request for 0x%02x, PSM %02x\n", cid, psm);
235 l2cap_cbm_accept_connection(cid, data_channel_buffer, sizeof(data_channel_buffer), initial_credits);
236 break;
237
238 case L2CAP_EVENT_CBM_CHANNEL_OPENED:
239 // inform about new l2cap connection
240 l2cap_event_cbm_channel_opened_get_address(packet, event_address);
241 psm = l2cap_event_cbm_channel_opened_get_psm(packet);
242 cid = l2cap_event_cbm_channel_opened_get_local_cid(packet);
243 handle = l2cap_event_cbm_channel_opened_get_handle(packet);
244 status = l2cap_event_cbm_channel_opened_get_status(packet);
245 if (status == ERROR_CODE_SUCCESS) {
246 printf("L2CAP: Channel successfully opened: %s, handle 0x%04x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n",
247 bd_addr_to_str(event_address), handle, psm, cid, little_endian_read_16(packet, 15));
248 // setup new
249 le_cbm_connection.counter = 'A';
250 le_cbm_connection.cid = cid;
251 le_cbm_connection.connection_handle = handle;
252 le_cbm_connection.test_data_len = btstack_min(l2cap_event_cbm_channel_opened_get_remote_mtu(packet), sizeof(le_cbm_connection.test_data));
253 printf("Test packet size: %u\n", le_cbm_connection.test_data_len);
254 test_reset(&le_cbm_connection);
255 #ifdef TEST_STREAM_DATA
256 l2cap_request_can_send_now_event(le_cbm_connection.cid);
257 #endif
258 } else {
259 printf("L2CAP: Connection to device %s failed, status 0x%02x\n", bd_addr_to_str(event_address), status);
260 }
261 break;
262
263 case L2CAP_EVENT_CHANNEL_CLOSED:
264 printf("L2CAP: Channel closed\n");
265 le_cbm_connection.cid = 0;
266 break;
267
268 #ifdef TEST_STREAM_DATA
269 case L2CAP_EVENT_CAN_SEND_NOW:
270 streamer();
271 break;
272 #endif
273
274 default:
275 break;
276 }
277 break;
278
279 case L2CAP_DATA_PACKET:
280 test_track_data(&le_cbm_connection, size);
281 break;
282
283 default:
284 break;
285 }
286 }
287
288 /*
289 * @section SM Packet Handler
290 *
291 * @text The packet handler is used to handle pairing requests
292 */
sm_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)293 static void sm_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
294 UNUSED(channel);
295 UNUSED(size);
296
297 if (packet_type != HCI_EVENT_PACKET) return;
298
299 switch (hci_event_packet_get_type(packet)) {
300 case SM_EVENT_JUST_WORKS_REQUEST:
301 printf("Just Works requested\n");
302 sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
303 break;
304 case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
305 printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
306 sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
307 break;
308 case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
309 printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
310 break;
311 default:
312 break;
313 }
314 }
315
316 /* @section Main Application Setup
317 *
318 * @text Listing MainConfiguration shows main application code.
319 * It initializes L2CAP, the Security Manager, and configures the ATT Server with the pre-compiled
320 * ATT Database generated from $le_credit_based_flow_control_mode_server.gatt$. Finally, it configures the advertisements
321 * and boots the Bluetooth stack.
322 */
323
324 int btstack_main(void);
btstack_main(void)325 int btstack_main(void)
326 {
327 l2cap_init();
328
329 // setup SM: Display only
330 sm_init();
331
332 // setup ATT server: iOS disconnects if ATT MTU Exchange fails
333 att_server_init(profile_data, NULL, NULL);
334
335 // register for HCI events
336 hci_event_callback_registration.callback = &packet_handler;
337 hci_add_event_handler(&hci_event_callback_registration);
338
339 // register for SM events
340 sm_event_callback_registration.callback = &sm_packet_handler;
341 sm_add_event_handler(&sm_event_callback_registration);
342
343 // register for L2CAP events
344 l2cap_event_callback_registration.callback = &packet_handler;
345 l2cap_add_event_handler(&l2cap_event_callback_registration);
346
347 // le data channel setup
348 l2cap_cbm_register_service(&packet_handler, TSPX_le_psm, LEVEL_0);
349
350 // setup advertisements
351 uint16_t adv_int_min = 0x0030;
352 uint16_t adv_int_max = 0x0030;
353 uint8_t adv_type = 0;
354 bd_addr_t null_addr;
355 memset(null_addr, 0, 6);
356 gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
357 gap_advertisements_set_data(adv_data_len, (uint8_t *) adv_data);
358 gap_advertisements_enable(1);
359
360 // turn on!
361 hci_power_control(HCI_POWER_ON);
362
363 return 0;
364 }
365 /* EXAMPLE_END */
366