xref: /btstack/src/ble/gatt-service/ancs_client.c (revision 6bdecec7ba8f55d805af8d13b9f45c3a7f6d5810)
1*6bdecec7SMatthias Ringwald /*
2*6bdecec7SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3*6bdecec7SMatthias Ringwald  *
4*6bdecec7SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5*6bdecec7SMatthias Ringwald  * modification, are permitted provided that the following conditions
6*6bdecec7SMatthias Ringwald  * are met:
7*6bdecec7SMatthias Ringwald  *
8*6bdecec7SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9*6bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10*6bdecec7SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11*6bdecec7SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12*6bdecec7SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13*6bdecec7SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14*6bdecec7SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15*6bdecec7SMatthias Ringwald  *    from this software without specific prior written permission.
16*6bdecec7SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17*6bdecec7SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18*6bdecec7SMatthias Ringwald  *    monetary gain.
19*6bdecec7SMatthias Ringwald  *
20*6bdecec7SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*6bdecec7SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*6bdecec7SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*6bdecec7SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24*6bdecec7SMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*6bdecec7SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*6bdecec7SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*6bdecec7SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*6bdecec7SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*6bdecec7SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*6bdecec7SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*6bdecec7SMatthias Ringwald  * SUCH DAMAGE.
32*6bdecec7SMatthias Ringwald  *
33*6bdecec7SMatthias Ringwald  * Please inquire about commercial licensing options at
34*6bdecec7SMatthias Ringwald  * [email protected]
35*6bdecec7SMatthias Ringwald  *
36*6bdecec7SMatthias Ringwald  */
37*6bdecec7SMatthias Ringwald 
38*6bdecec7SMatthias Ringwald #define BTSTACK_FILE__ "ancs_client.c"
39*6bdecec7SMatthias Ringwald 
40*6bdecec7SMatthias Ringwald #include "btstack_config.h"
41*6bdecec7SMatthias Ringwald 
42*6bdecec7SMatthias Ringwald #include <stdint.h>
43*6bdecec7SMatthias Ringwald #include <string.h>
44*6bdecec7SMatthias Ringwald 
45*6bdecec7SMatthias Ringwald #include "ble/gatt-service/ancs_client.h"
46*6bdecec7SMatthias Ringwald 
47*6bdecec7SMatthias Ringwald #include "ble/att_db.h"
48*6bdecec7SMatthias Ringwald #include "ble/core.h"
49*6bdecec7SMatthias Ringwald #include "ble/gatt_client.h"
50*6bdecec7SMatthias Ringwald #include "ble/sm.h"
51*6bdecec7SMatthias Ringwald #include "btstack_debug.h"
52*6bdecec7SMatthias Ringwald #include "btstack_event.h"
53*6bdecec7SMatthias Ringwald #include "btstack_run_loop.h"
54*6bdecec7SMatthias Ringwald #include "gap.h"
55*6bdecec7SMatthias Ringwald 
56*6bdecec7SMatthias Ringwald // ancs_client.h Start
57*6bdecec7SMatthias Ringwald typedef enum ancs_chunk_parser_state {
58*6bdecec7SMatthias Ringwald     W4_ATTRIBUTE_ID,
59*6bdecec7SMatthias Ringwald     W4_ATTRIBUTE_LEN,
60*6bdecec7SMatthias Ringwald     W4_ATTRIBUTE_COMPLETE,
61*6bdecec7SMatthias Ringwald } ancs_chunk_parser_state_t;
62*6bdecec7SMatthias Ringwald 
63*6bdecec7SMatthias Ringwald typedef enum {
64*6bdecec7SMatthias Ringwald     TC_IDLE,
65*6bdecec7SMatthias Ringwald     TC_W4_ENCRYPTED_CONNECTION,
66*6bdecec7SMatthias Ringwald     TC_W4_SERVICE_RESULT,
67*6bdecec7SMatthias Ringwald     TC_W4_CHARACTERISTIC_RESULT,
68*6bdecec7SMatthias Ringwald     TC_W4_DATA_SOURCE_SUBSCRIBED,
69*6bdecec7SMatthias Ringwald     TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED,
70*6bdecec7SMatthias Ringwald     TC_SUBSCRIBED,
71*6bdecec7SMatthias Ringwald     TC_W4_DISCONNECT
72*6bdecec7SMatthias Ringwald } tc_state_t;
73*6bdecec7SMatthias Ringwald 
74*6bdecec7SMatthias Ringwald static uint32_t ancs_notification_uid;
75*6bdecec7SMatthias Ringwald static uint16_t gc_handle;
76*6bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_notification_source_notification;
77*6bdecec7SMatthias Ringwald static gatt_client_notification_t ancs_data_source_notification;
78*6bdecec7SMatthias Ringwald static int ancs_service_found;
79*6bdecec7SMatthias Ringwald static gatt_client_service_t  ancs_service;
80*6bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_notification_source_characteristic;
81*6bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_control_point_characteristic;
82*6bdecec7SMatthias Ringwald static gatt_client_characteristic_t ancs_data_source_characteristic;
83*6bdecec7SMatthias Ringwald static int ancs_characteristcs;
84*6bdecec7SMatthias Ringwald static tc_state_t tc_state = TC_IDLE;
85*6bdecec7SMatthias Ringwald 
86*6bdecec7SMatthias Ringwald static ancs_chunk_parser_state_t chunk_parser_state;
87*6bdecec7SMatthias Ringwald static uint8_t  ancs_notification_buffer[50];
88*6bdecec7SMatthias Ringwald static uint16_t ancs_bytes_received;
89*6bdecec7SMatthias Ringwald static uint16_t ancs_bytes_needed;
90*6bdecec7SMatthias Ringwald static uint8_t  ancs_attribute_id;
91*6bdecec7SMatthias Ringwald static uint16_t ancs_attribute_len;
92*6bdecec7SMatthias Ringwald 
93*6bdecec7SMatthias Ringwald static btstack_packet_handler_t client_handler;
94*6bdecec7SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
95*6bdecec7SMatthias Ringwald 
96*6bdecec7SMatthias Ringwald void ancs_client_register_callback(btstack_packet_handler_t handler){
97*6bdecec7SMatthias Ringwald     client_handler = handler;
98*6bdecec7SMatthias Ringwald }
99*6bdecec7SMatthias Ringwald 
100*6bdecec7SMatthias Ringwald static void notify_client_text(int event_type){
101*6bdecec7SMatthias Ringwald     if (!client_handler) return;
102*6bdecec7SMatthias Ringwald     uint8_t event[7 + sizeof(ancs_notification_buffer) + 1];
103*6bdecec7SMatthias Ringwald     event[0] = HCI_EVENT_ANCS_META;
104*6bdecec7SMatthias Ringwald     event[1] = 5u + ancs_attribute_len;
105*6bdecec7SMatthias Ringwald     event[2] = event_type;
106*6bdecec7SMatthias Ringwald     little_endian_store_16(event, 3, gc_handle);
107*6bdecec7SMatthias Ringwald     little_endian_store_16(event, 5, ancs_attribute_id);
108*6bdecec7SMatthias Ringwald     (void)memcpy(&event[7], ancs_notification_buffer, ancs_attribute_len);
109*6bdecec7SMatthias Ringwald     // we're nice
110*6bdecec7SMatthias Ringwald     event[7u+ancs_attribute_len] = 0u;
111*6bdecec7SMatthias Ringwald     (*client_handler)(HCI_EVENT_PACKET, 0u, event, event[1u] + 2u);
112*6bdecec7SMatthias Ringwald }
113*6bdecec7SMatthias Ringwald 
114*6bdecec7SMatthias Ringwald static void notify_client_simple(int event_type){
115*6bdecec7SMatthias Ringwald     if (!client_handler) return;
116*6bdecec7SMatthias Ringwald     uint8_t event[5];
117*6bdecec7SMatthias Ringwald     event[0] = HCI_EVENT_ANCS_META;
118*6bdecec7SMatthias Ringwald     event[1] = 3;
119*6bdecec7SMatthias Ringwald     event[2] = event_type;
120*6bdecec7SMatthias Ringwald     little_endian_store_16(event, 3, gc_handle);
121*6bdecec7SMatthias Ringwald     (*client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
122*6bdecec7SMatthias Ringwald }
123*6bdecec7SMatthias Ringwald 
124*6bdecec7SMatthias Ringwald static void ancs_chunk_parser_init(void){
125*6bdecec7SMatthias Ringwald     chunk_parser_state = W4_ATTRIBUTE_ID;
126*6bdecec7SMatthias Ringwald     ancs_bytes_received = 0;
127*6bdecec7SMatthias Ringwald     ancs_bytes_needed = 6;
128*6bdecec7SMatthias Ringwald }
129*6bdecec7SMatthias Ringwald 
130*6bdecec7SMatthias Ringwald const char * ancs_client_attribute_name_for_id(int id){
131*6bdecec7SMatthias Ringwald     static const char * ancs_attribute_names[] = {
132*6bdecec7SMatthias Ringwald             "AppIdentifier",
133*6bdecec7SMatthias Ringwald             "IDTitle",
134*6bdecec7SMatthias Ringwald             "IDSubtitle",
135*6bdecec7SMatthias Ringwald             "IDMessage",
136*6bdecec7SMatthias Ringwald             "IDMessageSize",
137*6bdecec7SMatthias Ringwald             "IDDate"
138*6bdecec7SMatthias Ringwald     };
139*6bdecec7SMatthias Ringwald 
140*6bdecec7SMatthias Ringwald     static const uint16_t ANCS_ATTRBUTE_NAMES_COUNT = sizeof(ancs_attribute_names) / sizeof(char *);
141*6bdecec7SMatthias Ringwald 
142*6bdecec7SMatthias Ringwald     if (id >= ANCS_ATTRBUTE_NAMES_COUNT) return NULL;
143*6bdecec7SMatthias Ringwald     return ancs_attribute_names[id];
144*6bdecec7SMatthias Ringwald }
145*6bdecec7SMatthias Ringwald 
146*6bdecec7SMatthias Ringwald static void ancs_chunk_parser_handle_byte(uint8_t data){
147*6bdecec7SMatthias Ringwald     ancs_notification_buffer[ancs_bytes_received++] = data;
148*6bdecec7SMatthias Ringwald     if (ancs_bytes_received < ancs_bytes_needed) return;
149*6bdecec7SMatthias Ringwald     switch (chunk_parser_state){
150*6bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_ID:
151*6bdecec7SMatthias Ringwald             ancs_attribute_id   = ancs_notification_buffer[ancs_bytes_received-1u];
152*6bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
153*6bdecec7SMatthias Ringwald             ancs_bytes_needed   = 2;
154*6bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_LEN;
155*6bdecec7SMatthias Ringwald             break;
156*6bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_LEN:
157*6bdecec7SMatthias Ringwald             ancs_attribute_len  = little_endian_read_16(ancs_notification_buffer, ancs_bytes_received-2u);
158*6bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
159*6bdecec7SMatthias Ringwald             ancs_bytes_needed   = ancs_attribute_len;
160*6bdecec7SMatthias Ringwald             if (ancs_attribute_len == 0u) {
161*6bdecec7SMatthias Ringwald                 ancs_bytes_needed   = 1;
162*6bdecec7SMatthias Ringwald                 chunk_parser_state  = W4_ATTRIBUTE_ID;
163*6bdecec7SMatthias Ringwald                 break;
164*6bdecec7SMatthias Ringwald             }
165*6bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_COMPLETE;
166*6bdecec7SMatthias Ringwald             break;
167*6bdecec7SMatthias Ringwald         case W4_ATTRIBUTE_COMPLETE:
168*6bdecec7SMatthias Ringwald             ancs_notification_buffer[ancs_bytes_received] = 0;
169*6bdecec7SMatthias Ringwald             notify_client_text(ANCS_SUBEVENT_CLIENT_NOTIFICATION);
170*6bdecec7SMatthias Ringwald             ancs_bytes_received = 0;
171*6bdecec7SMatthias Ringwald             ancs_bytes_needed   = 1;
172*6bdecec7SMatthias Ringwald             chunk_parser_state  = W4_ATTRIBUTE_ID;
173*6bdecec7SMatthias Ringwald             break;
174*6bdecec7SMatthias Ringwald         default:
175*6bdecec7SMatthias Ringwald             btstack_assert(false);
176*6bdecec7SMatthias Ringwald             break;
177*6bdecec7SMatthias Ringwald     }
178*6bdecec7SMatthias Ringwald }
179*6bdecec7SMatthias Ringwald 
180*6bdecec7SMatthias Ringwald static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
181*6bdecec7SMatthias Ringwald 
182*6bdecec7SMatthias Ringwald     UNUSED(packet_type); // ok: only hci events
183*6bdecec7SMatthias Ringwald     UNUSED(channel);     // ok: there is no channel
184*6bdecec7SMatthias Ringwald     UNUSED(size);        // ok: fixed format events read from HCI buffer
185*6bdecec7SMatthias Ringwald 
186*6bdecec7SMatthias Ringwald     static const uint8_t ancs_service_uuid[] =             {0x79,0x05,0xF4,0x31,0xB5,0xCE,0x4E,0x99,0xA4,0x0F,0x4B,0x1E,0x12,0x2D,0x00,0xD0};
187*6bdecec7SMatthias 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};
188*6bdecec7SMatthias 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};
189*6bdecec7SMatthias 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};
190*6bdecec7SMatthias Ringwald 
191*6bdecec7SMatthias Ringwald 
192*6bdecec7SMatthias Ringwald     int connection_encrypted;
193*6bdecec7SMatthias Ringwald 
194*6bdecec7SMatthias Ringwald     // handle connect / disconncet events first
195*6bdecec7SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
196*6bdecec7SMatthias Ringwald         case HCI_EVENT_LE_META:
197*6bdecec7SMatthias Ringwald             switch (packet[2]) {
198*6bdecec7SMatthias Ringwald                 case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
199*6bdecec7SMatthias Ringwald                     gc_handle = little_endian_read_16(packet, 4);
200*6bdecec7SMatthias Ringwald                     log_info("Connection handle 0x%04x, request encryption", gc_handle);
201*6bdecec7SMatthias Ringwald 
202*6bdecec7SMatthias Ringwald                     // we need to be paired to enable notifications
203*6bdecec7SMatthias Ringwald                     tc_state = TC_W4_ENCRYPTED_CONNECTION;
204*6bdecec7SMatthias Ringwald                     sm_request_pairing(gc_handle);
205*6bdecec7SMatthias Ringwald                     break;
206*6bdecec7SMatthias Ringwald                 default:
207*6bdecec7SMatthias Ringwald                     break;
208*6bdecec7SMatthias Ringwald             }
209*6bdecec7SMatthias Ringwald             return;
210*6bdecec7SMatthias Ringwald 
211*6bdecec7SMatthias Ringwald         case HCI_EVENT_ENCRYPTION_CHANGE:
212*6bdecec7SMatthias Ringwald             if (gc_handle != little_endian_read_16(packet, 3)) return;
213*6bdecec7SMatthias Ringwald             connection_encrypted = packet[5];
214*6bdecec7SMatthias Ringwald             log_info("Encryption state change: %u", connection_encrypted);
215*6bdecec7SMatthias Ringwald             if (!connection_encrypted) return;
216*6bdecec7SMatthias Ringwald             if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return;
217*6bdecec7SMatthias Ringwald 
218*6bdecec7SMatthias Ringwald             // let's start
219*6bdecec7SMatthias Ringwald             log_info("\nANCS Client - CONNECTED, discover ANCS service");
220*6bdecec7SMatthias Ringwald             tc_state = TC_W4_SERVICE_RESULT;
221*6bdecec7SMatthias Ringwald             gatt_client_discover_primary_services_by_uuid128(handle_hci_event, gc_handle, ancs_service_uuid);
222*6bdecec7SMatthias Ringwald             return;
223*6bdecec7SMatthias Ringwald 
224*6bdecec7SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
225*6bdecec7SMatthias Ringwald             if (hci_event_disconnection_complete_get_connection_handle(packet) != gc_handle) break;
226*6bdecec7SMatthias Ringwald             if (tc_state == TC_SUBSCRIBED){
227*6bdecec7SMatthias Ringwald                 notify_client_simple(ANCS_SUBEVENT_CLIENT_DISCONNECTED);
228*6bdecec7SMatthias Ringwald             }
229*6bdecec7SMatthias Ringwald             tc_state = TC_IDLE;
230*6bdecec7SMatthias Ringwald             gc_handle = 0;
231*6bdecec7SMatthias Ringwald             return;
232*6bdecec7SMatthias Ringwald 
233*6bdecec7SMatthias Ringwald         default:
234*6bdecec7SMatthias Ringwald             break;
235*6bdecec7SMatthias Ringwald     }
236*6bdecec7SMatthias Ringwald 
237*6bdecec7SMatthias Ringwald     gatt_client_characteristic_t characteristic;
238*6bdecec7SMatthias Ringwald     uint8_t *           value;
239*6bdecec7SMatthias Ringwald     uint16_t            value_handle;
240*6bdecec7SMatthias Ringwald     uint16_t            value_length;
241*6bdecec7SMatthias Ringwald 
242*6bdecec7SMatthias Ringwald     switch(tc_state){
243*6bdecec7SMatthias Ringwald         case TC_W4_SERVICE_RESULT:
244*6bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
245*6bdecec7SMatthias Ringwald                 case GATT_EVENT_SERVICE_QUERY_RESULT:
246*6bdecec7SMatthias Ringwald                     gatt_event_service_query_result_get_service(packet, &ancs_service);
247*6bdecec7SMatthias Ringwald                     ancs_service_found = 1;
248*6bdecec7SMatthias Ringwald                     break;
249*6bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
250*6bdecec7SMatthias Ringwald                     if (!ancs_service_found){
251*6bdecec7SMatthias Ringwald                         log_info("ANCS Service not found");
252*6bdecec7SMatthias Ringwald                         tc_state = TC_IDLE;
253*6bdecec7SMatthias Ringwald                         break;
254*6bdecec7SMatthias Ringwald                     }
255*6bdecec7SMatthias Ringwald                     tc_state = TC_W4_CHARACTERISTIC_RESULT;
256*6bdecec7SMatthias Ringwald                     log_info("ANCS Client - Discover characteristics for ANCS SERVICE ");
257*6bdecec7SMatthias Ringwald                     gatt_client_discover_characteristics_for_service(handle_hci_event, gc_handle, &ancs_service);
258*6bdecec7SMatthias Ringwald                     break;
259*6bdecec7SMatthias Ringwald                 default:
260*6bdecec7SMatthias Ringwald                     break;
261*6bdecec7SMatthias Ringwald             }
262*6bdecec7SMatthias Ringwald             break;
263*6bdecec7SMatthias Ringwald 
264*6bdecec7SMatthias Ringwald         case TC_W4_CHARACTERISTIC_RESULT:
265*6bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
266*6bdecec7SMatthias Ringwald                 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
267*6bdecec7SMatthias Ringwald                     gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
268*6bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0){
269*6bdecec7SMatthias Ringwald                         log_info("ANCS Notification Source found, attribute handle %u", characteristic.value_handle);
270*6bdecec7SMatthias Ringwald                         ancs_notification_source_characteristic = characteristic;
271*6bdecec7SMatthias Ringwald                         ancs_characteristcs++;
272*6bdecec7SMatthias Ringwald                         break;
273*6bdecec7SMatthias Ringwald                     }
274*6bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0){
275*6bdecec7SMatthias Ringwald                         log_info("ANCS Control Point found, attribute handle %u", characteristic.value_handle);
276*6bdecec7SMatthias Ringwald                         ancs_control_point_characteristic = characteristic;
277*6bdecec7SMatthias Ringwald                         ancs_characteristcs++;
278*6bdecec7SMatthias Ringwald                         break;
279*6bdecec7SMatthias Ringwald                     }
280*6bdecec7SMatthias Ringwald                     if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0){
281*6bdecec7SMatthias Ringwald                         log_info("ANCS Data Source found, attribute handle %u", characteristic.value_handle);
282*6bdecec7SMatthias Ringwald                         ancs_data_source_characteristic = characteristic;
283*6bdecec7SMatthias Ringwald                         ancs_characteristcs++;
284*6bdecec7SMatthias Ringwald                         break;
285*6bdecec7SMatthias Ringwald                     }
286*6bdecec7SMatthias Ringwald                     break;
287*6bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
288*6bdecec7SMatthias Ringwald                     log_info("ANCS Characteristcs count %u", ancs_characteristcs);
289*6bdecec7SMatthias Ringwald                     tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED;
290*6bdecec7SMatthias Ringwald                     gatt_client_listen_for_characteristic_value_updates(&ancs_notification_source_notification, &handle_hci_event, gc_handle, &ancs_notification_source_characteristic);
291*6bdecec7SMatthias Ringwald                     gatt_client_write_client_characteristic_configuration(handle_hci_event, gc_handle, &ancs_notification_source_characteristic,
292*6bdecec7SMatthias Ringwald                         GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
293*6bdecec7SMatthias Ringwald                     break;
294*6bdecec7SMatthias Ringwald                 default:
295*6bdecec7SMatthias Ringwald                     break;
296*6bdecec7SMatthias Ringwald             }
297*6bdecec7SMatthias Ringwald             break;
298*6bdecec7SMatthias Ringwald         case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED:
299*6bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
300*6bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
301*6bdecec7SMatthias Ringwald                     log_info("ANCS Notification Source subscribed");
302*6bdecec7SMatthias Ringwald                     tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED;
303*6bdecec7SMatthias Ringwald                     gatt_client_listen_for_characteristic_value_updates(&ancs_data_source_notification, &handle_hci_event, gc_handle, &ancs_data_source_characteristic);
304*6bdecec7SMatthias Ringwald                     gatt_client_write_client_characteristic_configuration(handle_hci_event, gc_handle, &ancs_data_source_characteristic,
305*6bdecec7SMatthias Ringwald                         GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
306*6bdecec7SMatthias Ringwald                     break;
307*6bdecec7SMatthias Ringwald                 default:
308*6bdecec7SMatthias Ringwald                     break;
309*6bdecec7SMatthias Ringwald             }
310*6bdecec7SMatthias Ringwald             break;
311*6bdecec7SMatthias Ringwald         case TC_W4_DATA_SOURCE_SUBSCRIBED:
312*6bdecec7SMatthias Ringwald             switch(hci_event_packet_get_type(packet)){
313*6bdecec7SMatthias Ringwald                 case GATT_EVENT_QUERY_COMPLETE:
314*6bdecec7SMatthias Ringwald                     log_info("ANCS Data Source subscribed");
315*6bdecec7SMatthias Ringwald                     tc_state = TC_SUBSCRIBED;
316*6bdecec7SMatthias Ringwald                     notify_client_simple(ANCS_SUBEVENT_CLIENT_CONNECTED);
317*6bdecec7SMatthias Ringwald                     break;
318*6bdecec7SMatthias Ringwald                 default:
319*6bdecec7SMatthias Ringwald                     break;
320*6bdecec7SMatthias Ringwald             }
321*6bdecec7SMatthias Ringwald             break;
322*6bdecec7SMatthias Ringwald         case TC_SUBSCRIBED:
323*6bdecec7SMatthias Ringwald             if ((hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) && (hci_event_packet_get_type(packet) != GATT_EVENT_INDICATION) ) break;
324*6bdecec7SMatthias Ringwald 
325*6bdecec7SMatthias Ringwald             value_handle = little_endian_read_16(packet, 4);
326*6bdecec7SMatthias Ringwald             value_length = little_endian_read_16(packet, 6);
327*6bdecec7SMatthias Ringwald             value = &packet[8];
328*6bdecec7SMatthias Ringwald 
329*6bdecec7SMatthias Ringwald             log_info("ANCS Notification, value handle %u", value_handle);
330*6bdecec7SMatthias Ringwald 
331*6bdecec7SMatthias Ringwald             if (value_handle == ancs_data_source_characteristic.value_handle){
332*6bdecec7SMatthias Ringwald                 int i;
333*6bdecec7SMatthias Ringwald                 for (i=0;i<value_length;i++) {
334*6bdecec7SMatthias Ringwald                     ancs_chunk_parser_handle_byte(value[i]);
335*6bdecec7SMatthias Ringwald                 }
336*6bdecec7SMatthias Ringwald             } else if (value_handle == ancs_notification_source_characteristic.value_handle){
337*6bdecec7SMatthias Ringwald                 ancs_notification_uid = little_endian_read_32(value, 4);
338*6bdecec7SMatthias Ringwald                 log_info("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x",
339*6bdecec7SMatthias Ringwald                     value[0], value[1], value[2], value[3], (int) ancs_notification_uid);
340*6bdecec7SMatthias Ringwald                 static uint8_t get_notification_attributes[] = {0, 0,0,0,0,  0,  1,32,0,  2,32,0, 3,32,0, 4, 5};
341*6bdecec7SMatthias Ringwald                 little_endian_store_32(get_notification_attributes, 1, ancs_notification_uid);
342*6bdecec7SMatthias Ringwald                 ancs_notification_uid = 0;
343*6bdecec7SMatthias Ringwald                 ancs_chunk_parser_init();
344*6bdecec7SMatthias Ringwald                 gatt_client_write_value_of_characteristic(handle_hci_event, gc_handle, ancs_control_point_characteristic.value_handle,
345*6bdecec7SMatthias Ringwald                     sizeof(get_notification_attributes), get_notification_attributes);
346*6bdecec7SMatthias Ringwald             } else {
347*6bdecec7SMatthias Ringwald                 log_info("Unknown Source: ");
348*6bdecec7SMatthias Ringwald                 log_info_hexdump(value , value_length);
349*6bdecec7SMatthias Ringwald             }
350*6bdecec7SMatthias Ringwald             break;
351*6bdecec7SMatthias Ringwald         default:
352*6bdecec7SMatthias Ringwald             break;
353*6bdecec7SMatthias Ringwald     }
354*6bdecec7SMatthias Ringwald     // app_run();
355*6bdecec7SMatthias Ringwald }
356*6bdecec7SMatthias Ringwald 
357*6bdecec7SMatthias Ringwald void ancs_client_init(void){
358*6bdecec7SMatthias Ringwald     hci_event_callback_registration.callback = &handle_hci_event;
359*6bdecec7SMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
360*6bdecec7SMatthias Ringwald }
361