16bdecec7SMatthias Ringwald /* 26bdecec7SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 36bdecec7SMatthias Ringwald * 46bdecec7SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 56bdecec7SMatthias Ringwald * modification, are permitted provided that the following conditions 66bdecec7SMatthias Ringwald * are met: 76bdecec7SMatthias Ringwald * 86bdecec7SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 96bdecec7SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 106bdecec7SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 116bdecec7SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 126bdecec7SMatthias Ringwald * documentation and/or other materials provided with the distribution. 136bdecec7SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 146bdecec7SMatthias Ringwald * contributors may be used to endorse or promote products derived 156bdecec7SMatthias Ringwald * from this software without specific prior written permission. 166bdecec7SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 176bdecec7SMatthias Ringwald * personal benefit and not for any commercial purpose or for 186bdecec7SMatthias Ringwald * monetary gain. 196bdecec7SMatthias Ringwald * 206bdecec7SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 216bdecec7SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 226bdecec7SMatthias 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, 256bdecec7SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 266bdecec7SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 276bdecec7SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 286bdecec7SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 296bdecec7SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 306bdecec7SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 316bdecec7SMatthias Ringwald * SUCH DAMAGE. 326bdecec7SMatthias Ringwald * 336bdecec7SMatthias Ringwald * Please inquire about commercial licensing options at 346bdecec7SMatthias Ringwald * [email protected] 356bdecec7SMatthias Ringwald * 366bdecec7SMatthias Ringwald */ 376bdecec7SMatthias Ringwald 386bdecec7SMatthias Ringwald #define BTSTACK_FILE__ "ancs_client.c" 396bdecec7SMatthias Ringwald 406bdecec7SMatthias Ringwald #include "btstack_config.h" 416bdecec7SMatthias Ringwald 426bdecec7SMatthias Ringwald #include <stdint.h> 436bdecec7SMatthias Ringwald #include <string.h> 446bdecec7SMatthias Ringwald 456bdecec7SMatthias Ringwald #include "ble/gatt-service/ancs_client.h" 466bdecec7SMatthias Ringwald 476bdecec7SMatthias Ringwald #include "ble/core.h" 486bdecec7SMatthias Ringwald #include "ble/gatt_client.h" 496bdecec7SMatthias Ringwald #include "ble/sm.h" 506bdecec7SMatthias Ringwald #include "btstack_debug.h" 516bdecec7SMatthias Ringwald #include "btstack_event.h" 526bdecec7SMatthias Ringwald #include "gap.h" 536bdecec7SMatthias Ringwald 546bdecec7SMatthias Ringwald // ancs_client.h Start 556bdecec7SMatthias Ringwald typedef enum ancs_chunk_parser_state { 566bdecec7SMatthias Ringwald W4_ATTRIBUTE_ID, 576bdecec7SMatthias Ringwald W4_ATTRIBUTE_LEN, 586bdecec7SMatthias Ringwald W4_ATTRIBUTE_COMPLETE, 596bdecec7SMatthias Ringwald } ancs_chunk_parser_state_t; 606bdecec7SMatthias Ringwald 616bdecec7SMatthias Ringwald typedef enum { 626bdecec7SMatthias Ringwald TC_IDLE, 636bdecec7SMatthias Ringwald TC_W4_ENCRYPTED_CONNECTION, 6431d175ffSMatthias Ringwald TC_W2_QUERY_SERVICE, 656bdecec7SMatthias Ringwald TC_W4_SERVICE_RESULT, 6631d175ffSMatthias Ringwald TC_W2_QUERY_CARACTERISTIC, 676bdecec7SMatthias Ringwald TC_W4_CHARACTERISTIC_RESULT, 6831d175ffSMatthias Ringwald TC_W2_SUBSCRIBE_DATA_SOURCE, 696bdecec7SMatthias Ringwald TC_W4_DATA_SOURCE_SUBSCRIBED, 7031d175ffSMatthias Ringwald TC_W2_ENABLE_NOTIFICATION, 716bdecec7SMatthias Ringwald TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED, 726bdecec7SMatthias Ringwald TC_SUBSCRIBED, 736bdecec7SMatthias Ringwald TC_W4_DISCONNECT 746bdecec7SMatthias Ringwald } tc_state_t; 756bdecec7SMatthias Ringwald 766bdecec7SMatthias Ringwald static uint32_t ancs_notification_uid; 7731d175ffSMatthias Ringwald static hci_con_handle_t gc_handle; 786bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_notification_source_notification; 796bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_data_source_notification; 806bdecec7SMatthias Ringwald static int ancs_service_found; 816bdecec7SMatthias Ringwald static gatt_client_service_t ancs_service; 826bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_notification_source_characteristic; 836bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_control_point_characteristic; 846bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_data_source_characteristic; 856bdecec7SMatthias Ringwald static int ancs_characteristcs; 866bdecec7SMatthias Ringwald static tc_state_t tc_state = TC_IDLE; 876bdecec7SMatthias Ringwald 886bdecec7SMatthias Ringwald static ancs_chunk_parser_state_t chunk_parser_state; 896bdecec7SMatthias Ringwald static uint8_t ancs_notification_buffer[50]; 906bdecec7SMatthias Ringwald static uint16_t ancs_bytes_received; 916bdecec7SMatthias Ringwald static uint16_t ancs_bytes_needed; 926bdecec7SMatthias Ringwald static uint8_t ancs_attribute_id; 936bdecec7SMatthias Ringwald static uint16_t ancs_attribute_len; 946bdecec7SMatthias Ringwald 956bdecec7SMatthias Ringwald static btstack_packet_handler_t client_handler; 966bdecec7SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 9731d175ffSMatthias Ringwald static btstack_context_callback_registration_t ancs_client_handle_can_send_now; 986bdecec7SMatthias Ringwald 99e115a09fSMatthias Ringwald static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 100e115a09fSMatthias Ringwald 1016bdecec7SMatthias Ringwald void ancs_client_register_callback(btstack_packet_handler_t handler){ 1026bdecec7SMatthias Ringwald client_handler = handler; 1036bdecec7SMatthias Ringwald } 1046bdecec7SMatthias Ringwald 10531d175ffSMatthias Ringwald static uint8_t ancs_client_request_send_gatt_query(void){ 10631d175ffSMatthias Ringwald uint8_t status = gatt_client_request_to_send_gatt_query(&ancs_client_handle_can_send_now, gc_handle); 10731d175ffSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 10831d175ffSMatthias Ringwald tc_state = TC_IDLE; 10931d175ffSMatthias Ringwald gc_handle = HCI_CON_HANDLE_INVALID; 11031d175ffSMatthias Ringwald } 11131d175ffSMatthias Ringwald return status; 11231d175ffSMatthias Ringwald } 11331d175ffSMatthias Ringwald 1146bdecec7SMatthias Ringwald static void notify_client_text(int event_type){ 1156bdecec7SMatthias Ringwald if (!client_handler) return; 1166bdecec7SMatthias Ringwald uint8_t event[7 + sizeof(ancs_notification_buffer) + 1]; 1176bdecec7SMatthias Ringwald event[0] = HCI_EVENT_ANCS_META; 1186bdecec7SMatthias Ringwald event[1] = 5u + ancs_attribute_len; 1196bdecec7SMatthias Ringwald event[2] = event_type; 1206bdecec7SMatthias Ringwald little_endian_store_16(event, 3, gc_handle); 1216bdecec7SMatthias Ringwald little_endian_store_16(event, 5, ancs_attribute_id); 1226bdecec7SMatthias Ringwald (void)memcpy(&event[7], ancs_notification_buffer, ancs_attribute_len); 1236bdecec7SMatthias Ringwald // we're nice 1246bdecec7SMatthias Ringwald event[7u+ancs_attribute_len] = 0u; 1256bdecec7SMatthias Ringwald (*client_handler)(HCI_EVENT_PACKET, 0u, event, event[1u] + 2u); 1266bdecec7SMatthias Ringwald } 1276bdecec7SMatthias Ringwald 1286bdecec7SMatthias Ringwald static void notify_client_simple(int event_type){ 1296bdecec7SMatthias Ringwald if (!client_handler) return; 1306bdecec7SMatthias Ringwald uint8_t event[5]; 1316bdecec7SMatthias Ringwald event[0] = HCI_EVENT_ANCS_META; 1326bdecec7SMatthias Ringwald event[1] = 3; 1336bdecec7SMatthias Ringwald event[2] = event_type; 1346bdecec7SMatthias Ringwald little_endian_store_16(event, 3, gc_handle); 1356bdecec7SMatthias Ringwald (*client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 1366bdecec7SMatthias Ringwald } 1376bdecec7SMatthias Ringwald 1386bdecec7SMatthias Ringwald static void ancs_chunk_parser_init(void){ 13941866fd3SMatthias Ringwald // skip comand id and notification uid 1406bdecec7SMatthias Ringwald chunk_parser_state = W4_ATTRIBUTE_ID; 1416bdecec7SMatthias Ringwald ancs_bytes_received = 0; 1426bdecec7SMatthias Ringwald ancs_bytes_needed = 6; 1436bdecec7SMatthias Ringwald } 1446bdecec7SMatthias Ringwald 1456bdecec7SMatthias Ringwald const char * ancs_client_attribute_name_for_id(int id){ 1466bdecec7SMatthias Ringwald static const char * ancs_attribute_names[] = { 1476bdecec7SMatthias Ringwald "AppIdentifier", 1486bdecec7SMatthias Ringwald "IDTitle", 1496bdecec7SMatthias Ringwald "IDSubtitle", 1506bdecec7SMatthias Ringwald "IDMessage", 1516bdecec7SMatthias Ringwald "IDMessageSize", 1526bdecec7SMatthias Ringwald "IDDate" 1536bdecec7SMatthias Ringwald }; 1546bdecec7SMatthias Ringwald 15541866fd3SMatthias Ringwald static const uint16_t ANCS_ATTRIBUTE_NAMES_COUNT = sizeof(ancs_attribute_names) / sizeof(char *); 1566bdecec7SMatthias Ringwald 15741866fd3SMatthias Ringwald if (id >= ANCS_ATTRIBUTE_NAMES_COUNT) return NULL; 1586bdecec7SMatthias Ringwald return ancs_attribute_names[id]; 1596bdecec7SMatthias Ringwald } 1606bdecec7SMatthias Ringwald 1616bdecec7SMatthias Ringwald static void ancs_chunk_parser_handle_byte(uint8_t data){ 1626bdecec7SMatthias Ringwald ancs_notification_buffer[ancs_bytes_received++] = data; 1636bdecec7SMatthias Ringwald if (ancs_bytes_received < ancs_bytes_needed) return; 1646bdecec7SMatthias Ringwald switch (chunk_parser_state){ 1656bdecec7SMatthias Ringwald case W4_ATTRIBUTE_ID: 1666bdecec7SMatthias Ringwald ancs_attribute_id = ancs_notification_buffer[ancs_bytes_received-1u]; 1676bdecec7SMatthias Ringwald ancs_bytes_received = 0; 1686bdecec7SMatthias Ringwald ancs_bytes_needed = 2; 1696bdecec7SMatthias Ringwald chunk_parser_state = W4_ATTRIBUTE_LEN; 1706bdecec7SMatthias Ringwald break; 1716bdecec7SMatthias Ringwald case W4_ATTRIBUTE_LEN: 1726bdecec7SMatthias Ringwald ancs_attribute_len = little_endian_read_16(ancs_notification_buffer, ancs_bytes_received-2u); 1736bdecec7SMatthias Ringwald ancs_bytes_received = 0; 1746bdecec7SMatthias Ringwald ancs_bytes_needed = ancs_attribute_len; 1756bdecec7SMatthias Ringwald if (ancs_attribute_len == 0u) { 1766bdecec7SMatthias Ringwald ancs_bytes_needed = 1; 1776bdecec7SMatthias Ringwald chunk_parser_state = W4_ATTRIBUTE_ID; 1786bdecec7SMatthias Ringwald break; 1796bdecec7SMatthias Ringwald } 1806bdecec7SMatthias Ringwald chunk_parser_state = W4_ATTRIBUTE_COMPLETE; 1816bdecec7SMatthias Ringwald break; 1826bdecec7SMatthias Ringwald case W4_ATTRIBUTE_COMPLETE: 1836bdecec7SMatthias Ringwald ancs_notification_buffer[ancs_bytes_received] = 0; 1846bdecec7SMatthias Ringwald notify_client_text(ANCS_SUBEVENT_CLIENT_NOTIFICATION); 1856bdecec7SMatthias Ringwald ancs_bytes_received = 0; 1866bdecec7SMatthias Ringwald ancs_bytes_needed = 1; 1876bdecec7SMatthias Ringwald chunk_parser_state = W4_ATTRIBUTE_ID; 1886bdecec7SMatthias Ringwald break; 1896bdecec7SMatthias Ringwald default: 19041866fd3SMatthias Ringwald btstack_unreachable(); 1916bdecec7SMatthias Ringwald break; 1926bdecec7SMatthias Ringwald } 1936bdecec7SMatthias Ringwald } 1946bdecec7SMatthias Ringwald 195e115a09fSMatthias Ringwald static void ancs_client_handle_notification(uint8_t * packet, uint16_t size){ 1964ddcd9eaSMatthias Ringwald UNUSED(size); 1974ddcd9eaSMatthias Ringwald 198e115a09fSMatthias Ringwald uint16_t value_handle = little_endian_read_16(packet, 4); 199e115a09fSMatthias Ringwald uint16_t value_length = little_endian_read_16(packet, 6); 200e115a09fSMatthias Ringwald uint8_t * value = &packet[8]; 2016bdecec7SMatthias Ringwald 202e115a09fSMatthias Ringwald log_info("ANCS Notification, value handle %u", value_handle); 2036bdecec7SMatthias Ringwald 204e115a09fSMatthias Ringwald if (value_handle == ancs_data_source_characteristic.value_handle){ 205e115a09fSMatthias Ringwald int i; 206e115a09fSMatthias Ringwald for (i=0;i<value_length;i++) { 207e115a09fSMatthias Ringwald ancs_chunk_parser_handle_byte(value[i]); 208e115a09fSMatthias Ringwald } 209e115a09fSMatthias Ringwald } else if (value_handle == ancs_notification_source_characteristic.value_handle){ 210e115a09fSMatthias Ringwald ancs_notification_uid = little_endian_read_32(value, 4); 211e115a09fSMatthias Ringwald log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x", 212e115a09fSMatthias Ringwald value[0], value[1], value[2], value[3], (int) ancs_notification_uid); 213e115a09fSMatthias Ringwald static uint8_t get_notification_attributes[] = {0, 0,0,0,0, 0, 1,32,0, 2,32,0, 3,32,0, 4, 5}; 214e115a09fSMatthias Ringwald little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid); 215e115a09fSMatthias Ringwald ancs_notification_uid = 0; 216e115a09fSMatthias Ringwald ancs_chunk_parser_init(); 217e115a09fSMatthias Ringwald gatt_client_write_value_of_characteristic(ancs_client_handle_gatt_client_event, gc_handle, ancs_control_point_characteristic.value_handle, 218e115a09fSMatthias Ringwald sizeof(get_notification_attributes), get_notification_attributes); 219e115a09fSMatthias Ringwald } else { 220e115a09fSMatthias Ringwald log_info("Unknown Source: "); 221e115a09fSMatthias Ringwald log_info_hexdump(value , value_length); 222e115a09fSMatthias Ringwald } 223e115a09fSMatthias Ringwald } 224e115a09fSMatthias Ringwald 22531d175ffSMatthias Ringwald static void ancs_client_send_next_query(void * context){ 22631d175ffSMatthias Ringwald UNUSED(context); 22731d175ffSMatthias Ringwald static const uint8_t ancs_service_uuid[] = {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0}; 22831d175ffSMatthias Ringwald 22931d175ffSMatthias Ringwald switch(tc_state){ 23031d175ffSMatthias Ringwald case TC_W2_QUERY_SERVICE: 23131d175ffSMatthias Ringwald tc_state = TC_W4_SERVICE_RESULT; 23231d175ffSMatthias Ringwald (void) gatt_client_discover_primary_services_by_uuid128(ancs_client_handle_gatt_client_event, gc_handle, ancs_service_uuid); 23331d175ffSMatthias Ringwald break; 23431d175ffSMatthias Ringwald 23531d175ffSMatthias Ringwald case TC_W2_QUERY_CARACTERISTIC: 23631d175ffSMatthias Ringwald tc_state = TC_W4_CHARACTERISTIC_RESULT; 23731d175ffSMatthias Ringwald log_info("ANCS Client - Discover characteristics for ANCS SERVICE "); 23831d175ffSMatthias Ringwald gatt_client_discover_characteristics_for_service(ancs_client_handle_gatt_client_event, gc_handle, &ancs_service); 23931d175ffSMatthias Ringwald break; 24031d175ffSMatthias Ringwald 24131d175ffSMatthias Ringwald case TC_W2_ENABLE_NOTIFICATION: 24231d175ffSMatthias Ringwald tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED; 24331d175ffSMatthias Ringwald gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic); 24431d175ffSMatthias Ringwald gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic, 24531d175ffSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 24631d175ffSMatthias Ringwald break; 24731d175ffSMatthias Ringwald 24831d175ffSMatthias Ringwald case TC_W2_SUBSCRIBE_DATA_SOURCE: 24931d175ffSMatthias Ringwald tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED; 25031d175ffSMatthias Ringwald gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic); 25131d175ffSMatthias Ringwald gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic, 25231d175ffSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 25331d175ffSMatthias Ringwald break; 25431d175ffSMatthias Ringwald 25531d175ffSMatthias Ringwald default: 25631d175ffSMatthias Ringwald break; 25731d175ffSMatthias Ringwald } 25831d175ffSMatthias Ringwald } 25931d175ffSMatthias Ringwald 260*0d0cbb52SMatthias Ringwald static void ancs_client_handle_gatt_client_event_in_w4_service_result(uint8_t* packet) { 2616bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){ 2626bdecec7SMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT: 2636bdecec7SMatthias Ringwald gatt_event_service_query_result_get_service(packet, &ancs_service); 2646bdecec7SMatthias Ringwald ancs_service_found = 1; 2656bdecec7SMatthias Ringwald break; 2666bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 2676bdecec7SMatthias Ringwald if (!ancs_service_found){ 2686bdecec7SMatthias Ringwald log_info("ANCS Service not found"); 2696bdecec7SMatthias Ringwald tc_state = TC_IDLE; 2706bdecec7SMatthias Ringwald break; 2716bdecec7SMatthias Ringwald } 27231d175ffSMatthias Ringwald tc_state = TC_W2_QUERY_CARACTERISTIC; 2736bdecec7SMatthias Ringwald break; 2746bdecec7SMatthias Ringwald default: 2756bdecec7SMatthias Ringwald break; 2766bdecec7SMatthias Ringwald } 277*0d0cbb52SMatthias Ringwald } 278*0d0cbb52SMatthias Ringwald 279*0d0cbb52SMatthias Ringwald static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 280*0d0cbb52SMatthias Ringwald 281*0d0cbb52SMatthias Ringwald UNUSED(packet_type); 282*0d0cbb52SMatthias Ringwald UNUSED(channel); 283*0d0cbb52SMatthias Ringwald 284*0d0cbb52SMatthias Ringwald static const uint8_t ancs_notification_source_uuid[] = {0x9F,0xBF,0x12,0x0D,0x63,0x01,0x42,0xD9,0x8C,0x58,0x25,0xE6,0x99,0xA2,0x1D,0xBD}; 285*0d0cbb52SMatthias Ringwald static const uint8_t ancs_control_point_uuid[] = {0x69,0xD1,0xD8,0xF3,0x45,0xE1,0x49,0xA8,0x98,0x21,0x9B,0xBD,0xFD,0xAA,0xD9,0xD9}; 286*0d0cbb52SMatthias Ringwald static const uint8_t ancs_data_source_uuid[] = {0x22,0xEA,0xC6,0xE9,0x24,0xD6,0x4B,0xB5,0xBE,0x44,0xB3,0x6A,0xCE,0x7C,0x7B,0xFB}; 287*0d0cbb52SMatthias Ringwald 288*0d0cbb52SMatthias Ringwald gatt_client_characteristic_t characteristic; 289*0d0cbb52SMatthias Ringwald 290*0d0cbb52SMatthias Ringwald switch(tc_state){ 291*0d0cbb52SMatthias Ringwald case TC_W4_SERVICE_RESULT: 292*0d0cbb52SMatthias Ringwald ancs_client_handle_gatt_client_event_in_w4_service_result(packet); 2936bdecec7SMatthias Ringwald break; 2946bdecec7SMatthias Ringwald 2956bdecec7SMatthias Ringwald case TC_W4_CHARACTERISTIC_RESULT: 2966bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){ 2976bdecec7SMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 2986bdecec7SMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 2996bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){ 3006bdecec7SMatthias Ringwald log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle); 3016bdecec7SMatthias Ringwald ancs_notification_source_characteristic = characteristic; 3026bdecec7SMatthias Ringwald ancs_characteristcs++; 3036bdecec7SMatthias Ringwald break; 3046bdecec7SMatthias Ringwald } 3056bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){ 3066bdecec7SMatthias Ringwald log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle); 3076bdecec7SMatthias Ringwald ancs_control_point_characteristic = characteristic; 3086bdecec7SMatthias Ringwald ancs_characteristcs++; 3096bdecec7SMatthias Ringwald break; 3106bdecec7SMatthias Ringwald } 3116bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){ 3126bdecec7SMatthias Ringwald log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle); 3136bdecec7SMatthias Ringwald ancs_data_source_characteristic = characteristic; 3146bdecec7SMatthias Ringwald ancs_characteristcs++; 3156bdecec7SMatthias Ringwald break; 3166bdecec7SMatthias Ringwald } 3176bdecec7SMatthias Ringwald break; 3186bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 3196bdecec7SMatthias Ringwald log_info("ANCS Characteristcs count %u", ancs_characteristcs); 32031d175ffSMatthias Ringwald tc_state = TC_W2_ENABLE_NOTIFICATION; 3216bdecec7SMatthias Ringwald break; 3226bdecec7SMatthias Ringwald default: 3236bdecec7SMatthias Ringwald break; 3246bdecec7SMatthias Ringwald } 3256bdecec7SMatthias Ringwald break; 32631d175ffSMatthias Ringwald 3276bdecec7SMatthias Ringwald case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED: 3286bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){ 3296bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 3306bdecec7SMatthias Ringwald log_info("ANCS Notification Source subscribed"); 33131d175ffSMatthias Ringwald tc_state = TC_W2_SUBSCRIBE_DATA_SOURCE; 33231d175ffSMatthias Ringwald 3336bdecec7SMatthias Ringwald break; 3346bdecec7SMatthias Ringwald default: 3356bdecec7SMatthias Ringwald break; 3366bdecec7SMatthias Ringwald } 3376bdecec7SMatthias Ringwald break; 33831d175ffSMatthias Ringwald 3396bdecec7SMatthias Ringwald case TC_W4_DATA_SOURCE_SUBSCRIBED: 3406bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){ 3416bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 3426bdecec7SMatthias Ringwald log_info("ANCS Data Source subscribed"); 3436bdecec7SMatthias Ringwald tc_state = TC_SUBSCRIBED; 3446bdecec7SMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED); 3456bdecec7SMatthias Ringwald break; 3466bdecec7SMatthias Ringwald default: 3476bdecec7SMatthias Ringwald break; 3486bdecec7SMatthias Ringwald } 3496bdecec7SMatthias Ringwald break; 35031d175ffSMatthias Ringwald 3516bdecec7SMatthias Ringwald case TC_SUBSCRIBED: 35241866fd3SMatthias Ringwald switch(hci_event_packet_get_type(packet)){ 35341866fd3SMatthias Ringwald case GATT_EVENT_NOTIFICATION: 35441866fd3SMatthias Ringwald case GATT_EVENT_INDICATION: 355e115a09fSMatthias Ringwald ancs_client_handle_notification(packet, size); 3566bdecec7SMatthias Ringwald break; 3576bdecec7SMatthias Ringwald default: 3586bdecec7SMatthias Ringwald break; 3596bdecec7SMatthias Ringwald } 36041866fd3SMatthias Ringwald break; 36131d175ffSMatthias Ringwald 36231d175ffSMatthias Ringwald default: 36331d175ffSMatthias Ringwald break; 36431d175ffSMatthias Ringwald } 36531d175ffSMatthias Ringwald 36631d175ffSMatthias Ringwald uint8_t status; 36731d175ffSMatthias Ringwald switch(tc_state){ 36831d175ffSMatthias Ringwald case TC_W2_QUERY_SERVICE: 36931d175ffSMatthias Ringwald case TC_W2_QUERY_CARACTERISTIC: 37031d175ffSMatthias Ringwald case TC_W2_ENABLE_NOTIFICATION: 37131d175ffSMatthias Ringwald case TC_W2_SUBSCRIBE_DATA_SOURCE: 37231d175ffSMatthias Ringwald status = gatt_client_request_to_send_gatt_query(&ancs_client_handle_can_send_now, gc_handle); 37331d175ffSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 37431d175ffSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 37531d175ffSMatthias Ringwald } 37631d175ffSMatthias Ringwald break; 37731d175ffSMatthias Ringwald 37841866fd3SMatthias Ringwald default: 37941866fd3SMatthias Ringwald break; 38041866fd3SMatthias Ringwald } 381e115a09fSMatthias Ringwald } 382e115a09fSMatthias Ringwald 383e115a09fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 384e115a09fSMatthias Ringwald 385e115a09fSMatthias Ringwald UNUSED(packet_type); // ok: only hci events 386e115a09fSMatthias Ringwald UNUSED(channel); // ok: there is no channel 387e115a09fSMatthias Ringwald UNUSED(size); // ok: fixed format events read from HCI buffer 388e115a09fSMatthias Ringwald 389e115a09fSMatthias Ringwald int connection_encrypted; 39031d175ffSMatthias Ringwald uint8_t status; 391e115a09fSMatthias Ringwald 392e115a09fSMatthias Ringwald // handle connect / disconncet events first 393e115a09fSMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 3949d1eff91SMatthias Ringwald case HCI_EVENT_META_GAP: 3959d1eff91SMatthias Ringwald switch (hci_event_gap_meta_get_subevent_code(packet)) { 3969d1eff91SMatthias Ringwald case GAP_SUBEVENT_LE_CONNECTION_COMPLETE: 3979d1eff91SMatthias Ringwald gc_handle = gap_subevent_le_connection_complete_get_connection_handle(packet); 398e115a09fSMatthias Ringwald log_info("Connection handle 0x%04x, request encryption", gc_handle); 399e115a09fSMatthias Ringwald 400e115a09fSMatthias Ringwald // we need to be paired to enable notifications 401e115a09fSMatthias Ringwald tc_state = TC_W4_ENCRYPTED_CONNECTION; 402e115a09fSMatthias Ringwald ancs_service_found = false; 403e115a09fSMatthias Ringwald sm_request_pairing(gc_handle); 404e115a09fSMatthias Ringwald break; 405e115a09fSMatthias Ringwald default: 406e115a09fSMatthias Ringwald break; 407e115a09fSMatthias Ringwald } 408e115a09fSMatthias Ringwald return; 409e115a09fSMatthias Ringwald 410e115a09fSMatthias Ringwald case HCI_EVENT_ENCRYPTION_CHANGE: 4118601e696SMatthias Ringwald case HCI_EVENT_ENCRYPTION_CHANGE_V2: 4128601e696SMatthias Ringwald if (gc_handle != hci_event_encryption_change_get_connection_handle(packet)) return; 4138601e696SMatthias Ringwald connection_encrypted = hci_event_encryption_change_get_encryption_enabled(packet); 414e115a09fSMatthias Ringwald log_info("Encryption state change: %u", connection_encrypted); 415e115a09fSMatthias Ringwald if (!connection_encrypted) return; 416e115a09fSMatthias Ringwald if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return; 417e115a09fSMatthias Ringwald 418e115a09fSMatthias Ringwald // let's start 419e115a09fSMatthias Ringwald log_info("\nANCS Client - CONNECTED, discover ANCS service"); 42031d175ffSMatthias Ringwald tc_state = TC_W2_QUERY_SERVICE; 42131d175ffSMatthias Ringwald status = ancs_client_request_send_gatt_query(); 42231d175ffSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){ 42331d175ffSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 42431d175ffSMatthias Ringwald } 425e115a09fSMatthias Ringwald return; 426e115a09fSMatthias Ringwald 427e115a09fSMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 428e115a09fSMatthias Ringwald if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break; 429e115a09fSMatthias Ringwald if (tc_state == TC_SUBSCRIBED){ 430e115a09fSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED); 431e115a09fSMatthias Ringwald } 432e115a09fSMatthias Ringwald tc_state = TC_IDLE; 43331d175ffSMatthias Ringwald gc_handle = HCI_CON_HANDLE_INVALID; 434e115a09fSMatthias Ringwald return; 435e115a09fSMatthias Ringwald 436e115a09fSMatthias Ringwald default: 437e115a09fSMatthias Ringwald break; 438e115a09fSMatthias Ringwald } 4396bdecec7SMatthias Ringwald } 4406bdecec7SMatthias Ringwald 4416bdecec7SMatthias Ringwald void ancs_client_init(void){ 4426bdecec7SMatthias Ringwald hci_event_callback_registration.callback = &handle_hci_event; 4436bdecec7SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 44431d175ffSMatthias Ringwald ancs_client_handle_can_send_now.callback = &ancs_client_send_next_query; 4456bdecec7SMatthias Ringwald } 44641866fd3SMatthias Ringwald 44741866fd3SMatthias Ringwald // unit test only 44841866fd3SMatthias Ringwald #if defined __cplusplus 44941866fd3SMatthias Ringwald extern "C" 45041866fd3SMatthias Ringwald #endif 45141866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void); 45241866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void){ 45341866fd3SMatthias Ringwald chunk_parser_state = (ancs_chunk_parser_state_t) 0x17; 45441866fd3SMatthias Ringwald } 455