1*bcf00d8fSMatthias Ringwald /* 2*bcf00d8fSMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3*bcf00d8fSMatthias Ringwald * 4*bcf00d8fSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*bcf00d8fSMatthias Ringwald * modification, are permitted provided that the following conditions 6*bcf00d8fSMatthias Ringwald * are met: 7*bcf00d8fSMatthias Ringwald * 8*bcf00d8fSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*bcf00d8fSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*bcf00d8fSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*bcf00d8fSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*bcf00d8fSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*bcf00d8fSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*bcf00d8fSMatthias Ringwald * contributors may be used to endorse or promote products derived 15*bcf00d8fSMatthias Ringwald * from this software without specific prior written permission. 16*bcf00d8fSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*bcf00d8fSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*bcf00d8fSMatthias Ringwald * monetary gain. 19*bcf00d8fSMatthias Ringwald * 20*bcf00d8fSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*bcf00d8fSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*bcf00d8fSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*bcf00d8fSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*bcf00d8fSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*bcf00d8fSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*bcf00d8fSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*bcf00d8fSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*bcf00d8fSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*bcf00d8fSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*bcf00d8fSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*bcf00d8fSMatthias Ringwald * SUCH DAMAGE. 32*bcf00d8fSMatthias Ringwald * 33*bcf00d8fSMatthias Ringwald * Please inquire about commercial licensing options at 34*bcf00d8fSMatthias Ringwald * [email protected] 35*bcf00d8fSMatthias Ringwald * 36*bcf00d8fSMatthias Ringwald */ 37*bcf00d8fSMatthias Ringwald 38*bcf00d8fSMatthias Ringwald // ***************************************************************************** 39*bcf00d8fSMatthias Ringwald /* EXAMPLE_START(gatt_browser): GATT Client - Discovering primary services and their characteristics 40*bcf00d8fSMatthias Ringwald * 41*bcf00d8fSMatthias Ringwald * @text This example shows how to use the GATT Client 42*bcf00d8fSMatthias Ringwald * API to discover primary services and their characteristics of the first found 43*bcf00d8fSMatthias Ringwald * device that is advertising its services. 44*bcf00d8fSMatthias Ringwald * 45*bcf00d8fSMatthias Ringwald * The logic is divided between the HCI and GATT client packet handlers. 46*bcf00d8fSMatthias Ringwald * The HCI packet handler is responsible for finding a remote device, 47*bcf00d8fSMatthias Ringwald * connecting to it, and for starting the first GATT client query. 48*bcf00d8fSMatthias Ringwald * Then, the GATT client packet handler receives all primary services and 49*bcf00d8fSMatthias Ringwald * requests the characteristics of the last one to keep the example short. 50*bcf00d8fSMatthias Ringwald * 51*bcf00d8fSMatthias Ringwald */ 52*bcf00d8fSMatthias Ringwald // ***************************************************************************** 53*bcf00d8fSMatthias Ringwald 54*bcf00d8fSMatthias Ringwald #include <stdint.h> 55*bcf00d8fSMatthias Ringwald #include <stdio.h> 56*bcf00d8fSMatthias Ringwald #include <stdlib.h> 57*bcf00d8fSMatthias Ringwald #include <string.h> 58*bcf00d8fSMatthias Ringwald 59*bcf00d8fSMatthias Ringwald #include "btstack.h" 60*bcf00d8fSMatthias Ringwald 61*bcf00d8fSMatthias Ringwald typedef struct advertising_report { 62*bcf00d8fSMatthias Ringwald uint8_t type; 63*bcf00d8fSMatthias Ringwald uint8_t event_type; 64*bcf00d8fSMatthias Ringwald uint8_t address_type; 65*bcf00d8fSMatthias Ringwald bd_addr_t address; 66*bcf00d8fSMatthias Ringwald uint8_t rssi; 67*bcf00d8fSMatthias Ringwald uint8_t length; 68*bcf00d8fSMatthias Ringwald uint8_t * data; 69*bcf00d8fSMatthias Ringwald } advertising_report_t; 70*bcf00d8fSMatthias Ringwald 71*bcf00d8fSMatthias Ringwald static bd_addr_t cmdline_addr = { }; 72*bcf00d8fSMatthias Ringwald static int cmdline_addr_found = 0; 73*bcf00d8fSMatthias Ringwald 74*bcf00d8fSMatthias Ringwald static uint16_t gc_handle; 75*bcf00d8fSMatthias Ringwald static gatt_client_service_t services[40]; 76*bcf00d8fSMatthias Ringwald static int service_count = 0; 77*bcf00d8fSMatthias Ringwald static int service_index = 0; 78*bcf00d8fSMatthias Ringwald 79*bcf00d8fSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 80*bcf00d8fSMatthias Ringwald 81*bcf00d8fSMatthias Ringwald /* @section GATT client setup 82*bcf00d8fSMatthias Ringwald * 83*bcf00d8fSMatthias Ringwald * @text In the setup phase, a GATT client must register the HCI and GATT client 84*bcf00d8fSMatthias Ringwald * packet handlers, as shown in Listing GATTClientSetup. 85*bcf00d8fSMatthias Ringwald * Additionally, the security manager can be setup, if signed writes, or 86*bcf00d8fSMatthias Ringwald * encrypted, or authenticated connection are required, to access the 87*bcf00d8fSMatthias Ringwald * characteristics, as explained in Section on [SMP](../protocols/#sec:smpProtocols). 88*bcf00d8fSMatthias Ringwald */ 89*bcf00d8fSMatthias Ringwald 90*bcf00d8fSMatthias Ringwald /* LISTING_START(GATTClientSetup): Setting up GATT client */ 91*bcf00d8fSMatthias Ringwald 92*bcf00d8fSMatthias Ringwald // Handles connect, disconnect, and advertising report events, 93*bcf00d8fSMatthias Ringwald // starts the GATT client, and sends the first query. 94*bcf00d8fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 95*bcf00d8fSMatthias Ringwald 96*bcf00d8fSMatthias Ringwald // Handles GATT client query results, sends queries and the 97*bcf00d8fSMatthias Ringwald // GAP disconnect command when the querying is done. 98*bcf00d8fSMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 99*bcf00d8fSMatthias Ringwald 100*bcf00d8fSMatthias Ringwald static void gatt_client_setup(void){ 101*bcf00d8fSMatthias Ringwald 102*bcf00d8fSMatthias Ringwald // register for HCI events 103*bcf00d8fSMatthias Ringwald hci_event_callback_registration.callback = &handle_hci_event; 104*bcf00d8fSMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 105*bcf00d8fSMatthias Ringwald 106*bcf00d8fSMatthias Ringwald // Initialize L2CAP and register HCI event handler 107*bcf00d8fSMatthias Ringwald l2cap_init(); 108*bcf00d8fSMatthias Ringwald 109*bcf00d8fSMatthias Ringwald // Initialize GATT client 110*bcf00d8fSMatthias Ringwald gatt_client_init(); 111*bcf00d8fSMatthias Ringwald 112*bcf00d8fSMatthias Ringwald // Optinoally, Setup security manager 113*bcf00d8fSMatthias Ringwald sm_init(); 114*bcf00d8fSMatthias Ringwald sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); 115*bcf00d8fSMatthias Ringwald } 116*bcf00d8fSMatthias Ringwald /* LISTING_END */ 117*bcf00d8fSMatthias Ringwald 118*bcf00d8fSMatthias Ringwald static void printUUID(uint8_t * uuid128, uint16_t uuid16){ 119*bcf00d8fSMatthias Ringwald if (uuid16){ 120*bcf00d8fSMatthias Ringwald printf("%04x",uuid16); 121*bcf00d8fSMatthias Ringwald } else { 122*bcf00d8fSMatthias Ringwald printf("%s", uuid128_to_str(uuid128)); 123*bcf00d8fSMatthias Ringwald } 124*bcf00d8fSMatthias Ringwald } 125*bcf00d8fSMatthias Ringwald 126*bcf00d8fSMatthias Ringwald static void dump_advertising_report(advertising_report_t * e){ 127*bcf00d8fSMatthias Ringwald printf(" * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ", e->event_type, 128*bcf00d8fSMatthias Ringwald e->address_type, bd_addr_to_str(e->address), e->rssi, e->length); 129*bcf00d8fSMatthias Ringwald printf_hexdump(e->data, e->length); 130*bcf00d8fSMatthias Ringwald 131*bcf00d8fSMatthias Ringwald } 132*bcf00d8fSMatthias Ringwald 133*bcf00d8fSMatthias Ringwald static void dump_characteristic(gatt_client_characteristic_t * characteristic){ 134*bcf00d8fSMatthias Ringwald printf(" * characteristic: [0x%04x-0x%04x-0x%04x], properties 0x%02x, uuid ", 135*bcf00d8fSMatthias Ringwald characteristic->start_handle, characteristic->value_handle, characteristic->end_handle, characteristic->properties); 136*bcf00d8fSMatthias Ringwald printUUID(characteristic->uuid128, characteristic->uuid16); 137*bcf00d8fSMatthias Ringwald printf("\n"); 138*bcf00d8fSMatthias Ringwald } 139*bcf00d8fSMatthias Ringwald 140*bcf00d8fSMatthias Ringwald static void dump_service(gatt_client_service_t * service){ 141*bcf00d8fSMatthias Ringwald printf(" * service: [0x%04x-0x%04x], uuid ", service->start_group_handle, service->end_group_handle); 142*bcf00d8fSMatthias Ringwald printUUID(service->uuid128, service->uuid16); 143*bcf00d8fSMatthias Ringwald printf("\n"); 144*bcf00d8fSMatthias Ringwald } 145*bcf00d8fSMatthias Ringwald 146*bcf00d8fSMatthias Ringwald static void fill_advertising_report_from_packet(advertising_report_t * report, uint8_t *packet){ 147*bcf00d8fSMatthias Ringwald int pos = 2; 148*bcf00d8fSMatthias Ringwald report->event_type = packet[pos++]; 149*bcf00d8fSMatthias Ringwald report->address_type = packet[pos++]; 150*bcf00d8fSMatthias Ringwald reverse_bd_addr(&packet[pos], report->address); 151*bcf00d8fSMatthias Ringwald pos += 6; 152*bcf00d8fSMatthias Ringwald report->rssi = packet[pos++]; 153*bcf00d8fSMatthias Ringwald report->length = packet[pos++]; 154*bcf00d8fSMatthias Ringwald report->data = &packet[pos]; 155*bcf00d8fSMatthias Ringwald pos += report->length; 156*bcf00d8fSMatthias Ringwald dump_advertising_report(report); 157*bcf00d8fSMatthias Ringwald } 158*bcf00d8fSMatthias Ringwald 159*bcf00d8fSMatthias Ringwald 160*bcf00d8fSMatthias Ringwald /* @section HCI packet handler 161*bcf00d8fSMatthias Ringwald * 162*bcf00d8fSMatthias Ringwald * @text The HCI packet handler has to start the scanning, 163*bcf00d8fSMatthias Ringwald * to find the first advertising device, to stop scanning, to connect 164*bcf00d8fSMatthias Ringwald * to and later to disconnect from it, to start the GATT client upon 165*bcf00d8fSMatthias Ringwald * the connection is completed, and to send the first query - in this 166*bcf00d8fSMatthias Ringwald * case the gatt_client_discover_primary_services() is called, see 167*bcf00d8fSMatthias Ringwald * Listing GATTBrowserHCIPacketHandler. 168*bcf00d8fSMatthias Ringwald */ 169*bcf00d8fSMatthias Ringwald 170*bcf00d8fSMatthias Ringwald /* LISTING_START(GATTBrowserHCIPacketHandler): Connecting and disconnecting from the GATT client */ 171*bcf00d8fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 172*bcf00d8fSMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 173*bcf00d8fSMatthias Ringwald advertising_report_t report; 174*bcf00d8fSMatthias Ringwald 175*bcf00d8fSMatthias Ringwald uint8_t event = packet[0]; 176*bcf00d8fSMatthias Ringwald switch (event) { 177*bcf00d8fSMatthias Ringwald case BTSTACK_EVENT_STATE: 178*bcf00d8fSMatthias Ringwald // BTstack activated, get started 179*bcf00d8fSMatthias Ringwald if (packet[2] != HCI_STATE_WORKING) break; 180*bcf00d8fSMatthias Ringwald if (cmdline_addr_found){ 181*bcf00d8fSMatthias Ringwald printf("Trying to connect to %s\n", bd_addr_to_str(cmdline_addr)); 182*bcf00d8fSMatthias Ringwald gap_connect(cmdline_addr, 0); 183*bcf00d8fSMatthias Ringwald break; 184*bcf00d8fSMatthias Ringwald } 185*bcf00d8fSMatthias Ringwald printf("BTstack activated, start scanning!\n"); 186*bcf00d8fSMatthias Ringwald gap_set_scan_parameters(0,0x0030, 0x0030); 187*bcf00d8fSMatthias Ringwald gap_start_scan(); 188*bcf00d8fSMatthias Ringwald break; 189*bcf00d8fSMatthias Ringwald case GAP_LE_EVENT_ADVERTISING_REPORT: 190*bcf00d8fSMatthias Ringwald fill_advertising_report_from_packet(&report, packet); 191*bcf00d8fSMatthias Ringwald // stop scanning, and connect to the device 192*bcf00d8fSMatthias Ringwald gap_stop_scan(); 193*bcf00d8fSMatthias Ringwald gap_connect(report.address,report.address_type); 194*bcf00d8fSMatthias Ringwald break; 195*bcf00d8fSMatthias Ringwald case HCI_EVENT_LE_META: 196*bcf00d8fSMatthias Ringwald // wait for connection complete 197*bcf00d8fSMatthias Ringwald if (packet[2] != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; 198*bcf00d8fSMatthias Ringwald gc_handle = little_endian_read_16(packet, 4); 199*bcf00d8fSMatthias Ringwald // query primary services 200*bcf00d8fSMatthias Ringwald gatt_client_discover_primary_services(handle_gatt_client_event, gc_handle); 201*bcf00d8fSMatthias Ringwald break; 202*bcf00d8fSMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 203*bcf00d8fSMatthias Ringwald printf("\nGATT browser - DISCONNECTED\n"); 204*bcf00d8fSMatthias Ringwald exit(0); 205*bcf00d8fSMatthias Ringwald break; 206*bcf00d8fSMatthias Ringwald default: 207*bcf00d8fSMatthias Ringwald break; 208*bcf00d8fSMatthias Ringwald } 209*bcf00d8fSMatthias Ringwald } 210*bcf00d8fSMatthias Ringwald /* LISTING_END */ 211*bcf00d8fSMatthias Ringwald 212*bcf00d8fSMatthias Ringwald /* @section GATT Client event handler 213*bcf00d8fSMatthias Ringwald * 214*bcf00d8fSMatthias Ringwald * @text Query results and further queries are handled by the GATT client packet 215*bcf00d8fSMatthias Ringwald * handler, as shown in Listing GATTBrowserQueryHandler. Here, upon 216*bcf00d8fSMatthias Ringwald * receiving the primary services, the 217*bcf00d8fSMatthias Ringwald * gatt_client_discover_characteristics_for_service() query for the last 218*bcf00d8fSMatthias Ringwald * received service is sent. After receiving the characteristics for the service, 219*bcf00d8fSMatthias Ringwald * gap_disconnect is called to terminate the connection. Upon 220*bcf00d8fSMatthias Ringwald * disconnect, the HCI packet handler receives the disconnect complete event. 221*bcf00d8fSMatthias Ringwald */ 222*bcf00d8fSMatthias Ringwald 223*bcf00d8fSMatthias Ringwald /* LISTING_START(GATTBrowserQueryHandler): Handling of the GATT client queries */ 224*bcf00d8fSMatthias Ringwald static int search_services = 1; 225*bcf00d8fSMatthias Ringwald 226*bcf00d8fSMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 227*bcf00d8fSMatthias Ringwald gatt_client_service_t service; 228*bcf00d8fSMatthias Ringwald gatt_client_characteristic_t characteristic; 229*bcf00d8fSMatthias Ringwald switch(packet[0]){ 230*bcf00d8fSMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT:\ 231*bcf00d8fSMatthias Ringwald gatt_event_service_query_result_get_service(packet, &service); 232*bcf00d8fSMatthias Ringwald dump_service(&service); 233*bcf00d8fSMatthias Ringwald services[service_count++] = service; 234*bcf00d8fSMatthias Ringwald break; 235*bcf00d8fSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 236*bcf00d8fSMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 237*bcf00d8fSMatthias Ringwald dump_characteristic(&characteristic); 238*bcf00d8fSMatthias Ringwald break; 239*bcf00d8fSMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 240*bcf00d8fSMatthias Ringwald if (search_services){ 241*bcf00d8fSMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE of search services 242*bcf00d8fSMatthias Ringwald service_index = 0; 243*bcf00d8fSMatthias Ringwald printf("\nGATT browser - CHARACTERISTIC for SERVICE %s\n", uuid128_to_str(service.uuid128)); 244*bcf00d8fSMatthias Ringwald search_services = 0; 245*bcf00d8fSMatthias Ringwald gatt_client_discover_characteristics_for_service(handle_gatt_client_event, gc_handle, &services[service_index]); 246*bcf00d8fSMatthias Ringwald } else { 247*bcf00d8fSMatthias Ringwald // GATT_EVENT_QUERY_COMPLETE of search characteristics 248*bcf00d8fSMatthias Ringwald if (service_index < service_count) { 249*bcf00d8fSMatthias Ringwald service = services[service_index++]; 250*bcf00d8fSMatthias Ringwald printf("\nGATT browser - CHARACTERISTIC for SERVICE %s, [0x%04x-0x%04x]\n", 251*bcf00d8fSMatthias Ringwald uuid128_to_str(service.uuid128), service.start_group_handle, service.end_group_handle); 252*bcf00d8fSMatthias Ringwald gatt_client_discover_characteristics_for_service(handle_gatt_client_event, gc_handle, &service); 253*bcf00d8fSMatthias Ringwald break; 254*bcf00d8fSMatthias Ringwald } 255*bcf00d8fSMatthias Ringwald service_index = 0; 256*bcf00d8fSMatthias Ringwald gap_disconnect(gc_handle); 257*bcf00d8fSMatthias Ringwald } 258*bcf00d8fSMatthias Ringwald break; 259*bcf00d8fSMatthias Ringwald default: 260*bcf00d8fSMatthias Ringwald break; 261*bcf00d8fSMatthias Ringwald } 262*bcf00d8fSMatthias Ringwald } 263*bcf00d8fSMatthias Ringwald /* LISTING_END */ 264*bcf00d8fSMatthias Ringwald 265*bcf00d8fSMatthias Ringwald static void usage(const char *name){ 266*bcf00d8fSMatthias Ringwald fprintf(stderr, "\nUsage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", name); 267*bcf00d8fSMatthias Ringwald fprintf(stderr, "If no argument is provided, GATT browser will start scanning and connect to the first found device.\nTo connect to a specific device use argument [-a].\n\n"); 268*bcf00d8fSMatthias Ringwald } 269*bcf00d8fSMatthias Ringwald 270*bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]); 271*bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]){ 272*bcf00d8fSMatthias Ringwald 273*bcf00d8fSMatthias Ringwald int arg = 1; 274*bcf00d8fSMatthias Ringwald cmdline_addr_found = 0; 275*bcf00d8fSMatthias Ringwald 276*bcf00d8fSMatthias Ringwald while (arg < argc) { 277*bcf00d8fSMatthias Ringwald if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){ 278*bcf00d8fSMatthias Ringwald arg++; 279*bcf00d8fSMatthias Ringwald cmdline_addr_found = sscanf_bd_addr((uint8_t *)argv[arg], cmdline_addr); 280*bcf00d8fSMatthias Ringwald arg++; 281*bcf00d8fSMatthias Ringwald continue; 282*bcf00d8fSMatthias Ringwald } 283*bcf00d8fSMatthias Ringwald usage(argv[0]); 284*bcf00d8fSMatthias Ringwald return 0; 285*bcf00d8fSMatthias Ringwald } 286*bcf00d8fSMatthias Ringwald 287*bcf00d8fSMatthias Ringwald gatt_client_setup(); 288*bcf00d8fSMatthias Ringwald 289*bcf00d8fSMatthias Ringwald // turn on! 290*bcf00d8fSMatthias Ringwald hci_power_control(HCI_POWER_ON); 291*bcf00d8fSMatthias Ringwald 292*bcf00d8fSMatthias Ringwald return 0; 293*bcf00d8fSMatthias Ringwald } 294*bcf00d8fSMatthias Ringwald 295*bcf00d8fSMatthias Ringwald /* EXAMPLE_END */ 296*bcf00d8fSMatthias Ringwald 297*bcf00d8fSMatthias Ringwald 298