xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision de4a918b3a254c2924e3af4dfdc8639dc39ea4c5)
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 
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 static btstack_linked_list_t battery_services;
83 
84 static uint16_t bas_server_get_task_for_characteristic_index(bas_characteristic_index_t index){
85     switch (index){
86         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
87             return BAS_TASK_BATTERY_LEVEL_CHANGED;
88         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
89             return BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
90         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
91             return BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
92         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
93             return BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
94         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
95             return BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
96         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
97             return BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
98         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
99             return BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
100         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
101             return BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
102         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
103             return BAS_TASK_BATTERY_INFORMATION_CHANGED;
104         case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
105             return BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED;
106         case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
107             return BAS_TASK_MODEL_NUMBER_STRING_CHANGED;
108         case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
109             return BAS_TASK_SERIAL_NUMBER_STRING_CHANGED;
110         default:
111             btstack_assert(false);
112     }
113 }
114 
115 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){
116     if (service == NULL){
117         return NULL;
118     }
119 
120     uint8_t i;
121     for (i = 0; i < service->connections_max_num; i++){
122         if (service->connections[i].con_handle == con_handle){
123             return &service->connections[i];
124         }
125     }
126     return NULL;
127 }
128 
129 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){
130     if (service == NULL){
131         return NULL;
132     }
133 
134     uint8_t i;
135     for (i = 0; i < service->connections_max_num; i++){
136         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
137             service->connections[i].con_handle = con_handle;
138             service->connections[i].service = service;
139             return &service->connections[i];
140         }
141     }
142     return NULL;
143 }
144 
145 
146 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
147     btstack_linked_list_iterator_t it;
148     btstack_linked_list_iterator_init(&it, &battery_services);
149     while (btstack_linked_list_iterator_has_next(&it)){
150         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
151         if (attribute_handle < item->start_handle) continue;
152         if (attribute_handle > item->end_handle)   continue;
153         return item;
154     }
155     return NULL;
156 }
157 
158 
159 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){
160     btstack_linked_list_iterator_t it;
161     btstack_linked_list_iterator_init(&it, &battery_services);
162     while (btstack_linked_list_iterator_has_next(&it)){
163         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
164         uint8_t i;
165         for (i = 0; i < service->connections_max_num; i++){
166             if (service->connections[i].con_handle == con_handle){
167                 return service;
168             }
169         }
170     }
171     return NULL;
172 }
173 
174 static uint8_t bas_serialize_characteristic(battery_service_v1_t * service, bas_characteristic_index_t index, uint8_t * event, uint8_t event_size){
175     uint8_t pos = 0;
176     switch ((bas_characteristic_index_t) index){
177         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
178             event[pos++] = service->battery_level;
179             break;
180 
181         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
182             if (service->level_status == NULL){
183                 return 0;
184             }
185             event[pos++] = service->level_status->flags;
186             little_endian_store_16(event, pos, service->level_status->power_state_flags);
187             pos += 2;
188             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){
189                 little_endian_store_16(event, pos, service->level_status->identifier);
190                 pos += 2;
191             }
192             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){
193                 event[pos++] = service->level_status->battery_level;
194             }
195             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
196                 event[pos++] = service->level_status->additional_status_flags;
197             }
198             break;
199 
200         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
201             little_endian_store_24(event, pos, service->estimated_service_date_days);
202             pos += 3;
203             break;
204 
205         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
206             event[pos++] = service->critcal_status_flags;
207             break;
208 
209         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
210             if (service->energy_status == NULL){
211                 return 0;
212             }
213             event[pos++] = service->energy_status->flags;
214             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){
215                 little_endian_store_16(event, pos, service->energy_status->external_source_power_medfloat16);
216                 pos += 2;
217             }
218             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){
219                 little_endian_store_16(event, pos, service->energy_status->present_voltage_medfloat16);
220                 pos += 2;
221             }
222             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){
223                 little_endian_store_16(event, pos, service->energy_status->available_energy_medfloat16);
224                 pos += 2;
225             }
226             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){
227                 little_endian_store_16(event, pos, service->energy_status->available_battery_capacity_medfloat16);
228                 pos += 2;
229             }
230             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){
231                 little_endian_store_16(event, pos, service->energy_status->charge_rate_medfloat16);
232                 pos += 2;
233             }
234             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){
235                 little_endian_store_16(event, pos, service->energy_status->available_energy_at_last_charge_medfloat16);
236                 pos += 2;
237             }
238             break;
239 
240         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
241             if (service->time_status == NULL){
242                 return 0;
243             }
244             event[pos++] = service->time_status->flags;
245             little_endian_store_24(event, pos, service->time_status->time_until_discharged_minutes);
246             pos += 3;
247             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
248                 little_endian_store_24(event, pos, service->time_status->time_until_discharged_on_standby_minutes);
249                 pos += 3;
250             }
251             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
252                 little_endian_store_24(event, pos, service->time_status->time_until_recharged_minutes);
253                 pos += 3;
254             }
255             break;
256 
257         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
258             if (service->health_status == NULL){
259                 return 0;
260             }
261             event[pos++] = service->health_status->flags;
262             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
263                 event[pos++] = service->health_status->summary;
264             }
265             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){
266                 little_endian_store_16(event, pos, service->health_status->cycle_count);
267                 pos += 2;
268             }
269             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){
270                 event[pos++] = service->health_status->current_temperature_degree_celsius;
271             }
272             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){
273                 little_endian_store_16(event, pos, service->health_status->deep_discharge_count);
274                 pos += 2;
275             }
276             break;
277 
278         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
279             if (service->health_information == NULL){
280                 return 0;
281             }
282             event[pos++] = service->health_information->flags;
283             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){
284                 little_endian_store_16(event, pos, service->health_information->cycle_count_designed_lifetime);
285                 pos += 2;
286             }
287             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
288                 event[pos++] = service->health_information->min_designed_operating_temperature_degree_celsius;
289             }
290             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
291                 event[pos++] = service->health_information->max_designed_operating_temperature_degree_celsius;
292             }
293             break;
294 
295         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
296             if (service->information == NULL){
297                 return 0;
298             }
299             little_endian_store_16(event, pos, service->information->flags);
300             pos += 2;
301             event[pos++] = service->information->features;
302             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
303                 little_endian_store_24(event, pos, service->information->manufacture_date_days);
304                 pos += 3;
305             }
306             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
307                 little_endian_store_24(event, pos, service->information->expiration_date_days);
308                 pos += 3;
309             }
310             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
311                 little_endian_store_16(event, pos, service->information->designed_capacity_kWh_medfloat16);
312                 pos += 2;
313             }
314             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
315                 little_endian_store_16(event, pos, service->information->low_energy_kWh_medfloat16);
316                 pos += 2;
317             }
318             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
319                 little_endian_store_16(event, pos, service->information->critical_energy_kWh_medfloat16);
320                 pos += 2;
321             }
322             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
323                 event[pos++] = service->information->chemistry;
324             }
325             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
326                 little_endian_store_16(event, pos, service->information->nominal_voltage_medfloat16);
327                 pos += 2;
328             }
329             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
330                 event[pos++] = service->information->aggregation_group;
331             }
332             break;
333         default:
334             break;
335     }
336     return pos;
337 }
338 
339 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){
340     UNUSED(con_handle);
341 
342     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
343     if (service == NULL){
344         return 0;
345     }
346 
347     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
348     if (connection == NULL){
349         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
350         if (connection == NULL){
351             return 0;
352         }
353     }
354 
355     uint8_t index;
356     uint8_t event[18];
357     uint8_t pos = 0;
358 
359     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
360         if (attribute_handle != service->characteristics[index].value_handle){
361             continue;
362         }
363 
364         switch ((bas_characteristic_index_t) index){
365             case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
366                 if (service->manufacturer_name == NULL){
367                     return 0;
368                 }
369                 return att_read_callback_handle_blob((uint8_t *)service->manufacturer_name, strlen(service->manufacturer_name), offset, buffer, buffer_size);
370 
371             case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
372                 if (service->model_number == NULL){
373                     return 0;
374                 }
375                 return att_read_callback_handle_blob((uint8_t *)service->model_number, strlen(service->model_number), offset, buffer, buffer_size);
376 
377             case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
378                 if (service->serial_number == NULL){
379                     return 0;
380                 }
381                 return att_read_callback_handle_blob((uint8_t *)service->serial_number, strlen(service->serial_number), offset, buffer, buffer_size);
382 
383             default:
384                 pos = bas_serialize_characteristic(service, index, event, sizeof(event));
385                 if (pos == 1u){
386                     return att_read_callback_handle_byte(event[0], offset, buffer, buffer_size);
387                 }
388                 if (pos > 1u){
389                     return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size);
390                 }
391                 return 0;
392         }
393     }
394 
395     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
396         if (attribute_handle != service->characteristics[index].client_configuration_handle){
397             continue;
398         }
399         return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size);
400     }
401     return 0;
402 }
403 
404 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){
405     UNUSED(offset);
406     UNUSED(buffer_size);
407 
408     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
409         return 0;
410     }
411 
412     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
413     if (service == NULL){
414         return 0;
415     }
416 
417     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
418     if (connection == NULL){
419         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
420         if (connection == NULL){
421             return 0;
422         }
423     }
424 
425     uint8_t index;
426     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
427         if (attribute_handle != service->characteristics[index].client_configuration_handle){
428             continue;
429         }
430         connection->configurations[index] = little_endian_read_16(buffer, 0);
431     }
432     return 0;
433 }
434 
435 
436 static bool bas_characteristic_notify_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
437     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) != 0u;
438 }
439 static bool bas_characteristic_indicate_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
440     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) != 0u;
441 }
442 
443 static void battery_service_can_send_now(void * context){
444     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
445     if (connection == NULL){
446         return;
447     }
448     battery_service_v1_t * service = connection->service;
449     if (service == NULL){
450         return;
451     }
452 
453     bas_characteristic_index_t index;
454     uint8_t event[18];
455     uint8_t pos = 0;
456     bool task_valid = true;
457 
458 
459     if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_CHANGED) > 0u){
460         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_CHANGED;
461         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL;
462 
463     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED) > 0u){
464         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
465         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS;
466 
467     } else if ((connection->scheduled_tasks & BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED) > 0u){
468         connection->scheduled_tasks &= ~BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
469         index = BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE;
470 
471     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED) > 0u){
472         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
473         index = BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS;
474 
475     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED) > 0u){
476         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
477         index = BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS;
478 
479     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_TIME_STATUS_CHANGED) > 0u){
480         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
481         index = BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS;
482 
483     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED) > 0u){
484         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
485         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
486 
487     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED) > 0u){
488         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
489         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
490 
491     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_INFORMATION_CHANGED) > 0u){
492         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_INFORMATION_CHANGED;
493         index = BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION;
494 
495     } else {
496         // TODO  BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED
497         // TODO  BAS_TASK_MODEL_NUMBER_STRING_CHANGED
498         // TODO  BAS_TASK_SERIAL_NUMBER_STRING_CHANGED
499 
500         task_valid = false;
501     }
502 
503     if (task_valid){
504         pos = bas_serialize_characteristic(service, index, event, sizeof(event));
505 
506         if (bas_characteristic_notify_configured(connection, index)){
507             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
508         } else if (bas_characteristic_indicate_configured(connection, index)){
509             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
510         }
511     }
512 
513     if (connection->scheduled_tasks > 0u){
514         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
515     }
516 }
517 
518 void battery_service_v1_server_init(void){
519 
520 }
521 
522 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num){
523     btstack_assert(service != NULL);
524     btstack_assert(connections != NULL);
525     btstack_assert(connection_max_num > 0u);
526 
527     // get service handle range
528     uint16_t end_handle   = 0xffff;
529     uint16_t start_handle = 0;
530 
531     btstack_linked_list_iterator_t it;
532     btstack_linked_list_iterator_init(&it, &battery_services);
533     while (btstack_linked_list_iterator_has_next(&it)){
534         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
535         if (service->end_handle > start_handle){
536             start_handle = service->end_handle + 1;
537         }
538     }
539 
540     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
541     btstack_assert(service_found != 0);
542     UNUSED(service_found);
543 
544     service->start_handle = start_handle;
545     service->end_handle   = end_handle;
546 
547     uint8_t i;
548     for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){
549         // get characteristic value handle and client configuration handle
550         service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
551         service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
552     }
553 
554     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
555     for (i = 0; i < connection_max_num; i++){
556         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
557     }
558     service->connections_max_num = connection_max_num;
559     service->connections = connections;
560 
561     service->service_handler.read_callback  = &battery_service_read_callback;
562     service->service_handler.write_callback = &battery_service_write_callback;
563     att_server_register_service_handler(&service->service_handler);
564 
565     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
566 
567     btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service);
568 }
569 
570 
571 static void bas_server_set_callback_for_connection(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index, uint8_t task){
572     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
573         connection->scheduled_tasks = 0;
574         return;
575     }
576 
577     uint8_t scheduled_tasks = connection->scheduled_tasks;
578     connection->scheduled_tasks |= task;
579     if (scheduled_tasks == 0){
580         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
581         connection->scheduled_tasks_callback.context  = (void*) connection;
582         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
583     }
584 }
585 
586 static void bas_server_set_callback(battery_service_v1_t * service, bas_characteristic_index_t index){
587     uint8_t task = bas_server_get_task_for_characteristic_index(index);
588     uint8_t i;
589     for (i = 0; i < service->connections_max_num; i++){
590         if (service->connections[i].configurations[index] > 0u){
591             bas_server_set_callback_for_connection(&service->connections[i], index, task);
592         }
593     }
594 }
595 
596 uint8_t battery_service_v1_server_set_battery_level(battery_service_v1_t * service, uint8_t battery_level){
597     btstack_assert(service != NULL);
598     if (battery_level > 100){
599         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
600     }
601     if (service->battery_level != battery_level){
602         service->battery_level = battery_level;
603         bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL);
604     }
605     return ERROR_CODE_SUCCESS;
606 }
607 
608 uint8_t battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service, const battery_level_status_t * battery_level_status){
609     btstack_assert(service != NULL);
610     btstack_assert(battery_level_status != NULL);
611 
612     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_RFU) != 0u){
613         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
614     }
615     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
616        if ((battery_level_status->additional_status_flags & BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU) != 0u){
617             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
618         }
619     }
620 
621     service->level_status = battery_level_status;
622     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS);
623     return ERROR_CODE_SUCCESS;
624 }
625 
626 uint8_t battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service, uint32_t estimated_service_date_days){
627     btstack_assert(service != NULL);
628 
629     if (estimated_service_date_days > 0xFFFFFF){
630         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
631     }
632 
633     service->estimated_service_date_days = estimated_service_date_days;
634     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE);
635     return ERROR_CODE_SUCCESS;
636 }
637 
638 uint8_t battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service, uint8_t critcal_status_flags){
639     btstack_assert(service != NULL);
640 
641     if ((critcal_status_flags & BATTERY_CRITCAL_STATUS_BITMASK_RFU) != 0u){
642         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
643     }
644 
645     service->critcal_status_flags = critcal_status_flags;
646     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS);
647     return ERROR_CODE_SUCCESS;
648 }
649 
650 uint8_t battery_service_v1_server_set_energy_status(battery_service_v1_t * service, const battery_energy_status_t * energy_status){
651     btstack_assert(service != NULL);
652     btstack_assert(energy_status != NULL);
653 
654     if ((energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_RFU) != 0u){
655         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
656     }
657 
658     service->energy_status = energy_status;
659     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS);
660     return ERROR_CODE_SUCCESS;
661 }
662 
663 uint8_t battery_service_v1_server_set_time_status(battery_service_v1_t * service, const battery_time_status_t * time_status){
664     btstack_assert(service != NULL);
665     btstack_assert(time_status != NULL);
666 
667     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_RFU) != 0u){
668         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
669     }
670     if (time_status->time_until_discharged_minutes > 0xFFFFFF){
671         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
672     }
673     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
674         if (time_status->time_until_discharged_on_standby_minutes > 0xFFFFFF){
675             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
676         }
677     }
678     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
679         if (time_status->time_until_recharged_minutes > 0xFFFFFF){
680             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
681         }
682     }
683 
684     service->time_status = time_status;
685     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS);
686     return ERROR_CODE_SUCCESS;
687 }
688 
689 uint8_t battery_service_v1_server_set_health_status(battery_service_v1_t * service, const battery_health_status_t * health_status){
690     btstack_assert(service != NULL);
691     btstack_assert(health_status != NULL);
692 
693     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_RFU) != 0u){
694         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
695     }
696     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
697         if (health_status->summary > 100){
698             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
699         }
700     }
701 
702     service->health_status = health_status;
703     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS);
704     return ERROR_CODE_SUCCESS;
705 }
706 
707 uint8_t battery_service_v1_server_set_health_information(battery_service_v1_t * service, const battery_health_information_t * health_information){
708     btstack_assert(service != NULL);
709     btstack_assert(health_information != NULL);
710 
711     if ((health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_RFU) != 0u){
712         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
713     }
714 
715     service->health_information = health_information;
716     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION);
717     return ERROR_CODE_SUCCESS;
718 }
719 
720 uint8_t battery_service_v1_server_set_information(battery_service_v1_t * service, const battery_information_t * information){
721     btstack_assert(service != NULL);
722     btstack_assert(information != NULL);
723 
724     if ((information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_RFU) != 0u){
725         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
726     }
727     if ((information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
728         if (information->manufacture_date_days > 0xFFFFFF){
729             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
730         }
731     }
732     if ((information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
733         if (information->expiration_date_days > 0xFFFFFF){
734             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
735         }
736     }
737     if ((information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
738         if (information->chemistry >= BATTERY_CHEMISTRY_RFU_START || information->chemistry <= BATTERY_CHEMISTRY_RFU_END){
739             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
740         }
741     }
742     if ((information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
743         if (information->aggregation_group == 0xFF){
744             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
745         }
746     }
747 
748     service->information = information;
749     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION);
750     return ERROR_CODE_SUCCESS;
751 }
752 
753 uint8_t battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service, const char * manufacturer_name){
754     btstack_assert(service != NULL);
755     btstack_assert(manufacturer_name != NULL);
756 
757     service->manufacturer_name = manufacturer_name;
758     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING);
759     return ERROR_CODE_SUCCESS;
760 }
761 
762 uint8_t battery_service_v1_server_set_model_number(battery_service_v1_t * service, const char * model_number){
763     btstack_assert(service != NULL);
764     btstack_assert(model_number != NULL);
765 
766     service->model_number = model_number;
767     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING);
768     return ERROR_CODE_SUCCESS;
769 }
770 
771 uint8_t battery_service_v1_server_set_serial_number(battery_service_v1_t * service, const char * serial_number){
772     btstack_assert(service != NULL);
773     btstack_assert(serial_number != NULL);
774 
775     service->serial_number = serial_number;
776     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING);
777     return ERROR_CODE_SUCCESS;
778 }
779 
780 void battery_service_v1_server_deregister(battery_service_v1_t *service){
781     btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service);
782     // TODO cansel can send now
783 }
784 
785 void battery_service_v1_server_deinit(void){
786     // deregister listeners
787     // empty list
788     btstack_linked_list_iterator_t it;
789     btstack_linked_list_iterator_init(&it, &battery_services);
790     while (btstack_linked_list_iterator_has_next(&it)){
791         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
792         battery_service_v1_server_deregister(service);
793     }
794 }
795