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__ "gatt_streamer_server.c"
39
40 // *****************************************************************************
41 /* EXAMPLE_START(gatt_streamer_server): Performance - Stream Data over GATT (Server)
42 *
43 * @text All newer operating systems provide GATT Client functionality.
44 * This example shows how to get a maximal throughput via BLE:
45 * - send whenever possible,
46 * - use the max ATT MTU.
47 *
48 * @text In theory, we should also update the connection parameters, but we already get
49 * a connection interval of 30 ms and there's no public way to use a shorter
50 * interval with iOS (if we're not implementing an HID device).
51 *
52 * @text Note: To start the streaming, run the example.
53 * On remote device use some GATT Explorer, e.g. LightBlue, BLExplr to enable notifications.
54 */
55 // *****************************************************************************
56
57 #include <inttypes.h>
58 #include <stdint.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62
63 #include "btstack.h"
64
65 // le_streamer.gatt contains the declaration of the provided GATT Services + Characteristics
66 // le_streamer.h contains the binary representation of le_streamer.gatt
67 // it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py le_streamer.gatt le_streamer.h
68 // it needs to be regenerated when the GATT Database declared in le_streamer.gatt file is modified
69 #include "gatt_streamer_server.h"
70
71 #define REPORT_INTERVAL_MS 3000
72 #define MAX_NR_CONNECTIONS 3
73
74
75 static void hci_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
76 static void att_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
77 static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
78 static void streamer(void);
79
80 // Flags general discoverable, BR/EDR supported (== not supported flag not set) when ENABLE_GATT_OVER_CLASSIC is enabled
81 #ifdef ENABLE_GATT_OVER_CLASSIC
82 #define APP_AD_FLAGS 0x02
83 #else
84 #define APP_AD_FLAGS 0x06
85 #endif
86
87 const uint8_t adv_data[] = {
88 // Flags general discoverable
89 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS,
90 // Name
91 0x0c, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'L', 'E', ' ', 'S', 't', 'r', 'e', 'a', 'm', 'e', 'r',
92 // Incomplete List of 16-bit Service Class UUIDs -- FF10 - only valid for testing!
93 0x03, BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x10, 0xff,
94 };
95 const uint8_t adv_data_len = sizeof(adv_data);
96
97 static btstack_packet_callback_registration_t hci_event_callback_registration;
98
99 // support for multiple clients
100 typedef struct {
101 char name;
102 int le_notification_enabled;
103 uint16_t value_handle;
104 hci_con_handle_t connection_handle;
105 int counter;
106 char test_data[200];
107 int test_data_len;
108 uint32_t test_data_sent;
109 uint32_t test_data_start;
110 } le_streamer_connection_t;
111 static le_streamer_connection_t le_streamer_connections[MAX_NR_CONNECTIONS];
112
113 // round robin sending
114 static int connection_index;
115
116 #ifdef ENABLE_GATT_OVER_CLASSIC
117 static uint8_t gatt_service_buffer[70];
118 #endif
119
init_connections(void)120 static void init_connections(void){
121 // track connections
122 int i;
123 for (i=0;i<MAX_NR_CONNECTIONS;i++){
124 le_streamer_connections[i].connection_handle = HCI_CON_HANDLE_INVALID;
125 le_streamer_connections[i].name = 'A' + i;
126 }
127 }
128
connection_for_conn_handle(hci_con_handle_t conn_handle)129 static le_streamer_connection_t * connection_for_conn_handle(hci_con_handle_t conn_handle){
130 int i;
131 for (i=0;i<MAX_NR_CONNECTIONS;i++){
132 if (le_streamer_connections[i].connection_handle == conn_handle) return &le_streamer_connections[i];
133 }
134 return NULL;
135 }
136
next_connection_index(void)137 static void next_connection_index(void){
138 connection_index++;
139 if (connection_index == MAX_NR_CONNECTIONS){
140 connection_index = 0;
141 }
142 }
143
144 /* @section Main Application Setup
145 *
146 * @text Listing MainConfiguration shows main application code.
147 * It initializes L2CAP, the Security Manager, and configures the ATT Server with the pre-compiled
148 * ATT Database generated from $le_streamer.gatt$. Finally, it configures the advertisements
149 * and boots the Bluetooth stack.
150 */
151
152 /* LISTING_START(MainConfiguration): Init L2CAP, SM, ATT Server, and enable advertisements */
153
le_streamer_setup(void)154 static void le_streamer_setup(void){
155
156 l2cap_init();
157
158 // setup SM: Display only
159 sm_init();
160
161 #ifdef ENABLE_GATT_OVER_CLASSIC
162 // init SDP, create record for GATT and register with SDP
163 sdp_init();
164 memset(gatt_service_buffer, 0, sizeof(gatt_service_buffer));
165 gatt_create_sdp_record(gatt_service_buffer, 0x10001, ATT_SERVICE_GATT_SERVICE_START_HANDLE, ATT_SERVICE_GATT_SERVICE_END_HANDLE);
166 sdp_register_service(gatt_service_buffer);
167 printf("SDP service record size: %u\n", de_get_len(gatt_service_buffer));
168
169 // configure Classic GAP
170 gap_set_local_name("GATT Streamer BR/EDR 00:00:00:00:00:00");
171 gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
172 gap_discoverable_control(1);
173 #endif
174
175 // setup ATT server
176 att_server_init(profile_data, NULL, att_write_callback);
177
178 // register for HCI events
179 hci_event_callback_registration.callback = &hci_packet_handler;
180 hci_add_event_handler(&hci_event_callback_registration);
181
182 // register for ATT events
183 att_server_register_packet_handler(att_packet_handler);
184
185 // setup advertisements
186 uint16_t adv_int_min = 0x0030;
187 uint16_t adv_int_max = 0x0030;
188 uint8_t adv_type = 0;
189 bd_addr_t null_addr;
190 memset(null_addr, 0, 6);
191 gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
192 gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
193 gap_advertisements_enable(1);
194
195 // init client state
196 init_connections();
197 }
198 /* LISTING_END */
199
200 /*
201 * @section Track throughput
202 * @text We calculate the throughput by setting a start time and measuring the amount of
203 * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s
204 * and reset the counter and start time.
205 */
206
207 /* LISTING_START(tracking): Tracking throughput */
208
test_reset(le_streamer_connection_t * context)209 static void test_reset(le_streamer_connection_t * context){
210 context->test_data_start = btstack_run_loop_get_time_ms();
211 context->test_data_sent = 0;
212 }
213
test_track_sent(le_streamer_connection_t * context,int bytes_sent)214 static void test_track_sent(le_streamer_connection_t * context, int bytes_sent){
215 context->test_data_sent += bytes_sent;
216 // evaluate
217 uint32_t now = btstack_run_loop_get_time_ms();
218 uint32_t time_passed = now - context->test_data_start;
219 if (time_passed < REPORT_INTERVAL_MS) return;
220 // print speed
221 int bytes_per_second = context->test_data_sent * 1000 / time_passed;
222 printf("%c: %"PRIu32" bytes sent-> %u.%03u kB/s\n", context->name, context->test_data_sent, bytes_per_second / 1000, bytes_per_second % 1000);
223
224 // restart
225 context->test_data_start = now;
226 context->test_data_sent = 0;
227 }
228 /* LISTING_END(tracking): Tracking throughput */
229
230 /*
231 * @section HCI Packet Handler
232 *
233 * @text The packet handler is used track incoming connections and to stop notifications on disconnect
234 * It is also a good place to request the connection parameter update as indicated
235 * in the commented code block.
236 */
237
238 /* LISTING_START(hciPacketHandler): Packet Handler */
hci_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)239 static void hci_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
240 UNUSED(channel);
241 UNUSED(size);
242
243 if (packet_type != HCI_EVENT_PACKET) return;
244
245 uint16_t conn_interval;
246 hci_con_handle_t con_handle;
247 static const char * const phy_names[] = {
248 "Reserved", "1 M", "2 M", "Codec"
249 };
250
251 switch (hci_event_packet_get_type(packet)) {
252 case BTSTACK_EVENT_STATE:
253 // BTstack activated, get started
254 if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
255 printf("To start the streaming, please run the le_streamer_client example on other device, or use some GATT Explorer, e.g. LightBlue, BLExplr.\n");
256 }
257 break;
258 case HCI_EVENT_DISCONNECTION_COMPLETE:
259 con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
260 printf("- LE Connection 0x%04x: disconnect, reason %02x\n", con_handle, hci_event_disconnection_complete_get_reason(packet));
261 break;
262 case HCI_EVENT_META_GAP:
263 switch (hci_event_gap_meta_get_subevent_code(packet)) {
264 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
265 // print connection parameters (without using float operations)
266 con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet);
267 conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet);
268 printf("- LE Connection 0x%04x: connected - connection interval %u.%02u ms, latency %u\n", con_handle, conn_interval * 125 / 100,
269 25 * (conn_interval & 3), gap_subevent_le_connection_complete_get_conn_latency(packet));
270
271 // request min con interval 15 ms for iOS 11+
272 printf("- LE Connection 0x%04x: request 15 ms connection interval\n", con_handle);
273 gap_request_connection_parameter_update(con_handle, 12, 12, 4, 0x0048);
274 break;
275 default:
276 break;
277 }
278 break;
279 case HCI_EVENT_LE_META:
280 switch (hci_event_le_meta_get_subevent_code(packet)) {
281 case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
282 // print connection parameters (without using float operations)
283 con_handle = hci_subevent_le_connection_update_complete_get_connection_handle(packet);
284 conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
285 printf("- LE Connection 0x%04x: connection update - connection interval %u.%02u ms, latency %u\n", con_handle, conn_interval * 125 / 100,
286 25 * (conn_interval & 3), hci_subevent_le_connection_update_complete_get_conn_latency(packet));
287 break;
288 case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE:
289 con_handle = hci_subevent_le_data_length_change_get_connection_handle(packet);
290 printf("- LE Connection 0x%04x: data length change - max %u bytes per packet\n", con_handle,
291 hci_subevent_le_data_length_change_get_max_tx_octets(packet));
292 break;
293 case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE:
294 con_handle = hci_subevent_le_phy_update_complete_get_connection_handle(packet);
295 printf("- LE Connection 0x%04x: PHY update - using LE %s PHY now\n", con_handle,
296 phy_names[hci_subevent_le_phy_update_complete_get_tx_phy(packet)]);
297 break;
298 default:
299 break;
300 }
301 break;
302
303 default:
304 break;
305 }
306 }
307
308 /* LISTING_END */
309
310 /*
311 * @section ATT Packet Handler
312 *
313 * @text The packet handler is used to track the ATT MTU Exchange and trigger ATT send
314 */
315
316 /* LISTING_START(attPacketHandler): Packet Handler */
att_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)317 static void att_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
318 UNUSED(channel);
319 UNUSED(size);
320
321 int mtu;
322 le_streamer_connection_t * context;
323 switch (packet_type) {
324 case HCI_EVENT_PACKET:
325 switch (hci_event_packet_get_type(packet)) {
326 case ATT_EVENT_CONNECTED:
327 // setup new
328 context = connection_for_conn_handle(HCI_CON_HANDLE_INVALID);
329 if (!context) break;
330 context->counter = 'A';
331 context->connection_handle = att_event_connected_get_handle(packet);
332 context->test_data_len = btstack_min(att_server_get_mtu(context->connection_handle) - 3, sizeof(context->test_data));
333 printf("%c: ATT connected, handle 0x%04x, test data len %u\n", context->name, context->connection_handle, context->test_data_len);
334 break;
335 case ATT_EVENT_MTU_EXCHANGE_COMPLETE:
336 mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3;
337 context = connection_for_conn_handle(att_event_mtu_exchange_complete_get_handle(packet));
338 if (!context) break;
339 context->test_data_len = btstack_min(mtu - 3, sizeof(context->test_data));
340 printf("%c: ATT MTU = %u => use test data of len %u\n", context->name, mtu, context->test_data_len);
341 break;
342 case ATT_EVENT_CAN_SEND_NOW:
343 streamer();
344 break;
345 case ATT_EVENT_DISCONNECTED:
346 context = connection_for_conn_handle(att_event_disconnected_get_handle(packet));
347 if (!context) break;
348 // free connection
349 printf("%c: ATT disconnected, handle 0x%04x\n", context->name, context->connection_handle);
350 context->le_notification_enabled = 0;
351 context->connection_handle = HCI_CON_HANDLE_INVALID;
352 break;
353 default:
354 break;
355 }
356 break;
357 default:
358 break;
359 }
360 }
361 /* LISTING_END */
362
363 /*
364 * @section Streamer
365 *
366 * @text The streamer function checks if notifications are enabled and if a notification can be sent now.
367 * It creates some test data - a single letter that gets increased every time - and tracks the data sent.
368 */
369
370 /* LISTING_START(streamer): Streaming code */
streamer(void)371 static void streamer(void){
372
373 // find next active streaming connection
374 int old_connection_index = connection_index;
375 while (1){
376 // active found?
377 if ((le_streamer_connections[connection_index].connection_handle != HCI_CON_HANDLE_INVALID) &&
378 (le_streamer_connections[connection_index].le_notification_enabled)) break;
379
380 // check next
381 next_connection_index();
382
383 // none found
384 if (connection_index == old_connection_index) return;
385 }
386
387 le_streamer_connection_t * context = &le_streamer_connections[connection_index];
388
389 // create test data
390 context->counter++;
391 if (context->counter > 'Z') context->counter = 'A';
392 memset(context->test_data, context->counter, context->test_data_len);
393
394 // send
395 att_server_notify(context->connection_handle, context->value_handle, (uint8_t*) context->test_data, context->test_data_len);
396
397 // track
398 test_track_sent(context, context->test_data_len);
399
400 // request next send event
401 att_server_request_can_send_now_event(context->connection_handle);
402
403 // check next
404 next_connection_index();
405 }
406 /* LISTING_END */
407
408 /*
409 * @section ATT Write
410 *
411 * @text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification
412 * and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored.
413 * If notifications get enabled, an ATT_EVENT_CAN_SEND_NOW is requested. See Listing attWrite.
414 */
415
416 /* LISTING_START(attWrite): ATT Write */
att_write_callback(hci_con_handle_t con_handle,uint16_t att_handle,uint16_t transaction_mode,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)417 static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
418 UNUSED(offset);
419
420 // printf("att_write_callback att_handle 0x%04x, transaction mode %u\n", att_handle, transaction_mode);
421 if (transaction_mode != ATT_TRANSACTION_MODE_NONE) return 0;
422 le_streamer_connection_t * context = connection_for_conn_handle(con_handle);
423 switch(att_handle){
424 case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE:
425 case ATT_CHARACTERISTIC_0000FF12_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE:
426 context->le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
427 printf("%c: Notifications enabled %u\n", context->name, context->le_notification_enabled);
428 if (context->le_notification_enabled){
429 switch (att_handle){
430 case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE:
431 context->value_handle = ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE;
432 break;
433 case ATT_CHARACTERISTIC_0000FF12_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE:
434 context->value_handle = ATT_CHARACTERISTIC_0000FF12_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE;
435 break;
436 default:
437 break;
438 }
439 att_server_request_can_send_now_event(context->connection_handle);
440 }
441 test_reset(context);
442 break;
443 case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE:
444 case ATT_CHARACTERISTIC_0000FF12_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE:
445 test_track_sent(context, buffer_size);
446 break;
447 default:
448 printf("Write to 0x%04x, len %u\n", att_handle, buffer_size);
449 break;
450 }
451 return 0;
452 }
453 /* LISTING_END */
454
455 int btstack_main(void);
btstack_main(void)456 int btstack_main(void)
457 {
458 le_streamer_setup();
459
460 // turn on!
461 hci_power_control(HCI_POWER_ON);
462
463 return 0;
464 }
465 /* EXAMPLE_END */
466