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 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 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 */ 137 static void streamer(void){ 138 139 if (le_data_channel_connection.cid == 0) return; 140 141 // create test data 142 le_data_channel_connection.counter++; 143 if (le_data_channel_connection.counter > 'Z') le_data_channel_connection.counter = 'A'; 144 memset(le_data_channel_connection.test_data, le_data_channel_connection.counter, le_data_channel_connection.test_data_len); 145 146 // send 147 l2cap_cbm_send_data(le_data_channel_connection.cid, (uint8_t *) le_data_channel_connection.test_data, le_data_channel_connection.test_data_len); 148 149 // track 150 test_track_data(&le_data_channel_connection, le_data_channel_connection.test_data_len); 151 152 // request another packet 153 l2cap_cbm_request_can_send_now_event(le_data_channel_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 */ 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_LE_META: 193 switch (hci_event_le_meta_get_subevent_code(packet)) { 194 case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: 195 // print connection parameters (without using float operations) 196 conn_interval = hci_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, hci_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, 0, 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 case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: 207 // print connection parameters (without using float operations) 208 conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); 209 printf("LE Connection Update:\n"); 210 printf("%c: Connection Interval: %u.%02u ms\n", le_cbm_connection.name, conn_interval * 125 / 100, 25 * (conn_interval & 3)); 211 printf("%c: Connection Latency: %u\n", le_cbm_connection.name, hci_subevent_le_connection_update_complete_get_conn_latency(packet)); 212 break; 213 default: 214 break; 215 } 216 break; 217 218 case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: 219 printf("L2CAP Connection Parameter Update Complete, result: 0x%02x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); 220 break; 221 222 // LE Credit-based Flow-Control Mode 223 224 case L2CAP_EVENT_CBM_INCOMING_CONNECTION: 225 psm = l2cap_event_cbm_incoming_connection_get_psm(packet); 226 cid = l2cap_event_cbm_incoming_connection_get_local_cid(packet); 227 if (psm != TSPX_le_psm) break; 228 printf("L2CAP: Accepting incoming connection request for 0x%02x, PSM %02x\n", cid, psm); 229 l2cap_cbm_accept_connection(cid, data_channel_buffer, sizeof(data_channel_buffer), initial_credits); 230 break; 231 232 case L2CAP_EVENT_CBM_CHANNEL_OPENED: 233 // inform about new l2cap connection 234 l2cap_event_cbm_channel_opened_get_address(packet, event_address); 235 psm = l2cap_event_cbm_channel_opened_get_psm(packet); 236 cid = l2cap_event_cbm_channel_opened_get_local_cid(packet); 237 handle = l2cap_event_cbm_channel_opened_get_handle(packet); 238 status = l2cap_event_cbm_channel_opened_get_status(packet); 239 if (status == ERROR_CODE_SUCCESS) { 240 printf("L2CAP: Channel successfully opened: %s, handle 0x%04x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", 241 bd_addr_to_str(event_address), handle, psm, cid, little_endian_read_16(packet, 15)); 242 // setup new 243 le_cbm_connection.counter = 'A'; 244 le_cbm_connection.cid = cid; 245 le_cbm_connection.connection_handle = handle; 246 le_cbm_connection.test_data_len = btstack_min(l2cap_event_cbm_channel_opened_get_remote_mtu(packet), sizeof(le_cbm_connection.test_data)); 247 printf("Test packet size: %u\n", le_cbm_connection.test_data_len); 248 test_reset(&le_cbm_connection); 249 #ifdef TEST_STREAM_DATA 250 l2cap_cbm_request_can_send_now_event(le_data_channel_connection.cid); 251 #endif 252 } else { 253 printf("L2CAP: Connection to device %s failed, status 0x%02x\n", bd_addr_to_str(event_address), status); 254 } 255 break; 256 257 case L2CAP_EVENT_CHANNEL_CLOSED: 258 printf("L2CAP: Channel closed\n"); 259 le_cbm_connection.cid = 0; 260 break; 261 262 #ifdef TEST_STREAM_DATA 263 case L2CAP_EVENT_CBM_CAN_SEND_NOW: 264 streamer(); 265 break; 266 #endif 267 268 default: 269 break; 270 } 271 break; 272 273 case L2CAP_DATA_PACKET: 274 test_track_data(&le_cbm_connection, size); 275 break; 276 277 default: 278 break; 279 } 280 } 281 282 /* 283 * @section SM Packet Handler 284 * 285 * @text The packet handler is used to handle pairing requests 286 */ 287 static void sm_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 288 UNUSED(channel); 289 UNUSED(size); 290 291 if (packet_type != HCI_EVENT_PACKET) return; 292 293 switch (hci_event_packet_get_type(packet)) { 294 case SM_EVENT_JUST_WORKS_REQUEST: 295 printf("Just Works requested\n"); 296 sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); 297 break; 298 case SM_EVENT_NUMERIC_COMPARISON_REQUEST: 299 printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); 300 sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); 301 break; 302 case SM_EVENT_PASSKEY_DISPLAY_NUMBER: 303 printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); 304 break; 305 default: 306 break; 307 } 308 } 309 310 /* @section Main Application Setup 311 * 312 * @text Listing MainConfiguration shows main application code. 313 * It initializes L2CAP, the Security Manager, and configures the ATT Server with the pre-compiled 314 * ATT Database generated from $le_credit_based_flow_control_mode_server.gatt$. Finally, it configures the advertisements 315 * and boots the Bluetooth stack. 316 */ 317 318 int btstack_main(void); 319 int btstack_main(void) 320 { 321 l2cap_init(); 322 323 // setup SM: Display only 324 sm_init(); 325 326 // setup ATT server: iOS disconnects if ATT MTU Exchange fails 327 att_server_init(profile_data, NULL, NULL); 328 329 // register for HCI events 330 hci_event_callback_registration.callback = &packet_handler; 331 hci_add_event_handler(&hci_event_callback_registration); 332 333 // register for SM events 334 sm_event_callback_registration.callback = &sm_packet_handler; 335 sm_add_event_handler(&sm_event_callback_registration); 336 337 // register for L2CAP events 338 l2cap_event_callback_registration.callback = &packet_handler; 339 l2cap_add_event_handler(&l2cap_event_callback_registration); 340 341 // le data channel setup 342 l2cap_cbm_register_service(&packet_handler, TSPX_le_psm, LEVEL_0); 343 344 // setup advertisements 345 uint16_t adv_int_min = 0x0030; 346 uint16_t adv_int_max = 0x0030; 347 uint8_t adv_type = 0; 348 bd_addr_t null_addr; 349 memset(null_addr, 0, 6); 350 gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); 351 gap_advertisements_set_data(adv_data_len, (uint8_t *) adv_data); 352 gap_advertisements_enable(1); 353 354 // turn on! 355 hci_power_control(HCI_POWER_ON); 356 357 return 0; 358 } 359 /* EXAMPLE_END */ 360