1ebdf3c68SMilanka Ringwald /* 2ebdf3c68SMilanka Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3ebdf3c68SMilanka Ringwald * 4ebdf3c68SMilanka Ringwald * Redistribution and use in source and binary forms, with or without 5ebdf3c68SMilanka Ringwald * modification, are permitted provided that the following conditions 6ebdf3c68SMilanka Ringwald * are met: 7ebdf3c68SMilanka Ringwald * 8ebdf3c68SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 9ebdf3c68SMilanka Ringwald * notice, this list of conditions and the following disclaimer. 10ebdf3c68SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11ebdf3c68SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 12ebdf3c68SMilanka Ringwald * documentation and/or other materials provided with the distribution. 13ebdf3c68SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 14ebdf3c68SMilanka Ringwald * contributors may be used to endorse or promote products derived 15ebdf3c68SMilanka Ringwald * from this software without specific prior written permission. 16ebdf3c68SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 17ebdf3c68SMilanka Ringwald * personal benefit and not for any commercial purpose or for 18ebdf3c68SMilanka Ringwald * monetary gain. 19ebdf3c68SMilanka Ringwald * 20ebdf3c68SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21ebdf3c68SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22ebdf3c68SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25ebdf3c68SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26ebdf3c68SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27ebdf3c68SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28ebdf3c68SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29ebdf3c68SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30ebdf3c68SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31ebdf3c68SMilanka Ringwald * SUCH DAMAGE. 32ebdf3c68SMilanka Ringwald * 33ebdf3c68SMilanka Ringwald * Please inquire about commercial licensing options at 34ebdf3c68SMilanka Ringwald * [email protected] 35ebdf3c68SMilanka Ringwald * 36ebdf3c68SMilanka Ringwald */ 37ebdf3c68SMilanka Ringwald 38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "nordic_spp_le_streamer.c" 39ebdf3c68SMilanka Ringwald 40ebdf3c68SMilanka Ringwald // ***************************************************************************** 41ec8ae085SMilanka Ringwald /* EXAMPLE_START(nordic_spp_le_streamer): LE Nordic SPP-like Streamer Server 42ebdf3c68SMilanka Ringwald * 43ebdf3c68SMilanka Ringwald * @text All newer operating systems provide GATT Client functionality. 44ebdf3c68SMilanka Ringwald * This example shows how to get a maximal throughput via BLE: 45ebdf3c68SMilanka Ringwald * - send whenever possible, 46ebdf3c68SMilanka Ringwald * - use the max ATT MTU. 47ebdf3c68SMilanka Ringwald * 48ebdf3c68SMilanka Ringwald * @text In theory, we should also update the connection parameters, but we already get 49ebdf3c68SMilanka Ringwald * a connection interval of 30 ms and there's no public way to use a shorter 50ebdf3c68SMilanka Ringwald * interval with iOS (if we're not implementing an HID device). 51ebdf3c68SMilanka Ringwald * 52ebdf3c68SMilanka Ringwald * @text Note: To start the streaming, run the example. 53ebdf3c68SMilanka Ringwald * On remote device use some GATT Explorer, e.g. LightBlue, BLExplr to enable notifications. 54ebdf3c68SMilanka Ringwald */ 55ebdf3c68SMilanka Ringwald // ***************************************************************************** 56ebdf3c68SMilanka Ringwald 57ebdf3c68SMilanka Ringwald #include <inttypes.h> 58ebdf3c68SMilanka Ringwald #include <stdint.h> 59ebdf3c68SMilanka Ringwald #include <stdio.h> 60ebdf3c68SMilanka Ringwald #include <stdlib.h> 61ebdf3c68SMilanka Ringwald #include <string.h> 62ebdf3c68SMilanka Ringwald 63ebdf3c68SMilanka Ringwald #include "btstack.h" 64ebdf3c68SMilanka Ringwald #include "ble/gatt-service/nordic_spp_service_server.h" 65ebdf3c68SMilanka Ringwald 66a63a688aSMatthias Ringwald // nordic_spp_le_streamer.gatt contains the declaration of the provided GATT Services + Characteristics 67a63a688aSMatthias Ringwald // nordic_spp_le_streamer.h contains the binary representation of nordic_spp_le_streamer.gatt 68a63a688aSMatthias Ringwald // it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py nordic_spp_le_streamer.gatt nordic_spp_le_streamer.h 69a63a688aSMatthias Ringwald // it needs to be regenerated when the GATT Database declared in nordic_spp_le_streamer.gatt file is modified 70a63a688aSMatthias Ringwald #include "nordic_spp_le_streamer.h" 71a63a688aSMatthias Ringwald 72ebdf3c68SMilanka Ringwald #define REPORT_INTERVAL_MS 3000 73ebdf3c68SMilanka Ringwald #define MAX_NR_CONNECTIONS 3 74ebdf3c68SMilanka Ringwald 75ebdf3c68SMilanka Ringwald const uint8_t adv_data[] = { 76ebdf3c68SMilanka Ringwald // Flags general discoverable, BR/EDR not supported 77ebdf3c68SMilanka Ringwald 2, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, 78ebdf3c68SMilanka Ringwald // Name 79ba7944beSMilanka Ringwald 8, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'n', 'R', 'F',' ', 'S', 'P', 'P', 80ebdf3c68SMilanka Ringwald // UUID ... 814163d2ebSMilanka Ringwald 17, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, 0x9e, 0xca, 0xdc, 0x24, 0xe, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 0x1, 0x0, 0x40, 0x6e, 82ebdf3c68SMilanka Ringwald }; 83ebdf3c68SMilanka Ringwald const uint8_t adv_data_len = sizeof(adv_data); 84ebdf3c68SMilanka Ringwald 85ebdf3c68SMilanka Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 86ebdf3c68SMilanka Ringwald 87ebdf3c68SMilanka Ringwald // support for multiple clients 88ebdf3c68SMilanka Ringwald typedef struct { 89ebdf3c68SMilanka Ringwald char name; 90ebdf3c68SMilanka Ringwald int le_notification_enabled; 91ebdf3c68SMilanka Ringwald hci_con_handle_t connection_handle; 92ebdf3c68SMilanka Ringwald int counter; 93ebdf3c68SMilanka Ringwald char test_data[200]; 94ebdf3c68SMilanka Ringwald int test_data_len; 95ebdf3c68SMilanka Ringwald uint32_t test_data_sent; 96ebdf3c68SMilanka Ringwald uint32_t test_data_start; 97ebdf3c68SMilanka Ringwald btstack_context_callback_registration_t send_request; 98ebdf3c68SMilanka Ringwald } nordic_spp_le_streamer_connection_t; 99ebdf3c68SMilanka Ringwald 100ebdf3c68SMilanka Ringwald static nordic_spp_le_streamer_connection_t nordic_spp_le_streamer_connections[MAX_NR_CONNECTIONS]; 101ebdf3c68SMilanka Ringwald 102ebdf3c68SMilanka Ringwald // round robin sending 103ebdf3c68SMilanka Ringwald static int connection_index; 104ebdf3c68SMilanka Ringwald 105ebdf3c68SMilanka Ringwald static void init_connections(void){ 106ebdf3c68SMilanka Ringwald // track connections 107ebdf3c68SMilanka Ringwald int i; 108ebdf3c68SMilanka Ringwald for (i=0;i<MAX_NR_CONNECTIONS;i++){ 109ebdf3c68SMilanka Ringwald nordic_spp_le_streamer_connections[i].connection_handle = HCI_CON_HANDLE_INVALID; 110ebdf3c68SMilanka Ringwald nordic_spp_le_streamer_connections[i].name = 'A' + i; 111ebdf3c68SMilanka Ringwald } 112ebdf3c68SMilanka Ringwald } 113ebdf3c68SMilanka Ringwald 114ebdf3c68SMilanka Ringwald static nordic_spp_le_streamer_connection_t * connection_for_conn_handle(hci_con_handle_t conn_handle){ 115ebdf3c68SMilanka Ringwald int i; 116ebdf3c68SMilanka Ringwald for (i=0;i<MAX_NR_CONNECTIONS;i++){ 117ebdf3c68SMilanka Ringwald if (nordic_spp_le_streamer_connections[i].connection_handle == conn_handle) return &nordic_spp_le_streamer_connections[i]; 118ebdf3c68SMilanka Ringwald } 119ebdf3c68SMilanka Ringwald return NULL; 120ebdf3c68SMilanka Ringwald } 121ebdf3c68SMilanka Ringwald 122ebdf3c68SMilanka Ringwald static void next_connection_index(void){ 123ebdf3c68SMilanka Ringwald connection_index++; 124ebdf3c68SMilanka Ringwald if (connection_index == MAX_NR_CONNECTIONS){ 125ebdf3c68SMilanka Ringwald connection_index = 0; 126ebdf3c68SMilanka Ringwald } 127ebdf3c68SMilanka Ringwald } 128ebdf3c68SMilanka Ringwald 129ebdf3c68SMilanka Ringwald /* 130ebdf3c68SMilanka Ringwald * @section Track throughput 131ebdf3c68SMilanka Ringwald * @text We calculate the throughput by setting a start time and measuring the amount of 132ebdf3c68SMilanka Ringwald * data sent. After a configurable REPORT_INTERVAL_MS, we print the throughput in kB/s 133ebdf3c68SMilanka Ringwald * and reset the counter and start time. 134ebdf3c68SMilanka Ringwald */ 135ebdf3c68SMilanka Ringwald 136ebdf3c68SMilanka Ringwald /* LISTING_START(tracking): Tracking throughput */ 137ebdf3c68SMilanka Ringwald 138ebdf3c68SMilanka Ringwald static void test_reset(nordic_spp_le_streamer_connection_t * context){ 139ebdf3c68SMilanka Ringwald context->test_data_start = btstack_run_loop_get_time_ms(); 140ebdf3c68SMilanka Ringwald context->test_data_sent = 0; 141ebdf3c68SMilanka Ringwald } 142ebdf3c68SMilanka Ringwald 143ebdf3c68SMilanka Ringwald static void test_track_sent(nordic_spp_le_streamer_connection_t * context, int bytes_sent){ 144ebdf3c68SMilanka Ringwald context->test_data_sent += bytes_sent; 145ebdf3c68SMilanka Ringwald // evaluate 146ebdf3c68SMilanka Ringwald uint32_t now = btstack_run_loop_get_time_ms(); 147ebdf3c68SMilanka Ringwald uint32_t time_passed = now - context->test_data_start; 148ebdf3c68SMilanka Ringwald if (time_passed < REPORT_INTERVAL_MS) return; 149ebdf3c68SMilanka Ringwald // print speed 150ebdf3c68SMilanka Ringwald int bytes_per_second = context->test_data_sent * 1000 / time_passed; 151ebdf3c68SMilanka Ringwald printf("%c: %"PRIu32" bytes sent-> %u.%03u kB/s\n", context->name, context->test_data_sent, bytes_per_second / 1000, bytes_per_second % 1000); 152ebdf3c68SMilanka Ringwald 153ebdf3c68SMilanka Ringwald // restart 154ebdf3c68SMilanka Ringwald context->test_data_start = now; 155ebdf3c68SMilanka Ringwald context->test_data_sent = 0; 156ebdf3c68SMilanka Ringwald } 157ebdf3c68SMilanka Ringwald /* LISTING_END(tracking): Tracking throughput */ 158ebdf3c68SMilanka Ringwald 159ebdf3c68SMilanka Ringwald /* 16050514142SMatthias Ringwald * @section HCI Packet Handler 161ebdf3c68SMilanka Ringwald * 16250514142SMatthias Ringwald * @text The packet handler prints the welcome message and requests a connection paramter update for LE Connections 163ebdf3c68SMilanka Ringwald */ 164ebdf3c68SMilanka Ringwald 165ebdf3c68SMilanka Ringwald /* LISTING_START(packetHandler): Packet Handler */ 16650514142SMatthias Ringwald static void hci_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 167ebdf3c68SMilanka Ringwald UNUSED(channel); 168ebdf3c68SMilanka Ringwald UNUSED(size); 169ebdf3c68SMilanka Ringwald 170ebdf3c68SMilanka Ringwald uint16_t conn_interval; 17150514142SMatthias Ringwald hci_con_handle_t con_handle; 17250514142SMatthias Ringwald 1737bbeb3adSMilanka Ringwald if (packet_type != HCI_EVENT_PACKET) return; 1747bbeb3adSMilanka Ringwald 175ebdf3c68SMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 176ebdf3c68SMilanka Ringwald case BTSTACK_EVENT_STATE: 177ebdf3c68SMilanka Ringwald // BTstack activated, get started 178ebdf3c68SMilanka Ringwald if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { 17950514142SMatthias Ringwald printf("To start the streaming, please run nRF Toolbox -> UART to connect.\n"); 180ebdf3c68SMilanka Ringwald } 181ebdf3c68SMilanka Ringwald break; 182*bba48196SMatthias Ringwald case HCI_EVENT_META_GAP: 183*bba48196SMatthias Ringwald switch (hci_event_gap_meta_get_subevent_code(packet)) { 184*bba48196SMatthias Ringwald case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: 185*bba48196SMatthias Ringwald switch (hci_event_gap_meta_get_subevent_code(packet)) { 186*bba48196SMatthias Ringwald case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: 18750514142SMatthias Ringwald // print connection parameters (without using float operations) 188*bba48196SMatthias Ringwald con_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); 189*bba48196SMatthias Ringwald conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet); 19050514142SMatthias Ringwald printf("LE Connection - Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); 191*bba48196SMatthias Ringwald printf("LE Connection - Connection Latency: %u\n", gap_subevent_le_connection_complete_get_conn_latency(packet)); 19250514142SMatthias Ringwald 19350514142SMatthias Ringwald // request min con interval 15 ms for iOS 11+ 19450514142SMatthias Ringwald printf("LE Connection - Request 15 ms connection interval\n"); 195a6622c57SMatthias Ringwald gap_request_connection_parameter_update(con_handle, 12, 12, 4, 0x0048); 19650514142SMatthias Ringwald break; 197*bba48196SMatthias Ringwald default: 198*bba48196SMatthias Ringwald break; 199*bba48196SMatthias Ringwald } 200*bba48196SMatthias Ringwald break; 201*bba48196SMatthias Ringwald default: 202*bba48196SMatthias Ringwald break; 203*bba48196SMatthias Ringwald } 204*bba48196SMatthias Ringwald break; 205*bba48196SMatthias Ringwald 206*bba48196SMatthias Ringwald case HCI_EVENT_LE_META: 207*bba48196SMatthias Ringwald switch (hci_event_le_meta_get_subevent_code(packet)) { 20850514142SMatthias Ringwald case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: 20950514142SMatthias Ringwald // print connection parameters (without using float operations) 21050514142SMatthias Ringwald con_handle = hci_subevent_le_connection_update_complete_get_connection_handle(packet); 21150514142SMatthias Ringwald conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); 21250514142SMatthias Ringwald printf("LE Connection - Connection Param update - connection interval %u.%02u ms, latency %u\n", conn_interval * 125 / 100, 21350514142SMatthias Ringwald 25 * (conn_interval & 3), hci_subevent_le_connection_update_complete_get_conn_latency(packet)); 21450514142SMatthias Ringwald break; 21550514142SMatthias Ringwald default: 21650514142SMatthias Ringwald break; 21750514142SMatthias Ringwald } 21850514142SMatthias Ringwald break; 21950514142SMatthias Ringwald default: 22050514142SMatthias Ringwald break; 22150514142SMatthias Ringwald } 22250514142SMatthias Ringwald } 22350514142SMatthias Ringwald /* LISTING_END */ 22450514142SMatthias Ringwald 22550514142SMatthias Ringwald /* 22650514142SMatthias Ringwald * @section ATT Packet Handler 22750514142SMatthias Ringwald * 22850514142SMatthias Ringwald * @text The packet handler is used to setup and tear down the spp-over-gatt connection and its MTU 22950514142SMatthias Ringwald */ 23050514142SMatthias Ringwald 23150514142SMatthias Ringwald /* LISTING_START(packetHandler): Packet Handler */ 23250514142SMatthias Ringwald static void att_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 23350514142SMatthias Ringwald UNUSED(channel); 23450514142SMatthias Ringwald UNUSED(size); 23550514142SMatthias Ringwald 2367bbeb3adSMilanka Ringwald if (packet_type != HCI_EVENT_PACKET) return; 2377bbeb3adSMilanka Ringwald 23850514142SMatthias Ringwald int mtu; 23950514142SMatthias Ringwald nordic_spp_le_streamer_connection_t * context; 2407bbeb3adSMilanka Ringwald 24150514142SMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 24250514142SMatthias Ringwald case ATT_EVENT_CONNECTED: 243ebdf3c68SMilanka Ringwald // setup new 244ebdf3c68SMilanka Ringwald context = connection_for_conn_handle(HCI_CON_HANDLE_INVALID); 245ebdf3c68SMilanka Ringwald if (!context) break; 246ebdf3c68SMilanka Ringwald context->counter = 'A'; 247ebdf3c68SMilanka Ringwald context->test_data_len = ATT_DEFAULT_MTU - 4; // -1 for nordic 0x01 packet type 24850514142SMatthias Ringwald context->connection_handle = att_event_connected_get_handle(packet); 249ebdf3c68SMilanka Ringwald break; 250ebdf3c68SMilanka Ringwald case ATT_EVENT_MTU_EXCHANGE_COMPLETE: 251ebdf3c68SMilanka Ringwald mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; 252ebdf3c68SMilanka Ringwald context = connection_for_conn_handle(att_event_mtu_exchange_complete_get_handle(packet)); 253ebdf3c68SMilanka Ringwald if (!context) break; 254ebdf3c68SMilanka Ringwald context->test_data_len = btstack_min(mtu - 3, sizeof(context->test_data)); 255ebdf3c68SMilanka Ringwald printf("%c: ATT MTU = %u => use test data of len %u\n", context->name, mtu, context->test_data_len); 256ebdf3c68SMilanka Ringwald break; 25750514142SMatthias Ringwald case ATT_EVENT_DISCONNECTED: 25850514142SMatthias Ringwald context = connection_for_conn_handle(att_event_disconnected_get_handle(packet)); 25950514142SMatthias Ringwald if (!context) break; 26050514142SMatthias Ringwald // free connection 26150514142SMatthias Ringwald printf("%c: Disconnect\n", context->name); 26250514142SMatthias Ringwald context->le_notification_enabled = 0; 26350514142SMatthias Ringwald context->connection_handle = HCI_CON_HANDLE_INVALID; 26450514142SMatthias Ringwald break; 265ebdf3c68SMilanka Ringwald default: 266ebdf3c68SMilanka Ringwald break; 267ebdf3c68SMilanka Ringwald } 268ebdf3c68SMilanka Ringwald } 269ebdf3c68SMilanka Ringwald /* LISTING_END */ 27050514142SMatthias Ringwald 27150514142SMatthias Ringwald 27250514142SMatthias Ringwald 273ebdf3c68SMilanka Ringwald /* 274ebdf3c68SMilanka Ringwald * @section Streamer 275ebdf3c68SMilanka Ringwald * 276ebdf3c68SMilanka Ringwald * @text The streamer function checks if notifications are enabled and if a notification can be sent now. 277ebdf3c68SMilanka Ringwald * It creates some test data - a single letter that gets increased every time - and tracks the data sent. 278ebdf3c68SMilanka Ringwald */ 279ebdf3c68SMilanka Ringwald 280ebdf3c68SMilanka Ringwald /* LISTING_START(streamer): Streaming code */ 281ebdf3c68SMilanka Ringwald static void nordic_can_send(void * some_context){ 282ebdf3c68SMilanka Ringwald UNUSED(some_context); 283ebdf3c68SMilanka Ringwald 284ebdf3c68SMilanka Ringwald // find next active streaming connection 285ebdf3c68SMilanka Ringwald int old_connection_index = connection_index; 286ebdf3c68SMilanka Ringwald while (1){ 287ebdf3c68SMilanka Ringwald // active found? 288ebdf3c68SMilanka Ringwald if ((nordic_spp_le_streamer_connections[connection_index].connection_handle != HCI_CON_HANDLE_INVALID) && 289ebdf3c68SMilanka Ringwald (nordic_spp_le_streamer_connections[connection_index].le_notification_enabled)) break; 290ebdf3c68SMilanka Ringwald 291ebdf3c68SMilanka Ringwald // check next 292ebdf3c68SMilanka Ringwald next_connection_index(); 293ebdf3c68SMilanka Ringwald 294ebdf3c68SMilanka Ringwald // none found 295ebdf3c68SMilanka Ringwald if (connection_index == old_connection_index) return; 296ebdf3c68SMilanka Ringwald } 297ebdf3c68SMilanka Ringwald 298ebdf3c68SMilanka Ringwald nordic_spp_le_streamer_connection_t * context = &nordic_spp_le_streamer_connections[connection_index]; 299ebdf3c68SMilanka Ringwald 300ebdf3c68SMilanka Ringwald // create test data 301ebdf3c68SMilanka Ringwald context->counter++; 302ebdf3c68SMilanka Ringwald if (context->counter > 'Z') context->counter = 'A'; 303ebdf3c68SMilanka Ringwald memset(context->test_data, context->counter, context->test_data_len); 304ebdf3c68SMilanka Ringwald 305ebdf3c68SMilanka Ringwald // send 306ebdf3c68SMilanka Ringwald nordic_spp_service_server_send(context->connection_handle, (uint8_t*) context->test_data, context->test_data_len); 307ebdf3c68SMilanka Ringwald 308ebdf3c68SMilanka Ringwald // track 309ebdf3c68SMilanka Ringwald test_track_sent(context, context->test_data_len); 310ebdf3c68SMilanka Ringwald 311ebdf3c68SMilanka Ringwald // request next send event 312ebdf3c68SMilanka Ringwald nordic_spp_service_server_request_can_send_now(&context->send_request, context->connection_handle); 313ebdf3c68SMilanka Ringwald 314ebdf3c68SMilanka Ringwald // check next 315ebdf3c68SMilanka Ringwald next_connection_index(); 316ebdf3c68SMilanka Ringwald } 317ebdf3c68SMilanka Ringwald /* LISTING_END */ 318ebdf3c68SMilanka Ringwald 319257f2b00SMatthias Ringwald static void nordic_spp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 320257f2b00SMatthias Ringwald hci_con_handle_t con_handle; 321257f2b00SMatthias Ringwald nordic_spp_le_streamer_connection_t * context; 322257f2b00SMatthias Ringwald switch (packet_type){ 323257f2b00SMatthias Ringwald case HCI_EVENT_PACKET: 324257f2b00SMatthias Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META) break; 325257f2b00SMatthias Ringwald switch (hci_event_gattservice_meta_get_subevent_code(packet)){ 326257f2b00SMatthias Ringwald case GATTSERVICE_SUBEVENT_SPP_SERVICE_CONNECTED: 327257f2b00SMatthias Ringwald con_handle = gattservice_subevent_spp_service_connected_get_con_handle(packet); 328257f2b00SMatthias Ringwald context = connection_for_conn_handle(con_handle); 329257f2b00SMatthias Ringwald if (!context) break; 330ebdf3c68SMilanka Ringwald context->le_notification_enabled = 1; 331ebdf3c68SMilanka Ringwald test_reset(context); 332ebdf3c68SMilanka Ringwald context->send_request.callback = &nordic_can_send; 333ebdf3c68SMilanka Ringwald nordic_spp_service_server_request_can_send_now(&context->send_request, context->connection_handle); 334257f2b00SMatthias Ringwald break; 335257f2b00SMatthias Ringwald case GATTSERVICE_SUBEVENT_SPP_SERVICE_DISCONNECTED: 336257f2b00SMatthias Ringwald con_handle = HCI_CON_HANDLE_INVALID; 337257f2b00SMatthias Ringwald context = connection_for_conn_handle(con_handle); 338257f2b00SMatthias Ringwald if (!context) break; 339257f2b00SMatthias Ringwald context->le_notification_enabled = 0; 340257f2b00SMatthias Ringwald break; 341257f2b00SMatthias Ringwald default: 342257f2b00SMatthias Ringwald break; 343257f2b00SMatthias Ringwald } 344257f2b00SMatthias Ringwald break; 345257f2b00SMatthias Ringwald case RFCOMM_DATA_PACKET: 346ebdf3c68SMilanka Ringwald printf("RECV: "); 347257f2b00SMatthias Ringwald printf_hexdump(packet, size); 348005e7bd2SMatthias Ringwald context = connection_for_conn_handle((hci_con_handle_t) channel); 349005e7bd2SMatthias Ringwald if (!context) break; 350ebdf3c68SMilanka Ringwald test_track_sent(context, size); 351257f2b00SMatthias Ringwald break; 352257f2b00SMatthias Ringwald default: 353257f2b00SMatthias Ringwald break; 354ebdf3c68SMilanka Ringwald } 355ebdf3c68SMilanka Ringwald } 356ebdf3c68SMilanka Ringwald 357ebdf3c68SMilanka Ringwald int btstack_main(void); 358ba7944beSMilanka Ringwald int btstack_main(void){ 359ebdf3c68SMilanka Ringwald // register for HCI events 36050514142SMatthias Ringwald hci_event_callback_registration.callback = &hci_packet_handler; 361ebdf3c68SMilanka Ringwald hci_add_event_handler(&hci_event_callback_registration); 362ebdf3c68SMilanka Ringwald 363ebdf3c68SMilanka Ringwald l2cap_init(); 364ebdf3c68SMilanka Ringwald 365ebdf3c68SMilanka Ringwald // setup SM: Display only 366ebdf3c68SMilanka Ringwald sm_init(); 367ebdf3c68SMilanka Ringwald 368ebdf3c68SMilanka Ringwald // setup ATT server 369ebdf3c68SMilanka Ringwald att_server_init(profile_data, NULL, NULL); 370ebdf3c68SMilanka Ringwald 371ebdf3c68SMilanka Ringwald // setup Nordic SPP service 372257f2b00SMatthias Ringwald nordic_spp_service_server_init(&nordic_spp_packet_handler); 373ebdf3c68SMilanka Ringwald 37450514142SMatthias Ringwald // register for ATT events 37550514142SMatthias Ringwald att_server_register_packet_handler(att_packet_handler); 37650514142SMatthias Ringwald 377ebdf3c68SMilanka Ringwald // setup advertisements 378ebdf3c68SMilanka Ringwald uint16_t adv_int_min = 0x0030; 379ebdf3c68SMilanka Ringwald uint16_t adv_int_max = 0x0030; 380ebdf3c68SMilanka Ringwald uint8_t adv_type = 0; 381ebdf3c68SMilanka Ringwald bd_addr_t null_addr; 382ebdf3c68SMilanka Ringwald memset(null_addr, 0, 6); 383ebdf3c68SMilanka Ringwald gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); 384ebdf3c68SMilanka Ringwald gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); 385ebdf3c68SMilanka Ringwald gap_advertisements_enable(1); 386ebdf3c68SMilanka Ringwald 387ebdf3c68SMilanka Ringwald // init client state 388ebdf3c68SMilanka Ringwald init_connections(); 389ebdf3c68SMilanka Ringwald 390ebdf3c68SMilanka Ringwald // turn on! 391ebdf3c68SMilanka Ringwald hci_power_control(HCI_POWER_ON); 392ebdf3c68SMilanka Ringwald 393ebdf3c68SMilanka Ringwald return 0; 394ebdf3c68SMilanka Ringwald } 395ebdf3c68SMilanka Ringwald /* EXAMPLE_END */ 396