xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision f368eb91f4405e2bc282bf963e549e4e03d07b55)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "battery_service_v1_server.c"
39 
40 /**
41  * Implementation of the GATT Battery Service Server
42  * To use with your application, add `#import <battery_service.gatt>` to your .gatt file
43  */
44 #include <stdio.h>
45 #include "btstack_defines.h"
46 #include "ble/att_db.h"
47 #include "ble/att_server.h"
48 #include "btstack_util.h"
49 #include "bluetooth_gatt.h"
50 #include "btstack_debug.h"
51 
52 #include "ble/gatt-service/battery_service_v1_server.h"
53 
54 #define BAS_TASK_BATTERY_LEVEL_CHANGED                                  0x0001
55 #define BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED                           0x0002
56 #define BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED                         0x0004
57 #define BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED                         0x0008
58 #define BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED                          0x0010
59 #define BAS_TASK_BATTERY_TIME_STATUS_CHANGED                            0x0020
60 #define BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED                          0x0040
61 #define BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED                     0x0080
62 #define BAS_TASK_BATTERY_INFORMATION_CHANGED                            0x0100
63 #define BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED                       0x0200
64 #define BAS_TASK_MODEL_NUMBER_STRING_CHANGED                            0x0400
65 #define BAS_TASK_SERIAL_NUMBER_STRING_CHANGED                           0x0800
66 
67 // list of uuids
68 static const uint16_t bas_uuid16s[BAS_CHARACTERISTIC_INDEX_NUM] = {
69     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL,
70     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS,
71     ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE,
72     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS,
73     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS,
74     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS,
75     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS,
76     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION,
77     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION,
78     ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING,
79     ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING,
80     ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING,
81 };
82 
83 static const char * bas_uuid16_name[BAS_CHARACTERISTIC_INDEX_NUM] = {
84     "BATTERY_LEVEL",
85     "BATTERY_LEVEL_STATUS",
86     "ESTIMATED_SERVICE_DATE",
87     "BATTERY_CRITCAL_STATUS",
88     "BATTERY_ENERGY_STATUS",
89     "BATTERY_TIME_STATUS",
90     "BATTERY_HEALTH_STATUS",
91     "BATTERY_HEALTH_INFORMATION",
92     "BATTERY_INFORMATION",
93     "MANUFACTURER_NAME_STRING",
94     "MODEL_NUMBER_STRING",
95     "SERIAL_NUMBER_STRING",
96 };
97 
98 static uint16_t bas_service_id_counter = 0;
99 static btstack_linked_list_t battery_services;
100 
101 #define MEDFLOAT16_POSITIVE_INFINITY            0x07FE
102 #define MEDFLOAT16_NOT_A_NUMBER                 0x07FF
103 #define MEDFLOAT16_NOT_AT_THIS_RESOLUTION       0x0800
104 #define MEDFLOAT16_RFU                          0x0801
105 #define MEDFLOAT16_NEGATIVE_INFINITY            0x0802
106 
107 static bool bas_server_medfloat16_is_real_number(uint16_t value_medfloat16){
108     switch (value_medfloat16){
109         case MEDFLOAT16_POSITIVE_INFINITY:
110         case MEDFLOAT16_NOT_A_NUMBER:
111         case MEDFLOAT16_NOT_AT_THIS_RESOLUTION:
112         case MEDFLOAT16_RFU:
113         case MEDFLOAT16_NEGATIVE_INFINITY:
114             return false;
115         default:
116             return true;
117     }
118 }
119 
120 static uint16_t bas_server_get_task_for_characteristic_index(bas_characteristic_index_t index){
121     switch (index){
122         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
123             return BAS_TASK_BATTERY_LEVEL_CHANGED;
124         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
125             return BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
126         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
127             return BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
128         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
129             return BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
130         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
131             return BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
132         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
133             return BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
134         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
135             return BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
136         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
137             return BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
138         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
139             return BAS_TASK_BATTERY_INFORMATION_CHANGED;
140         case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
141             return BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED;
142         case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
143             return BAS_TASK_MODEL_NUMBER_STRING_CHANGED;
144         case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
145             return BAS_TASK_SERIAL_NUMBER_STRING_CHANGED;
146         default:
147             btstack_assert(false);
148             return 0;
149     }
150 }
151 
152 static battery_service_v1_server_connection_t * battery_service_server_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){
153     if (service == NULL){
154         return NULL;
155     }
156 
157     uint8_t i;
158     for (i = 0; i < service->connections_max_num; i++){
159         if (service->connections[i].con_handle == con_handle){
160             return &service->connections[i];
161         }
162     }
163     return NULL;
164 }
165 
166 static battery_service_v1_server_connection_t * battery_service_server_add_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){
167     if (service == NULL){
168         return NULL;
169     }
170 
171     uint8_t i;
172     for (i = 0; i < service->connections_max_num; i++){
173         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
174             service->connections[i].con_handle = con_handle;
175             service->connections[i].service = service;
176             return &service->connections[i];
177         }
178     }
179     return NULL;
180 }
181 
182 
183 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
184     btstack_linked_list_iterator_t it;
185     btstack_linked_list_iterator_init(&it, &battery_services);
186     while (btstack_linked_list_iterator_has_next(&it)){
187         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
188         if (attribute_handle < item->service_handler.start_handle) continue;
189         if (attribute_handle > item->service_handler.end_handle)   continue;
190         return item;
191     }
192     return NULL;
193 }
194 
195 
196 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){
197     btstack_linked_list_iterator_t it;
198     btstack_linked_list_iterator_init(&it, &battery_services);
199     while (btstack_linked_list_iterator_has_next(&it)){
200         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
201         uint8_t i;
202         for (i = 0; i < service->connections_max_num; i++){
203             if (service->connections[i].con_handle == con_handle){
204                 return service;
205             }
206         }
207     }
208     return NULL;
209 }
210 
211 static uint8_t bas_serialize_characteristic(battery_service_v1_t * service, bas_characteristic_index_t index, uint8_t * event, uint8_t event_size){
212     uint8_t pos = 0;
213     switch ((bas_characteristic_index_t) index){
214         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
215             event[pos++] = service->battery_level;
216             break;
217 
218         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
219             if (service->level_status == NULL){
220                 return 0;
221             }
222             event[pos++] = service->level_status->flags;
223             little_endian_store_16(event, pos, service->level_status->power_state_flags);
224             pos += 2;
225             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){
226                 little_endian_store_16(event, pos, service->level_status->identifier);
227                 pos += 2;
228             }
229             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){
230                 event[pos++] = service->level_status->battery_level;
231             }
232             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
233                 event[pos++] = service->level_status->additional_status_flags;
234             }
235             break;
236 
237         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
238             little_endian_store_24(event, pos, service->estimated_service_date_days);
239             pos += 3;
240             break;
241 
242         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
243             event[pos++] = service->critcal_status_flags;
244             break;
245 
246         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
247             if (service->energy_status == NULL){
248                 return 0;
249             }
250             event[pos++] = service->energy_status->flags;
251             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){
252                 little_endian_store_16(event, pos, service->energy_status->external_source_power_medfloat16);
253                 pos += 2;
254             }
255             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){
256                 little_endian_store_16(event, pos, service->energy_status->present_voltage_medfloat16);
257                 pos += 2;
258             }
259             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){
260                 little_endian_store_16(event, pos, service->energy_status->available_energy_medfloat16);
261                 pos += 2;
262             }
263             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){
264                 little_endian_store_16(event, pos, service->energy_status->available_battery_capacity_medfloat16);
265                 pos += 2;
266             }
267             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){
268                 little_endian_store_16(event, pos, service->energy_status->charge_rate_medfloat16);
269                 pos += 2;
270             }
271             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){
272                 little_endian_store_16(event, pos, service->energy_status->available_energy_at_last_charge_medfloat16);
273                 pos += 2;
274             }
275             break;
276 
277         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
278             if (service->time_status == NULL){
279                 return 0;
280             }
281             event[pos++] = service->time_status->flags;
282             little_endian_store_24(event, pos, service->time_status->time_until_discharged_minutes);
283             pos += 3;
284             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
285                 little_endian_store_24(event, pos, service->time_status->time_until_discharged_on_standby_minutes);
286                 pos += 3;
287             }
288             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
289                 little_endian_store_24(event, pos, service->time_status->time_until_recharged_minutes);
290                 pos += 3;
291             }
292             break;
293 
294         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
295             if (service->health_status == NULL){
296                 return 0;
297             }
298             event[pos++] = service->health_status->flags;
299             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
300                 event[pos++] = service->health_status->summary;
301             }
302             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){
303                 little_endian_store_16(event, pos, service->health_status->cycle_count);
304                 pos += 2;
305             }
306             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){
307                 event[pos++] = service->health_status->current_temperature_degree_celsius;
308             }
309             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){
310                 little_endian_store_16(event, pos, service->health_status->deep_discharge_count);
311                 pos += 2;
312             }
313             break;
314 
315         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
316             if (service->health_information == NULL){
317                 return 0;
318             }
319             event[pos++] = service->health_information->flags;
320             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){
321                 little_endian_store_16(event, pos, service->health_information->cycle_count_designed_lifetime);
322                 pos += 2;
323             }
324             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
325                 event[pos++] = service->health_information->min_designed_operating_temperature_degree_celsius;
326             }
327             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
328                 event[pos++] = service->health_information->max_designed_operating_temperature_degree_celsius;
329             }
330             break;
331 
332         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
333             if (service->information == NULL){
334                 return 0;
335             }
336             little_endian_store_16(event, pos, service->information->flags);
337             pos += 2;
338             event[pos++] = service->information->features;
339             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
340                 little_endian_store_24(event, pos, service->information->manufacture_date_days);
341                 pos += 3;
342             }
343             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
344                 little_endian_store_24(event, pos, service->information->expiration_date_days);
345                 pos += 3;
346             }
347             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
348                 little_endian_store_16(event, pos, service->information->designed_capacity_kWh_medfloat16);
349                 pos += 2;
350             }
351             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
352                 little_endian_store_16(event, pos, service->information->low_energy_kWh_medfloat16);
353                 pos += 2;
354             }
355             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
356                 little_endian_store_16(event, pos, service->information->critical_energy_kWh_medfloat16);
357                 pos += 2;
358             }
359             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
360                 event[pos++] = service->information->chemistry;
361             }
362             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
363                 little_endian_store_16(event, pos, service->information->nominal_voltage_medfloat16);
364                 pos += 2;
365             }
366             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
367                 event[pos++] = service->information->aggregation_group;
368             }
369             break;
370         default:
371             break;
372     }
373     return pos;
374 }
375 
376 static uint16_t battery_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
377     UNUSED(con_handle);
378 
379     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
380     if (service == NULL){
381         return 0;
382     }
383 
384     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
385     if (connection == NULL){
386         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
387         if (connection == NULL){
388             return 0;
389         }
390     }
391 
392     uint8_t index;
393     uint8_t event[19];
394     uint8_t pos = 0;
395 
396     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
397         if (attribute_handle != service->characteristics[index].value_handle){
398             continue;
399         }
400 
401         switch ((bas_characteristic_index_t) index){
402             case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
403                 if (service->manufacturer_name == NULL){
404                     return 0;
405                 }
406                 return att_read_callback_handle_blob((uint8_t *)service->manufacturer_name, strlen(service->manufacturer_name), offset, buffer, buffer_size);
407 
408             case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
409                 if (service->model_number == NULL){
410                     return 0;
411                 }
412                 return att_read_callback_handle_blob((uint8_t *)service->model_number, strlen(service->model_number), offset, buffer, buffer_size);
413 
414             case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
415                 if (service->serial_number == NULL){
416                     return 0;
417                 }
418                 return att_read_callback_handle_blob((uint8_t *)service->serial_number, strlen(service->serial_number), offset, buffer, buffer_size);
419 
420             default:
421                 pos = bas_serialize_characteristic(service, index, event, sizeof(event));
422                 if (pos == 1u){
423                     return att_read_callback_handle_byte(event[0], offset, buffer, buffer_size);
424                 }
425                 if (pos > 1u){
426                     return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size);
427                 }
428                 return 0;
429         }
430     }
431 
432     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
433         return att_read_callback_handle_little_endian_16(service->battery_level_status_broadcast_configuration, offset, buffer, buffer_size);
434     }
435 
436     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
437         if (attribute_handle != service->characteristics[index].client_configuration_handle){
438             continue;
439         }
440         return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size);
441     }
442     return 0;
443 }
444 
445 static int battery_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
446     UNUSED(offset);
447     UNUSED(buffer_size);
448 
449     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
450         return 0;
451     }
452 
453     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
454     if (service == NULL){
455         return 0;
456     }
457 
458     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
459     if (connection == NULL){
460         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
461         if (connection == NULL){
462             return 0;
463         }
464     }
465 
466     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
467         service->battery_level_status_broadcast_configuration = little_endian_read_16(buffer, 0);
468         return 0;
469     }
470 
471     uint8_t index;
472     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
473         if (attribute_handle != service->characteristics[index].client_configuration_handle){
474             continue;
475         }
476         connection->configurations[index] = little_endian_read_16(buffer, 0);
477         return 0;
478     }
479     return 0;
480 }
481 
482 
483 static bool bas_characteristic_notify_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
484     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) != 0u;
485 }
486 static bool bas_characteristic_indicate_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
487     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) != 0u;
488 }
489 
490 static void battery_service_can_send_now(void * context){
491     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
492     if (connection == NULL){
493         return;
494     }
495     battery_service_v1_t * service = connection->service;
496     if (service == NULL){
497         return;
498     }
499 
500     // if battery is removed, no indications or notification should be sent
501 //    if ( (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
502 //        return;
503 //    }
504 
505     bas_characteristic_index_t index;
506     uint8_t event[19];
507     uint8_t pos = 0;
508     bool task_valid = true;
509 
510 
511     if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_CHANGED) > 0u){
512         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_CHANGED;
513         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL;
514 
515     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED) > 0u){
516         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
517         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS;
518 
519     } else if ((connection->scheduled_tasks & BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED) > 0u){
520         connection->scheduled_tasks &= ~BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
521         index = BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE;
522 
523     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED) > 0u){
524         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
525         index = BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS;
526 
527     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED) > 0u){
528         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
529         index = BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS;
530 
531     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_TIME_STATUS_CHANGED) > 0u){
532         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
533         index = BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS;
534 
535     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED) > 0u){
536         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
537         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
538 
539     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED) > 0u){
540         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
541         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
542 
543     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_INFORMATION_CHANGED) > 0u){
544         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_INFORMATION_CHANGED;
545         index = BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION;
546 
547     } else {
548         // TODO  BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED
549         // TODO  BAS_TASK_MODEL_NUMBER_STRING_CHANGED
550         // TODO  BAS_TASK_SERIAL_NUMBER_STRING_CHANGED
551 
552         task_valid = false;
553     }
554 
555     if (task_valid){
556         pos = bas_serialize_characteristic(service, index, event, sizeof(event));
557 
558         if (bas_characteristic_notify_configured(connection, index)){
559             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
560         } else if (bas_characteristic_indicate_configured(connection, index)){
561             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
562         }
563     }
564 
565     if (connection->scheduled_tasks > 0u){
566         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
567     }
568 }
569 
570 void battery_service_v1_server_init(void){
571 
572 }
573 
574 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num, uint16_t * out_service_id){
575     btstack_assert(service != NULL);
576     btstack_assert(connections != NULL);
577     btstack_assert(connection_max_num > 0u);
578 
579     // get service handle range
580     uint16_t end_handle   = 0xffff;
581     uint16_t start_handle = 0;
582 
583     btstack_linked_list_iterator_t it;
584     btstack_linked_list_iterator_init(&it, &battery_services);
585     while (btstack_linked_list_iterator_has_next(&it)){
586         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
587         if (service->service_handler.end_handle > start_handle){
588             start_handle = service->service_handler.end_handle + 1;
589         }
590     }
591 
592     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
593     btstack_assert(service_found != 0);
594     UNUSED(service_found);
595 
596     // get next service id
597     bas_service_id_counter = btstack_next_cid_ignoring_zero(bas_service_id_counter);
598     service->service_id = bas_service_id_counter;
599     if (out_service_id != NULL) {
600         *out_service_id = bas_service_id_counter;
601     }
602 
603     service->service_handler.start_handle = start_handle;
604     service->service_handler.end_handle = end_handle;
605     printf("start handle 0x%04X, end0x%04X\n", service->service_handler.start_handle , service->service_handler.end_handle);
606     uint8_t i;
607     for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){
608         // get characteristic value handle and client configuration handle
609         service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
610         service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
611         printf("%30s: 0x%04X, CCC 0x%04X\n", bas_uuid16_name[i], service->characteristics[i].value_handle , service->characteristics[i].client_configuration_handle );
612     }
613 
614     service->battery_level_status_broadcast_configuration_handle = gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS);
615 
616     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
617     for (i = 0; i < connection_max_num; i++){
618         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
619     }
620     service->connections_max_num = connection_max_num;
621     service->connections = connections;
622 
623     service->service_handler.start_handle = start_handle;
624     service->service_handler.end_handle = end_handle;
625     service->service_handler.read_callback  = &battery_service_read_callback;
626     service->service_handler.write_callback = &battery_service_write_callback;
627     att_server_register_service_handler(&service->service_handler);
628 
629     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
630 
631     btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service);
632 }
633 
634 
635 static void bas_server_set_callback_for_connection(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index, uint8_t task){
636     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
637         connection->scheduled_tasks = 0;
638         return;
639     }
640 
641     uint8_t scheduled_tasks = connection->scheduled_tasks;
642     connection->scheduled_tasks |= task;
643     if (scheduled_tasks == 0){
644         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
645         connection->scheduled_tasks_callback.context  = (void*) connection;
646         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
647     }
648 }
649 
650 static void bas_server_set_callback(battery_service_v1_t * service, bas_characteristic_index_t index){
651     // if battery is removed, no indications or notification should be sent
652 
653     if (service->level_status == NULL){
654         return;
655     }
656     if ((index != BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS) && (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
657         return;
658     }
659     uint8_t task = bas_server_get_task_for_characteristic_index(index);
660     uint8_t i;
661     for (i = 0; i < service->connections_max_num; i++){
662         if (service->connections[i].configurations[index] > 0u){
663             bas_server_set_callback_for_connection(&service->connections[i], index, task);
664         }
665     }
666 }
667 
668 uint8_t battery_service_v1_server_set_battery_level(battery_service_v1_t * service, uint8_t battery_level){
669     btstack_assert(service != NULL);
670     if (battery_level > 100){
671         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
672     }
673     if (service->battery_level != battery_level){
674         service->battery_level = battery_level;
675         bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL);
676     }
677     return ERROR_CODE_SUCCESS;
678 }
679 
680 uint8_t battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service, const battery_level_status_t * battery_level_status){
681     btstack_assert(service != NULL);
682     btstack_assert(battery_level_status != NULL);
683 
684     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_RFU) != 0u){
685         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
686     }
687     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
688        if ((battery_level_status->additional_status_flags & BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU) != 0u){
689             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
690         }
691     }
692 
693     service->level_status = battery_level_status;
694     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS);
695     return ERROR_CODE_SUCCESS;
696 }
697 
698 uint8_t battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service, uint32_t estimated_service_date_days){
699     btstack_assert(service != NULL);
700 
701     if (estimated_service_date_days > 0xFFFFFF){
702         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
703     }
704 
705     service->estimated_service_date_days = estimated_service_date_days;
706     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE);
707     return ERROR_CODE_SUCCESS;
708 }
709 
710 uint8_t battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service, uint8_t critcal_status_flags){
711     btstack_assert(service != NULL);
712 
713     if ((critcal_status_flags & BATTERY_CRITCAL_STATUS_BITMASK_RFU) != 0u){
714         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
715     }
716 
717     service->critcal_status_flags = critcal_status_flags;
718     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS);
719     return ERROR_CODE_SUCCESS;
720 }
721 
722 uint8_t battery_service_v1_server_set_energy_status(battery_service_v1_t * service, const battery_energy_status_t * energy_status){
723     btstack_assert(service != NULL);
724     btstack_assert(energy_status != NULL);
725 
726     if ((energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_RFU) != 0u){
727         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
728     }
729 
730     service->energy_status = energy_status;
731     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS);
732     return ERROR_CODE_SUCCESS;
733 }
734 
735 uint8_t battery_service_v1_server_set_time_status(battery_service_v1_t * service, const battery_time_status_t * time_status){
736     btstack_assert(service != NULL);
737     btstack_assert(time_status != NULL);
738 
739     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_RFU) != 0u){
740         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
741     }
742     if (time_status->time_until_discharged_minutes > 0xFFFFFF){
743         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
744     }
745     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
746         if (time_status->time_until_discharged_on_standby_minutes > 0xFFFFFF){
747             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
748         }
749     }
750     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
751         if (time_status->time_until_recharged_minutes > 0xFFFFFF){
752             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
753         }
754     }
755 
756     service->time_status = time_status;
757     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS);
758     return ERROR_CODE_SUCCESS;
759 }
760 
761 uint8_t battery_service_v1_server_set_health_status(battery_service_v1_t * service, const battery_health_status_t * health_status){
762     btstack_assert(service != NULL);
763     btstack_assert(health_status != NULL);
764 
765     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_RFU) != 0u){
766         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
767     }
768     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
769         if (health_status->summary > 100){
770             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
771         }
772     }
773 
774     service->health_status = health_status;
775     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS);
776     return ERROR_CODE_SUCCESS;
777 }
778 
779 uint8_t battery_service_v1_server_set_health_information(battery_service_v1_t * service, const battery_health_information_t * health_information){
780     btstack_assert(service != NULL);
781     btstack_assert(health_information != NULL);
782 
783     if ((health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_RFU) != 0u){
784         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
785     }
786 
787     service->health_information = health_information;
788     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION);
789     return ERROR_CODE_SUCCESS;
790 }
791 
792 uint8_t battery_service_v1_server_set_information(battery_service_v1_t * service, const battery_information_t * information){
793     btstack_assert(service != NULL);
794     btstack_assert(information != NULL);
795 
796     if ((information->flags & BATTERY_INFORMATION_BITMASK_RFU) != 0u){
797         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
798     }
799     if ((information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
800         if (information->manufacture_date_days > 0xFFFFFF){
801             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
802         }
803     }
804     if ((information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
805         if (information->expiration_date_days > 0xFFFFFF){
806             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
807         }
808     }
809     if ((information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
810         if (information->chemistry >= BATTERY_CHEMISTRY_RFU_START && information->chemistry <= BATTERY_CHEMISTRY_RFU_END){
811             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
812         }
813     }
814     if ((information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
815         if (information->aggregation_group == 0xFF){
816             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
817         }
818     }
819     if ((information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
820         if (!bas_server_medfloat16_is_real_number(information->designed_capacity_kWh_medfloat16)){
821             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
822         }
823     }
824     if ((information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
825         if (!bas_server_medfloat16_is_real_number(information->low_energy_kWh_medfloat16)){
826             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
827         }
828     }
829     if ((information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
830         if (!bas_server_medfloat16_is_real_number(information->critical_energy_kWh_medfloat16)){
831             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
832         }
833     }
834     if ((information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
835         if (!bas_server_medfloat16_is_real_number(information->nominal_voltage_medfloat16)){
836             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
837         }
838     }
839     service->information = information;
840     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION);
841     return ERROR_CODE_SUCCESS;
842 }
843 
844 uint8_t battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service, const char * manufacturer_name){
845     btstack_assert(service != NULL);
846     btstack_assert(manufacturer_name != NULL);
847 
848     service->manufacturer_name = manufacturer_name;
849     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING);
850     return ERROR_CODE_SUCCESS;
851 }
852 
853 uint8_t battery_service_v1_server_set_model_number(battery_service_v1_t * service, const char * model_number){
854     btstack_assert(service != NULL);
855     btstack_assert(model_number != NULL);
856 
857     service->model_number = model_number;
858     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING);
859     return ERROR_CODE_SUCCESS;
860 }
861 
862 uint8_t battery_service_v1_server_set_serial_number(battery_service_v1_t * service, const char * serial_number){
863     btstack_assert(service != NULL);
864     btstack_assert(serial_number != NULL);
865 
866     service->serial_number = serial_number;
867     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING);
868     return ERROR_CODE_SUCCESS;
869 }
870 
871 void battery_service_v1_server_deregister(battery_service_v1_t *service){
872     btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service);
873     // TODO cansel can send now
874 }
875 
876 void battery_service_v1_server_deinit(void){
877     // deregister listeners
878     // empty list
879     btstack_linked_list_iterator_t it;
880     btstack_linked_list_iterator_init(&it, &battery_services);
881     while (btstack_linked_list_iterator_has_next(&it)){
882         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
883         battery_service_v1_server_deregister(service);
884     }
885 }
886