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
ancs_client_register_callback(btstack_packet_handler_t handler)1016bdecec7SMatthias Ringwald void ancs_client_register_callback(btstack_packet_handler_t handler){
1026bdecec7SMatthias Ringwald client_handler = handler;
1036bdecec7SMatthias Ringwald }
1046bdecec7SMatthias Ringwald
ancs_client_request_send_gatt_query(void)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
notify_client_text(int event_type)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
notify_client_simple(int event_type)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
ancs_chunk_parser_init(void)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
ancs_client_attribute_name_for_id(int id)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
ancs_chunk_parser_handle_byte(uint8_t data)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
ancs_client_handle_notification(uint16_t value_handle,const uint8_t * value,uint16_t value_length)195*77043c43SMatthias Ringwald static void ancs_client_handle_notification(uint16_t value_handle, const uint8_t * value, uint16_t value_length){
1966bdecec7SMatthias Ringwald
197e115a09fSMatthias Ringwald log_info("ANCS Notification, value handle %u", value_handle);
1986bdecec7SMatthias Ringwald
199e115a09fSMatthias Ringwald if (value_handle == ancs_data_source_characteristic.value_handle){
200e115a09fSMatthias Ringwald int i;
201e115a09fSMatthias Ringwald for (i=0;i<value_length;i++) {
202e115a09fSMatthias Ringwald ancs_chunk_parser_handle_byte(value[i]);
203e115a09fSMatthias Ringwald }
204e115a09fSMatthias Ringwald } else if (value_handle == ancs_notification_source_characteristic.value_handle){
205e115a09fSMatthias Ringwald ancs_notification_uid = little_endian_read_32(value, 4);
206e115a09fSMatthias Ringwald log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x",
207e115a09fSMatthias Ringwald value[0], value[1], value[2], value[3], (int) ancs_notification_uid);
208e115a09fSMatthias Ringwald static uint8_t get_notification_attributes[] = {0, 0,0,0,0, 0, 1,32,0, 2,32,0, 3,32,0, 4, 5};
209e115a09fSMatthias Ringwald little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid);
210e115a09fSMatthias Ringwald ancs_notification_uid = 0;
211e115a09fSMatthias Ringwald ancs_chunk_parser_init();
212e115a09fSMatthias Ringwald gatt_client_write_value_of_characteristic(ancs_client_handle_gatt_client_event, gc_handle, ancs_control_point_characteristic.value_handle,
213e115a09fSMatthias Ringwald sizeof(get_notification_attributes), get_notification_attributes);
214e115a09fSMatthias Ringwald } else {
215e115a09fSMatthias Ringwald log_info("Unknown Source: ");
216e115a09fSMatthias Ringwald log_info_hexdump(value , value_length);
217e115a09fSMatthias Ringwald }
218e115a09fSMatthias Ringwald }
219e115a09fSMatthias Ringwald
ancs_client_send_next_query(void * context)22031d175ffSMatthias Ringwald static void ancs_client_send_next_query(void * context){
22131d175ffSMatthias Ringwald UNUSED(context);
22231d175ffSMatthias Ringwald static const uint8_t ancs_service_uuid[] = {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0};
22331d175ffSMatthias Ringwald
22431d175ffSMatthias Ringwald switch(tc_state){
22531d175ffSMatthias Ringwald case TC_W2_QUERY_SERVICE:
22631d175ffSMatthias Ringwald tc_state = TC_W4_SERVICE_RESULT;
22731d175ffSMatthias Ringwald (void) gatt_client_discover_primary_services_by_uuid128(ancs_client_handle_gatt_client_event, gc_handle, ancs_service_uuid);
22831d175ffSMatthias Ringwald break;
22931d175ffSMatthias Ringwald
23031d175ffSMatthias Ringwald case TC_W2_QUERY_CARACTERISTIC:
23131d175ffSMatthias Ringwald tc_state = TC_W4_CHARACTERISTIC_RESULT;
23231d175ffSMatthias Ringwald log_info("ANCS Client - Discover characteristics for ANCS SERVICE ");
23331d175ffSMatthias Ringwald gatt_client_discover_characteristics_for_service(ancs_client_handle_gatt_client_event, gc_handle, &ancs_service);
23431d175ffSMatthias Ringwald break;
23531d175ffSMatthias Ringwald
23631d175ffSMatthias Ringwald case TC_W2_ENABLE_NOTIFICATION:
23731d175ffSMatthias Ringwald tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED;
23831d175ffSMatthias Ringwald gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic);
23931d175ffSMatthias Ringwald gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_notification_source_characteristic,
24031d175ffSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
24131d175ffSMatthias Ringwald break;
24231d175ffSMatthias Ringwald
24331d175ffSMatthias Ringwald case TC_W2_SUBSCRIBE_DATA_SOURCE:
24431d175ffSMatthias Ringwald tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED;
24531d175ffSMatthias Ringwald gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic);
24631d175ffSMatthias Ringwald gatt_client_write_client_characteristic_configuration(ancs_client_handle_gatt_client_event, gc_handle, &ancs_data_source_characteristic,
24731d175ffSMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
24831d175ffSMatthias Ringwald break;
24931d175ffSMatthias Ringwald
25031d175ffSMatthias Ringwald default:
25131d175ffSMatthias Ringwald break;
25231d175ffSMatthias Ringwald }
25331d175ffSMatthias Ringwald }
25431d175ffSMatthias Ringwald
ancs_client_handle_gatt_client_event_in_w4_service_result(uint8_t * packet)2550d0cbb52SMatthias Ringwald static void ancs_client_handle_gatt_client_event_in_w4_service_result(uint8_t* packet) {
2566bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
2576bdecec7SMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT:
2586bdecec7SMatthias Ringwald gatt_event_service_query_result_get_service(packet, &ancs_service);
2596bdecec7SMatthias Ringwald ancs_service_found = 1;
2606bdecec7SMatthias Ringwald break;
2616bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
2626bdecec7SMatthias Ringwald if (!ancs_service_found){
2636bdecec7SMatthias Ringwald log_info("ANCS Service not found");
2646bdecec7SMatthias Ringwald tc_state = TC_IDLE;
2656bdecec7SMatthias Ringwald break;
2666bdecec7SMatthias Ringwald }
26731d175ffSMatthias Ringwald tc_state = TC_W2_QUERY_CARACTERISTIC;
2686bdecec7SMatthias Ringwald break;
2696bdecec7SMatthias Ringwald default:
2706bdecec7SMatthias Ringwald break;
2716bdecec7SMatthias Ringwald }
2720d0cbb52SMatthias Ringwald }
2730d0cbb52SMatthias Ringwald
ancs_client_handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)2740d0cbb52SMatthias Ringwald static void ancs_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
2750d0cbb52SMatthias Ringwald
2760d0cbb52SMatthias Ringwald UNUSED(packet_type);
2770d0cbb52SMatthias Ringwald UNUSED(channel);
278*77043c43SMatthias Ringwald UNUSED(size);
2790d0cbb52SMatthias Ringwald
2800d0cbb52SMatthias 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};
2810d0cbb52SMatthias 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};
2820d0cbb52SMatthias 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};
2830d0cbb52SMatthias Ringwald
2840d0cbb52SMatthias Ringwald gatt_client_characteristic_t characteristic;
2850d0cbb52SMatthias Ringwald
2860d0cbb52SMatthias Ringwald switch(tc_state){
2870d0cbb52SMatthias Ringwald case TC_W4_SERVICE_RESULT:
2880d0cbb52SMatthias Ringwald ancs_client_handle_gatt_client_event_in_w4_service_result(packet);
2896bdecec7SMatthias Ringwald break;
2906bdecec7SMatthias Ringwald
2916bdecec7SMatthias Ringwald case TC_W4_CHARACTERISTIC_RESULT:
2926bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
2936bdecec7SMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
2946bdecec7SMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
2956bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){
2966bdecec7SMatthias Ringwald log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle);
2976bdecec7SMatthias Ringwald ancs_notification_source_characteristic = characteristic;
2986bdecec7SMatthias Ringwald ancs_characteristcs++;
2996bdecec7SMatthias Ringwald break;
3006bdecec7SMatthias Ringwald }
3016bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){
3026bdecec7SMatthias Ringwald log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle);
3036bdecec7SMatthias Ringwald ancs_control_point_characteristic = characteristic;
3046bdecec7SMatthias Ringwald ancs_characteristcs++;
3056bdecec7SMatthias Ringwald break;
3066bdecec7SMatthias Ringwald }
3076bdecec7SMatthias Ringwald if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){
3086bdecec7SMatthias Ringwald log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle);
3096bdecec7SMatthias Ringwald ancs_data_source_characteristic = characteristic;
3106bdecec7SMatthias Ringwald ancs_characteristcs++;
3116bdecec7SMatthias Ringwald break;
3126bdecec7SMatthias Ringwald }
3136bdecec7SMatthias Ringwald break;
3146bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
3156bdecec7SMatthias Ringwald log_info("ANCS Characteristcs count %u", ancs_characteristcs);
31631d175ffSMatthias Ringwald tc_state = TC_W2_ENABLE_NOTIFICATION;
3176bdecec7SMatthias Ringwald break;
3186bdecec7SMatthias Ringwald default:
3196bdecec7SMatthias Ringwald break;
3206bdecec7SMatthias Ringwald }
3216bdecec7SMatthias Ringwald break;
32231d175ffSMatthias Ringwald
3236bdecec7SMatthias Ringwald case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED:
3246bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
3256bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
3266bdecec7SMatthias Ringwald log_info("ANCS Notification Source subscribed");
32731d175ffSMatthias Ringwald tc_state = TC_W2_SUBSCRIBE_DATA_SOURCE;
32831d175ffSMatthias Ringwald
3296bdecec7SMatthias Ringwald break;
3306bdecec7SMatthias Ringwald default:
3316bdecec7SMatthias Ringwald break;
3326bdecec7SMatthias Ringwald }
3336bdecec7SMatthias Ringwald break;
33431d175ffSMatthias Ringwald
3356bdecec7SMatthias Ringwald case TC_W4_DATA_SOURCE_SUBSCRIBED:
3366bdecec7SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
3376bdecec7SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
3386bdecec7SMatthias Ringwald log_info("ANCS Data Source subscribed");
3396bdecec7SMatthias Ringwald tc_state = TC_SUBSCRIBED;
3406bdecec7SMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED);
3416bdecec7SMatthias Ringwald break;
3426bdecec7SMatthias Ringwald default:
3436bdecec7SMatthias Ringwald break;
3446bdecec7SMatthias Ringwald }
3456bdecec7SMatthias Ringwald break;
34631d175ffSMatthias Ringwald
3476bdecec7SMatthias Ringwald case TC_SUBSCRIBED:
34841866fd3SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
34941866fd3SMatthias Ringwald case GATT_EVENT_NOTIFICATION:
350*77043c43SMatthias Ringwald ancs_client_handle_notification(
351*77043c43SMatthias Ringwald gatt_event_notification_get_value_handle(packet),
352*77043c43SMatthias Ringwald gatt_event_notification_get_value(packet),
353*77043c43SMatthias Ringwald gatt_event_notification_get_value_length(packet)
354*77043c43SMatthias Ringwald );
355*77043c43SMatthias Ringwald break;
35641866fd3SMatthias Ringwald case GATT_EVENT_INDICATION:
357*77043c43SMatthias Ringwald ancs_client_handle_notification(
358*77043c43SMatthias Ringwald gatt_event_indication_get_value_handle(packet),
359*77043c43SMatthias Ringwald gatt_event_indication_get_value(packet),
360*77043c43SMatthias Ringwald gatt_event_indication_get_value_length(packet)
361*77043c43SMatthias Ringwald );
3626bdecec7SMatthias Ringwald break;
3636bdecec7SMatthias Ringwald default:
3646bdecec7SMatthias Ringwald break;
3656bdecec7SMatthias Ringwald }
36641866fd3SMatthias Ringwald break;
36731d175ffSMatthias Ringwald
36831d175ffSMatthias Ringwald default:
36931d175ffSMatthias Ringwald break;
37031d175ffSMatthias Ringwald }
37131d175ffSMatthias Ringwald
37231d175ffSMatthias Ringwald uint8_t status;
37331d175ffSMatthias Ringwald switch(tc_state){
37431d175ffSMatthias Ringwald case TC_W2_QUERY_SERVICE:
37531d175ffSMatthias Ringwald case TC_W2_QUERY_CARACTERISTIC:
37631d175ffSMatthias Ringwald case TC_W2_ENABLE_NOTIFICATION:
37731d175ffSMatthias Ringwald case TC_W2_SUBSCRIBE_DATA_SOURCE:
37831d175ffSMatthias Ringwald status = gatt_client_request_to_send_gatt_query(&ancs_client_handle_can_send_now, gc_handle);
37931d175ffSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
38031d175ffSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED);
38131d175ffSMatthias Ringwald }
38231d175ffSMatthias Ringwald break;
38331d175ffSMatthias Ringwald
38441866fd3SMatthias Ringwald default:
38541866fd3SMatthias Ringwald break;
38641866fd3SMatthias Ringwald }
387e115a09fSMatthias Ringwald }
388e115a09fSMatthias Ringwald
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)389e115a09fSMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
390e115a09fSMatthias Ringwald
391e115a09fSMatthias Ringwald UNUSED(packet_type); // ok: only hci events
392e115a09fSMatthias Ringwald UNUSED(channel); // ok: there is no channel
393e115a09fSMatthias Ringwald UNUSED(size); // ok: fixed format events read from HCI buffer
394e115a09fSMatthias Ringwald
395e115a09fSMatthias Ringwald int connection_encrypted;
39631d175ffSMatthias Ringwald uint8_t status;
397e115a09fSMatthias Ringwald
398e115a09fSMatthias Ringwald // handle connect / disconncet events first
399e115a09fSMatthias Ringwald switch (hci_event_packet_get_type(packet)) {
4009d1eff91SMatthias Ringwald case HCI_EVENT_META_GAP:
4019d1eff91SMatthias Ringwald switch (hci_event_gap_meta_get_subevent_code(packet)) {
4029d1eff91SMatthias Ringwald case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
4039d1eff91SMatthias Ringwald gc_handle = gap_subevent_le_connection_complete_get_connection_handle(packet);
404e115a09fSMatthias Ringwald log_info("Connection handle 0x%04x, request encryption", gc_handle);
405e115a09fSMatthias Ringwald
406e115a09fSMatthias Ringwald // we need to be paired to enable notifications
407e115a09fSMatthias Ringwald tc_state = TC_W4_ENCRYPTED_CONNECTION;
408e115a09fSMatthias Ringwald ancs_service_found = false;
409e115a09fSMatthias Ringwald sm_request_pairing(gc_handle);
410e115a09fSMatthias Ringwald break;
411e115a09fSMatthias Ringwald default:
412e115a09fSMatthias Ringwald break;
413e115a09fSMatthias Ringwald }
414e115a09fSMatthias Ringwald return;
415e115a09fSMatthias Ringwald
416e115a09fSMatthias Ringwald case HCI_EVENT_ENCRYPTION_CHANGE:
4178601e696SMatthias Ringwald case HCI_EVENT_ENCRYPTION_CHANGE_V2:
4188601e696SMatthias Ringwald if (gc_handle != hci_event_encryption_change_get_connection_handle(packet)) return;
4198601e696SMatthias Ringwald connection_encrypted = hci_event_encryption_change_get_encryption_enabled(packet);
420e115a09fSMatthias Ringwald log_info("Encryption state change: %u", connection_encrypted);
421e115a09fSMatthias Ringwald if (!connection_encrypted) return;
422e115a09fSMatthias Ringwald if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return;
423e115a09fSMatthias Ringwald
424e115a09fSMatthias Ringwald // let's start
425e115a09fSMatthias Ringwald log_info("\nANCS Client - CONNECTED, discover ANCS service");
42631d175ffSMatthias Ringwald tc_state = TC_W2_QUERY_SERVICE;
42731d175ffSMatthias Ringwald status = ancs_client_request_send_gatt_query();
42831d175ffSMatthias Ringwald if (status != ERROR_CODE_SUCCESS){
42931d175ffSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED);
43031d175ffSMatthias Ringwald }
431e115a09fSMatthias Ringwald return;
432e115a09fSMatthias Ringwald
433e115a09fSMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE:
434e115a09fSMatthias Ringwald if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break;
435e115a09fSMatthias Ringwald if (tc_state == TC_SUBSCRIBED){
436e115a09fSMatthias Ringwald notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED);
437e115a09fSMatthias Ringwald }
438e115a09fSMatthias Ringwald tc_state = TC_IDLE;
43931d175ffSMatthias Ringwald gc_handle = HCI_CON_HANDLE_INVALID;
440e115a09fSMatthias Ringwald return;
441e115a09fSMatthias Ringwald
442e115a09fSMatthias Ringwald default:
443e115a09fSMatthias Ringwald break;
444e115a09fSMatthias Ringwald }
4456bdecec7SMatthias Ringwald }
4466bdecec7SMatthias Ringwald
ancs_client_init(void)4476bdecec7SMatthias Ringwald void ancs_client_init(void){
4486bdecec7SMatthias Ringwald hci_event_callback_registration.callback = &handle_hci_event;
4496bdecec7SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration);
45031d175ffSMatthias Ringwald ancs_client_handle_can_send_now.callback = &ancs_client_send_next_query;
4516bdecec7SMatthias Ringwald }
45241866fd3SMatthias Ringwald
45341866fd3SMatthias Ringwald // unit test only
45441866fd3SMatthias Ringwald #if defined __cplusplus
45541866fd3SMatthias Ringwald extern "C"
45641866fd3SMatthias Ringwald #endif
45741866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void);
ancs_client_set_invalid_parser_state(void)45841866fd3SMatthias Ringwald void ancs_client_set_invalid_parser_state(void){
45941866fd3SMatthias Ringwald chunk_parser_state = (ancs_chunk_parser_state_t) 0x17;
46041866fd3SMatthias Ringwald }
461