xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision 7f5d8e5f11e4f050a2628944569bacd15f3dbd6a)
1d62aa1c5SMilanka Ringwald /*
2d62aa1c5SMilanka Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3d62aa1c5SMilanka Ringwald  *
4d62aa1c5SMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5d62aa1c5SMilanka Ringwald  * modification, are permitted provided that the following conditions
6d62aa1c5SMilanka Ringwald  * are met:
7d62aa1c5SMilanka Ringwald  *
8d62aa1c5SMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9d62aa1c5SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10d62aa1c5SMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11d62aa1c5SMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12d62aa1c5SMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13d62aa1c5SMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14d62aa1c5SMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15d62aa1c5SMilanka Ringwald  *    from this software without specific prior written permission.
16d62aa1c5SMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17d62aa1c5SMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18d62aa1c5SMilanka Ringwald  *    monetary gain.
19d62aa1c5SMilanka Ringwald  *
20d62aa1c5SMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21d62aa1c5SMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22d62aa1c5SMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23d62aa1c5SMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24d62aa1c5SMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25d62aa1c5SMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26d62aa1c5SMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27d62aa1c5SMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28d62aa1c5SMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29d62aa1c5SMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30d62aa1c5SMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31d62aa1c5SMilanka Ringwald  * SUCH DAMAGE.
32d62aa1c5SMilanka Ringwald  *
33d62aa1c5SMilanka Ringwald  * Please inquire about commercial licensing options at
34d62aa1c5SMilanka Ringwald  * [email protected]
35d62aa1c5SMilanka Ringwald  *
36d62aa1c5SMilanka Ringwald  */
37d62aa1c5SMilanka Ringwald 
38d62aa1c5SMilanka Ringwald #define BTSTACK_FILE__ "battery_service_v1_server.c"
39d62aa1c5SMilanka Ringwald 
40d62aa1c5SMilanka Ringwald /**
41d62aa1c5SMilanka Ringwald  * Implementation of the GATT Battery Service Server
42d62aa1c5SMilanka Ringwald  * To use with your application, add `#import <battery_service.gatt>` to your .gatt file
43d62aa1c5SMilanka Ringwald  */
44164aeebbSMilanka Ringwald #include <stdio.h>
45d62aa1c5SMilanka Ringwald #include "btstack_defines.h"
46d62aa1c5SMilanka Ringwald #include "ble/att_db.h"
47d62aa1c5SMilanka Ringwald #include "ble/att_server.h"
48d62aa1c5SMilanka Ringwald #include "btstack_util.h"
49d62aa1c5SMilanka Ringwald #include "bluetooth_gatt.h"
50d62aa1c5SMilanka Ringwald #include "btstack_debug.h"
51d62aa1c5SMilanka Ringwald 
52d62aa1c5SMilanka Ringwald #include "ble/gatt-service/battery_service_v1_server.h"
53feeb8459SMatthias Ringwald #include "hci_event_builder.h"
546a9e015fSMatthias Ringwald #include "bluetooth_data_types.h"
55d62aa1c5SMilanka Ringwald 
56e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_LEVEL_CHANGED                                  0x0001
57e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED                           0x0002
58e16068dbSMilanka Ringwald #define BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED                         0x0004
59e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED                         0x0008
60e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED                          0x0010
61e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_TIME_STATUS_CHANGED                            0x0020
62e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED                          0x0040
63e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED                     0x0080
64e16068dbSMilanka Ringwald #define BAS_TASK_BATTERY_INFORMATION_CHANGED                            0x0100
65e16068dbSMilanka Ringwald #define BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED                       0x0200
66e16068dbSMilanka Ringwald #define BAS_TASK_MODEL_NUMBER_STRING_CHANGED                            0x0400
67e16068dbSMilanka Ringwald #define BAS_TASK_SERIAL_NUMBER_STRING_CHANGED                           0x0800
68d62aa1c5SMilanka Ringwald 
69e5ae30daSMilanka Ringwald // list of uuids
70e5ae30daSMilanka Ringwald static const uint16_t bas_uuid16s[BAS_CHARACTERISTIC_INDEX_NUM] = {
71e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL,
72e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS,
73e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE,
74*7f5d8e5fSMatthias Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITICAL_STATUS,
75e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS,
76e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS,
77e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS,
78e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION,
79e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION,
80e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING,
81e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING,
82e5ae30daSMilanka Ringwald     ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING,
83e5ae30daSMilanka Ringwald };
84164aeebbSMilanka Ringwald 
85164aeebbSMilanka Ringwald static const char * bas_uuid16_name[BAS_CHARACTERISTIC_INDEX_NUM] = {
86164aeebbSMilanka Ringwald     "BATTERY_LEVEL",
87164aeebbSMilanka Ringwald     "BATTERY_LEVEL_STATUS",
88164aeebbSMilanka Ringwald     "ESTIMATED_SERVICE_DATE",
89164aeebbSMilanka Ringwald     "BATTERY_CRITCAL_STATUS",
90164aeebbSMilanka Ringwald     "BATTERY_ENERGY_STATUS",
91164aeebbSMilanka Ringwald     "BATTERY_TIME_STATUS",
92164aeebbSMilanka Ringwald     "BATTERY_HEALTH_STATUS",
93164aeebbSMilanka Ringwald     "BATTERY_HEALTH_INFORMATION",
94164aeebbSMilanka Ringwald     "BATTERY_INFORMATION",
95164aeebbSMilanka Ringwald     "MANUFACTURER_NAME_STRING",
96164aeebbSMilanka Ringwald     "MODEL_NUMBER_STRING",
97164aeebbSMilanka Ringwald     "SERIAL_NUMBER_STRING",
98164aeebbSMilanka Ringwald };
99164aeebbSMilanka Ringwald 
100f368eb91SMatthias Ringwald static uint16_t bas_service_id_counter = 0;
101d62aa1c5SMilanka Ringwald static btstack_linked_list_t battery_services;
102feeb8459SMatthias Ringwald static btstack_packet_handler_t battery_service_app_callback;
103d62aa1c5SMilanka Ringwald 
104164aeebbSMilanka Ringwald #define MEDFLOAT16_POSITIVE_INFINITY            0x07FE
105164aeebbSMilanka Ringwald #define MEDFLOAT16_NOT_A_NUMBER                 0x07FF
106164aeebbSMilanka Ringwald #define MEDFLOAT16_NOT_AT_THIS_RESOLUTION       0x0800
107164aeebbSMilanka Ringwald #define MEDFLOAT16_RFU                          0x0801
108164aeebbSMilanka Ringwald #define MEDFLOAT16_NEGATIVE_INFINITY            0x0802
109164aeebbSMilanka Ringwald 
bas_server_medfloat16_is_real_number(uint16_t value_medfloat16)110164aeebbSMilanka Ringwald static bool bas_server_medfloat16_is_real_number(uint16_t value_medfloat16){
111164aeebbSMilanka Ringwald     switch (value_medfloat16){
112164aeebbSMilanka Ringwald         case MEDFLOAT16_POSITIVE_INFINITY:
113164aeebbSMilanka Ringwald         case MEDFLOAT16_NOT_A_NUMBER:
114164aeebbSMilanka Ringwald         case MEDFLOAT16_NOT_AT_THIS_RESOLUTION:
115164aeebbSMilanka Ringwald         case MEDFLOAT16_RFU:
116164aeebbSMilanka Ringwald         case MEDFLOAT16_NEGATIVE_INFINITY:
117164aeebbSMilanka Ringwald             return false;
118164aeebbSMilanka Ringwald         default:
119164aeebbSMilanka Ringwald             return true;
120164aeebbSMilanka Ringwald     }
121164aeebbSMilanka Ringwald }
122164aeebbSMilanka Ringwald 
bas_server_get_task_for_characteristic_index(bas_characteristic_index_t index)123e16068dbSMilanka Ringwald static uint16_t bas_server_get_task_for_characteristic_index(bas_characteristic_index_t index){
124e16068dbSMilanka Ringwald     switch (index){
125e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
126e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_LEVEL_CHANGED;
127e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
128e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
129e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
130e16068dbSMilanka Ringwald             return BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
131e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
132e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
133e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
134e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
135e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
136e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
137e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
138e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
139e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
140e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
141e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
142e16068dbSMilanka Ringwald             return BAS_TASK_BATTERY_INFORMATION_CHANGED;
143e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
144e16068dbSMilanka Ringwald             return BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED;
145e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
146e16068dbSMilanka Ringwald             return BAS_TASK_MODEL_NUMBER_STRING_CHANGED;
147e16068dbSMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
148e16068dbSMilanka Ringwald             return BAS_TASK_SERIAL_NUMBER_STRING_CHANGED;
149e16068dbSMilanka Ringwald         default:
150e16068dbSMilanka Ringwald             btstack_assert(false);
151164aeebbSMilanka Ringwald             return 0;
152e16068dbSMilanka Ringwald     }
153e16068dbSMilanka Ringwald }
154d62aa1c5SMilanka Ringwald 
battery_service_server_connection_for_con_handle(battery_service_v1_t * service,hci_con_handle_t con_handle)155d62aa1c5SMilanka Ringwald 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){
156d62aa1c5SMilanka Ringwald     if (service == NULL){
157d62aa1c5SMilanka Ringwald         return NULL;
158d62aa1c5SMilanka Ringwald     }
159d62aa1c5SMilanka Ringwald 
160d62aa1c5SMilanka Ringwald     uint8_t i;
161d62aa1c5SMilanka Ringwald     for (i = 0; i < service->connections_max_num; i++){
162d62aa1c5SMilanka Ringwald         if (service->connections[i].con_handle == con_handle){
163d62aa1c5SMilanka Ringwald             return &service->connections[i];
164d62aa1c5SMilanka Ringwald         }
165d62aa1c5SMilanka Ringwald     }
166d62aa1c5SMilanka Ringwald     return NULL;
167d62aa1c5SMilanka Ringwald }
168d62aa1c5SMilanka Ringwald 
battery_service_server_add_connection_for_con_handle(battery_service_v1_t * service,hci_con_handle_t con_handle)169d62aa1c5SMilanka Ringwald 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){
170d62aa1c5SMilanka Ringwald     if (service == NULL){
171d62aa1c5SMilanka Ringwald         return NULL;
172d62aa1c5SMilanka Ringwald     }
173d62aa1c5SMilanka Ringwald 
174d62aa1c5SMilanka Ringwald     uint8_t i;
175d62aa1c5SMilanka Ringwald     for (i = 0; i < service->connections_max_num; i++){
176d62aa1c5SMilanka Ringwald         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
177d62aa1c5SMilanka Ringwald             service->connections[i].con_handle = con_handle;
178d62aa1c5SMilanka Ringwald             service->connections[i].service = service;
179d62aa1c5SMilanka Ringwald             return &service->connections[i];
180d62aa1c5SMilanka Ringwald         }
181d62aa1c5SMilanka Ringwald     }
182d62aa1c5SMilanka Ringwald     return NULL;
183d62aa1c5SMilanka Ringwald }
184d62aa1c5SMilanka Ringwald 
185d62aa1c5SMilanka Ringwald 
battery_service_service_for_attribute_handle(uint16_t attribute_handle)186d62aa1c5SMilanka Ringwald static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
187d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_t it;
188d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &battery_services);
189d62aa1c5SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
190d62aa1c5SMilanka Ringwald         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
191164aeebbSMilanka Ringwald         if (attribute_handle < item->service_handler.start_handle) continue;
192164aeebbSMilanka Ringwald         if (attribute_handle > item->service_handler.end_handle)   continue;
193d62aa1c5SMilanka Ringwald         return item;
194d62aa1c5SMilanka Ringwald     }
195d62aa1c5SMilanka Ringwald     return NULL;
196d62aa1c5SMilanka Ringwald }
197d62aa1c5SMilanka Ringwald 
198d62aa1c5SMilanka Ringwald 
bas_serialize_characteristic(battery_service_v1_t * service,bas_characteristic_index_t index,uint8_t * buffer,uint8_t buffer_size)1996a9e015fSMatthias Ringwald static uint8_t bas_serialize_characteristic(battery_service_v1_t * service, bas_characteristic_index_t index, uint8_t * buffer, uint8_t buffer_size){
200133170f2SDirk Helbig     UNUSED(buffer_size);
201db9fdd68SMilanka Ringwald     uint8_t pos = 0;
202db9fdd68SMilanka Ringwald     switch ((bas_characteristic_index_t) index){
203db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
2046a9e015fSMatthias Ringwald             buffer[pos++] = service->battery_level;
205db9fdd68SMilanka Ringwald             break;
206db9fdd68SMilanka Ringwald 
207db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
208db9fdd68SMilanka Ringwald             if (service->level_status == NULL){
209db9fdd68SMilanka Ringwald                 return 0;
210db9fdd68SMilanka Ringwald             }
2116a9e015fSMatthias Ringwald             buffer[pos++] = service->level_status->flags;
2126a9e015fSMatthias Ringwald             little_endian_store_16(buffer, pos, service->level_status->power_state_flags);
213db9fdd68SMilanka Ringwald             pos += 2;
214db9fdd68SMilanka Ringwald             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){
2156a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->level_status->identifier);
216db9fdd68SMilanka Ringwald                 pos += 2;
217db9fdd68SMilanka Ringwald             }
218db9fdd68SMilanka Ringwald             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){
2196a9e015fSMatthias Ringwald                 buffer[pos++] = service->level_status->battery_level;
220db9fdd68SMilanka Ringwald             }
221db9fdd68SMilanka Ringwald             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
2226a9e015fSMatthias Ringwald                 buffer[pos++] = service->level_status->additional_status_flags;
223db9fdd68SMilanka Ringwald             }
224db9fdd68SMilanka Ringwald             break;
225db9fdd68SMilanka Ringwald 
226db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
2276a9e015fSMatthias Ringwald             little_endian_store_24(buffer, pos, service->estimated_service_date_days);
228db9fdd68SMilanka Ringwald             pos += 3;
229db9fdd68SMilanka Ringwald             break;
230db9fdd68SMilanka Ringwald 
231db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
2326a9e015fSMatthias Ringwald             buffer[pos++] = service->critical_status_flags;
233db9fdd68SMilanka Ringwald             break;
234db9fdd68SMilanka Ringwald 
235db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
236db9fdd68SMilanka Ringwald             if (service->energy_status == NULL){
237db9fdd68SMilanka Ringwald                 return 0;
238db9fdd68SMilanka Ringwald             }
2396a9e015fSMatthias Ringwald             buffer[pos++] = service->energy_status->flags;
240db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){
2416a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->external_source_power_medfloat16);
242db9fdd68SMilanka Ringwald                 pos += 2;
243db9fdd68SMilanka Ringwald             }
244db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){
2456a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->present_voltage_medfloat16);
246db9fdd68SMilanka Ringwald                 pos += 2;
247db9fdd68SMilanka Ringwald             }
248db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){
2496a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->available_energy_medfloat16);
250db9fdd68SMilanka Ringwald                 pos += 2;
251db9fdd68SMilanka Ringwald             }
252db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){
2536a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->available_battery_capacity_medfloat16);
254db9fdd68SMilanka Ringwald                 pos += 2;
255db9fdd68SMilanka Ringwald             }
256db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){
2576a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->charge_rate_medfloat16);
258db9fdd68SMilanka Ringwald                 pos += 2;
259db9fdd68SMilanka Ringwald             }
260db9fdd68SMilanka Ringwald             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){
2616a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->energy_status->available_energy_at_last_charge_medfloat16);
262db9fdd68SMilanka Ringwald                 pos += 2;
263db9fdd68SMilanka Ringwald             }
264db9fdd68SMilanka Ringwald             break;
265db9fdd68SMilanka Ringwald 
266db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
267db9fdd68SMilanka Ringwald             if (service->time_status == NULL){
268db9fdd68SMilanka Ringwald                 return 0;
269db9fdd68SMilanka Ringwald             }
2706a9e015fSMatthias Ringwald             buffer[pos++] = service->time_status->flags;
2716a9e015fSMatthias Ringwald             little_endian_store_24(buffer, pos, service->time_status->time_until_discharged_minutes);
272db9fdd68SMilanka Ringwald             pos += 3;
273db9fdd68SMilanka Ringwald             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
2746a9e015fSMatthias Ringwald                 little_endian_store_24(buffer, pos, service->time_status->time_until_discharged_on_standby_minutes);
275db9fdd68SMilanka Ringwald                 pos += 3;
276db9fdd68SMilanka Ringwald             }
277db9fdd68SMilanka Ringwald             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
2786a9e015fSMatthias Ringwald                 little_endian_store_24(buffer, pos, service->time_status->time_until_recharged_minutes);
279db9fdd68SMilanka Ringwald                 pos += 3;
280db9fdd68SMilanka Ringwald             }
281db9fdd68SMilanka Ringwald             break;
282db9fdd68SMilanka Ringwald 
283db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
284db9fdd68SMilanka Ringwald             if (service->health_status == NULL){
285db9fdd68SMilanka Ringwald                 return 0;
286db9fdd68SMilanka Ringwald             }
2876a9e015fSMatthias Ringwald             buffer[pos++] = service->health_status->flags;
288db9fdd68SMilanka Ringwald             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
2896a9e015fSMatthias Ringwald                 buffer[pos++] = service->health_status->summary;
290db9fdd68SMilanka Ringwald             }
291db9fdd68SMilanka Ringwald             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){
2926a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->health_status->cycle_count);
293db9fdd68SMilanka Ringwald                 pos += 2;
294db9fdd68SMilanka Ringwald             }
295db9fdd68SMilanka Ringwald             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){
2966a9e015fSMatthias Ringwald                 buffer[pos++] = service->health_status->current_temperature_degree_celsius;
297db9fdd68SMilanka Ringwald             }
298db9fdd68SMilanka Ringwald             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){
2996a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->health_status->deep_discharge_count);
300db9fdd68SMilanka Ringwald                 pos += 2;
301db9fdd68SMilanka Ringwald             }
302db9fdd68SMilanka Ringwald             break;
303db9fdd68SMilanka Ringwald 
304db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
305db9fdd68SMilanka Ringwald             if (service->health_information == NULL){
306db9fdd68SMilanka Ringwald                 return 0;
307db9fdd68SMilanka Ringwald             }
3086a9e015fSMatthias Ringwald             buffer[pos++] = service->health_information->flags;
309db9fdd68SMilanka Ringwald             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){
3106a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->health_information->cycle_count_designed_lifetime);
311db9fdd68SMilanka Ringwald                 pos += 2;
312db9fdd68SMilanka Ringwald             }
313db9fdd68SMilanka Ringwald             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
3146a9e015fSMatthias Ringwald                 buffer[pos++] = service->health_information->min_designed_operating_temperature_degree_celsius;
315db9fdd68SMilanka Ringwald             }
316db9fdd68SMilanka Ringwald             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
3176a9e015fSMatthias Ringwald                 buffer[pos++] = service->health_information->max_designed_operating_temperature_degree_celsius;
318db9fdd68SMilanka Ringwald             }
319db9fdd68SMilanka Ringwald             break;
320db9fdd68SMilanka Ringwald 
321db9fdd68SMilanka Ringwald         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
322db9fdd68SMilanka Ringwald             if (service->information == NULL){
323db9fdd68SMilanka Ringwald                 return 0;
324db9fdd68SMilanka Ringwald             }
3256a9e015fSMatthias Ringwald             little_endian_store_16(buffer, pos, service->information->flags);
326db9fdd68SMilanka Ringwald             pos += 2;
3276a9e015fSMatthias Ringwald             buffer[pos++] = service->information->features;
328db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
3296a9e015fSMatthias Ringwald                 little_endian_store_24(buffer, pos, service->information->manufacture_date_days);
330db9fdd68SMilanka Ringwald                 pos += 3;
331db9fdd68SMilanka Ringwald             }
332db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
3336a9e015fSMatthias Ringwald                 little_endian_store_24(buffer, pos, service->information->expiration_date_days);
334db9fdd68SMilanka Ringwald                 pos += 3;
335db9fdd68SMilanka Ringwald             }
336db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
3376a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->information->designed_capacity_kWh_medfloat16);
338db9fdd68SMilanka Ringwald                 pos += 2;
339db9fdd68SMilanka Ringwald             }
340db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
3416a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->information->low_energy_kWh_medfloat16);
342db9fdd68SMilanka Ringwald                 pos += 2;
343db9fdd68SMilanka Ringwald             }
344db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
3456a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->information->critical_energy_kWh_medfloat16);
346db9fdd68SMilanka Ringwald                 pos += 2;
347db9fdd68SMilanka Ringwald             }
348db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
3496a9e015fSMatthias Ringwald                 buffer[pos++] = service->information->chemistry;
350db9fdd68SMilanka Ringwald             }
351db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
3526a9e015fSMatthias Ringwald                 little_endian_store_16(buffer, pos, service->information->nominal_voltage_medfloat16);
353db9fdd68SMilanka Ringwald                 pos += 2;
354db9fdd68SMilanka Ringwald             }
355db9fdd68SMilanka Ringwald             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
3566a9e015fSMatthias Ringwald                 buffer[pos++] = service->information->aggregation_group;
357db9fdd68SMilanka Ringwald             }
358db9fdd68SMilanka Ringwald             break;
359db9fdd68SMilanka Ringwald         default:
360db9fdd68SMilanka Ringwald             break;
361db9fdd68SMilanka Ringwald     }
362db9fdd68SMilanka Ringwald     return pos;
363db9fdd68SMilanka Ringwald }
364d62aa1c5SMilanka Ringwald 
battery_service_read_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)365d62aa1c5SMilanka Ringwald 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){
366d62aa1c5SMilanka Ringwald     UNUSED(con_handle);
367d62aa1c5SMilanka Ringwald 
368d62aa1c5SMilanka Ringwald     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
369d62aa1c5SMilanka Ringwald     if (service == NULL){
370d62aa1c5SMilanka Ringwald         return 0;
371d62aa1c5SMilanka Ringwald     }
372d62aa1c5SMilanka Ringwald 
373d62aa1c5SMilanka Ringwald     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
374d62aa1c5SMilanka Ringwald     if (connection == NULL){
375d62aa1c5SMilanka Ringwald         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
376d62aa1c5SMilanka Ringwald         if (connection == NULL){
377d62aa1c5SMilanka Ringwald             return 0;
378d62aa1c5SMilanka Ringwald         }
379d62aa1c5SMilanka Ringwald     }
380d62aa1c5SMilanka Ringwald 
381e5ae30daSMilanka Ringwald     uint8_t index;
382164aeebbSMilanka Ringwald     uint8_t event[19];
383e5ae30daSMilanka Ringwald     uint8_t pos = 0;
384e5ae30daSMilanka Ringwald 
385e5ae30daSMilanka Ringwald     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
386e5ae30daSMilanka Ringwald         if (attribute_handle != service->characteristics[index].value_handle){
387e5ae30daSMilanka Ringwald             continue;
388d62aa1c5SMilanka Ringwald         }
389e5ae30daSMilanka Ringwald 
390e5ae30daSMilanka Ringwald         switch ((bas_characteristic_index_t) index){
391e5ae30daSMilanka Ringwald             case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
392db9fdd68SMilanka Ringwald                 if (service->manufacturer_name == NULL){
393db9fdd68SMilanka Ringwald                     return 0;
394db9fdd68SMilanka Ringwald                 }
395db9fdd68SMilanka Ringwald                 return att_read_callback_handle_blob((uint8_t *)service->manufacturer_name, strlen(service->manufacturer_name), offset, buffer, buffer_size);
396e5ae30daSMilanka Ringwald 
397e5ae30daSMilanka Ringwald             case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
398db9fdd68SMilanka Ringwald                 if (service->model_number == NULL){
399db9fdd68SMilanka Ringwald                     return 0;
400db9fdd68SMilanka Ringwald                 }
401db9fdd68SMilanka Ringwald                 return att_read_callback_handle_blob((uint8_t *)service->model_number, strlen(service->model_number), offset, buffer, buffer_size);
402e5ae30daSMilanka Ringwald 
403e5ae30daSMilanka Ringwald             case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
404db9fdd68SMilanka Ringwald                 if (service->serial_number == NULL){
405db9fdd68SMilanka Ringwald                     return 0;
406db9fdd68SMilanka Ringwald                 }
407db9fdd68SMilanka Ringwald                 return att_read_callback_handle_blob((uint8_t *)service->serial_number, strlen(service->serial_number), offset, buffer, buffer_size);
408e5ae30daSMilanka Ringwald 
409e5ae30daSMilanka Ringwald             default:
410db9fdd68SMilanka Ringwald                 pos = bas_serialize_characteristic(service, index, event, sizeof(event));
411db9fdd68SMilanka Ringwald                 if (pos == 1u){
412db9fdd68SMilanka Ringwald                     return att_read_callback_handle_byte(event[0], offset, buffer, buffer_size);
413db9fdd68SMilanka Ringwald                 }
414db9fdd68SMilanka Ringwald                 if (pos > 1u){
415db9fdd68SMilanka Ringwald                     return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size);
416db9fdd68SMilanka Ringwald                 }
417e5ae30daSMilanka Ringwald                 return 0;
418e5ae30daSMilanka Ringwald         }
419e5ae30daSMilanka Ringwald     }
420e5ae30daSMilanka Ringwald 
421164aeebbSMilanka Ringwald     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
422164aeebbSMilanka Ringwald         return att_read_callback_handle_little_endian_16(service->battery_level_status_broadcast_configuration, offset, buffer, buffer_size);
423164aeebbSMilanka Ringwald     }
424164aeebbSMilanka Ringwald 
425e5ae30daSMilanka Ringwald     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
426e5ae30daSMilanka Ringwald         if (attribute_handle != service->characteristics[index].client_configuration_handle){
427e5ae30daSMilanka Ringwald             continue;
428e5ae30daSMilanka Ringwald         }
429e5ae30daSMilanka Ringwald         return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size);
430d62aa1c5SMilanka Ringwald     }
431d62aa1c5SMilanka Ringwald     return 0;
432d62aa1c5SMilanka Ringwald }
433d62aa1c5SMilanka Ringwald 
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)434d62aa1c5SMilanka Ringwald 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){
435d62aa1c5SMilanka Ringwald     UNUSED(offset);
436d62aa1c5SMilanka Ringwald     UNUSED(buffer_size);
437d62aa1c5SMilanka Ringwald 
438d62aa1c5SMilanka Ringwald     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
439d62aa1c5SMilanka Ringwald         return 0;
440d62aa1c5SMilanka Ringwald     }
441d62aa1c5SMilanka Ringwald 
442d62aa1c5SMilanka Ringwald     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
443d62aa1c5SMilanka Ringwald     if (service == NULL){
444d62aa1c5SMilanka Ringwald         return 0;
445d62aa1c5SMilanka Ringwald     }
446d62aa1c5SMilanka Ringwald 
447d62aa1c5SMilanka Ringwald     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
448d62aa1c5SMilanka Ringwald     if (connection == NULL){
449d62aa1c5SMilanka Ringwald         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
450d62aa1c5SMilanka Ringwald         if (connection == NULL){
451d62aa1c5SMilanka Ringwald             return 0;
452d62aa1c5SMilanka Ringwald         }
453d62aa1c5SMilanka Ringwald     }
454d62aa1c5SMilanka Ringwald 
455164aeebbSMilanka Ringwald     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
456feeb8459SMatthias Ringwald         uint8_t new_value = little_endian_read_16(buffer, 0);
457feeb8459SMatthias Ringwald         bool broadcast_old = (service->battery_level_status_broadcast_configuration & 1) != 0;
458feeb8459SMatthias Ringwald         bool broadcast_new = (new_value & 1) != 0;
4596a9e015fSMatthias Ringwald         service->battery_level_status_broadcast_configuration = new_value;
460feeb8459SMatthias Ringwald         if (broadcast_old != broadcast_new){
461feeb8459SMatthias Ringwald             // emit broadcast start/stop based on value of broadcast_new
462feeb8459SMatthias Ringwald             uint8_t event[5];
463feeb8459SMatthias Ringwald             hci_event_builder_context_t context;
464feeb8459SMatthias Ringwald             uint8_t subevent_type = broadcast_new ? GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL_BROADCAST_START : GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL_BROADCAST_STOP;
465feeb8459SMatthias Ringwald             hci_event_builder_init(&context, event, sizeof(buffer), HCI_EVENT_GATTSERVICE_META, subevent_type);
466feeb8459SMatthias Ringwald             hci_event_builder_add_16(&context, service->service_id);
467feeb8459SMatthias Ringwald             if (battery_service_app_callback != NULL){
468feeb8459SMatthias Ringwald                 (*battery_service_app_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
469feeb8459SMatthias Ringwald             }
470feeb8459SMatthias Ringwald         }
471164aeebbSMilanka Ringwald         return 0;
472164aeebbSMilanka Ringwald     }
473164aeebbSMilanka Ringwald 
474e5ae30daSMilanka Ringwald     uint8_t index;
475e5ae30daSMilanka Ringwald     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
476e5ae30daSMilanka Ringwald         if (attribute_handle != service->characteristics[index].client_configuration_handle){
477e5ae30daSMilanka Ringwald             continue;
478e5ae30daSMilanka Ringwald         }
479e5ae30daSMilanka Ringwald         connection->configurations[index] = little_endian_read_16(buffer, 0);
480164aeebbSMilanka Ringwald         return 0;
481d62aa1c5SMilanka Ringwald     }
482d62aa1c5SMilanka Ringwald     return 0;
483d62aa1c5SMilanka Ringwald }
484d62aa1c5SMilanka Ringwald 
485e16068dbSMilanka Ringwald 
bas_characteristic_notify_configured(battery_service_v1_server_connection_t * connection,bas_characteristic_index_t index)486e16068dbSMilanka Ringwald static bool bas_characteristic_notify_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
487e16068dbSMilanka Ringwald     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) != 0u;
488e16068dbSMilanka Ringwald }
bas_characteristic_indicate_configured(battery_service_v1_server_connection_t * connection,bas_characteristic_index_t index)489e16068dbSMilanka Ringwald static bool bas_characteristic_indicate_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
490e16068dbSMilanka Ringwald     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) != 0u;
491e16068dbSMilanka Ringwald }
492e16068dbSMilanka Ringwald 
battery_service_can_send_now(void * context)493d62aa1c5SMilanka Ringwald static void battery_service_can_send_now(void * context){
494d62aa1c5SMilanka Ringwald     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
495d62aa1c5SMilanka Ringwald     if (connection == NULL){
496d62aa1c5SMilanka Ringwald         return;
497d62aa1c5SMilanka Ringwald     }
498d62aa1c5SMilanka Ringwald     battery_service_v1_t * service = connection->service;
499d62aa1c5SMilanka Ringwald     if (service == NULL){
500d62aa1c5SMilanka Ringwald         return;
501d62aa1c5SMilanka Ringwald     }
502d62aa1c5SMilanka Ringwald 
503164aeebbSMilanka Ringwald     // if battery is removed, no indications or notification should be sent
504164aeebbSMilanka Ringwald //    if ( (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
505164aeebbSMilanka Ringwald //        return;
506164aeebbSMilanka Ringwald //    }
507164aeebbSMilanka Ringwald 
508e16068dbSMilanka Ringwald     bas_characteristic_index_t index;
509164aeebbSMilanka Ringwald     uint8_t event[19];
510e16068dbSMilanka Ringwald     uint8_t pos = 0;
511e16068dbSMilanka Ringwald     bool task_valid = true;
512e16068dbSMilanka Ringwald 
513e16068dbSMilanka Ringwald 
514e16068dbSMilanka Ringwald     if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_CHANGED) > 0u){
515e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_CHANGED;
516e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL;
517e16068dbSMilanka Ringwald 
518e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED) > 0u){
519e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
520e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS;
521e16068dbSMilanka Ringwald 
522e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED) > 0u){
523e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
524e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE;
525e16068dbSMilanka Ringwald 
526e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED) > 0u){
527e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
528e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS;
529e16068dbSMilanka Ringwald 
530e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED) > 0u){
531e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
532e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS;
533e16068dbSMilanka Ringwald 
534e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_TIME_STATUS_CHANGED) > 0u){
535e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
536e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS;
537e16068dbSMilanka Ringwald 
538e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED) > 0u){
539e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
540e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
541e16068dbSMilanka Ringwald 
542e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED) > 0u){
543e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
544e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
545e16068dbSMilanka Ringwald 
546e16068dbSMilanka Ringwald     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_INFORMATION_CHANGED) > 0u){
547e16068dbSMilanka Ringwald         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_INFORMATION_CHANGED;
548e16068dbSMilanka Ringwald         index = BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION;
549e16068dbSMilanka Ringwald 
550e16068dbSMilanka Ringwald     } else {
551e16068dbSMilanka Ringwald         // TODO  BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED
552e16068dbSMilanka Ringwald         // TODO  BAS_TASK_MODEL_NUMBER_STRING_CHANGED
553e16068dbSMilanka Ringwald         // TODO  BAS_TASK_SERIAL_NUMBER_STRING_CHANGED
554e16068dbSMilanka Ringwald 
555e16068dbSMilanka Ringwald         task_valid = false;
556e16068dbSMilanka Ringwald     }
557e16068dbSMilanka Ringwald 
558e16068dbSMilanka Ringwald     if (task_valid){
559e16068dbSMilanka Ringwald         pos = bas_serialize_characteristic(service, index, event, sizeof(event));
560e16068dbSMilanka Ringwald 
561e16068dbSMilanka Ringwald         if (bas_characteristic_notify_configured(connection, index)){
562e16068dbSMilanka Ringwald             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
563e16068dbSMilanka Ringwald         } else if (bas_characteristic_indicate_configured(connection, index)){
564e16068dbSMilanka Ringwald             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
565e16068dbSMilanka Ringwald         }
566d62aa1c5SMilanka Ringwald     }
567d62aa1c5SMilanka Ringwald 
568d62aa1c5SMilanka Ringwald     if (connection->scheduled_tasks > 0u){
569d62aa1c5SMilanka Ringwald         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
570d62aa1c5SMilanka Ringwald     }
571d62aa1c5SMilanka Ringwald }
572d62aa1c5SMilanka Ringwald 
battery_service_v1_server_init(void)573d62aa1c5SMilanka Ringwald void battery_service_v1_server_init(void){
574d62aa1c5SMilanka Ringwald 
575d62aa1c5SMilanka Ringwald }
576d62aa1c5SMilanka Ringwald 
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)577f368eb91SMatthias Ringwald 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){
578d62aa1c5SMilanka Ringwald     btstack_assert(service != NULL);
579d62aa1c5SMilanka Ringwald     btstack_assert(connections != NULL);
580d62aa1c5SMilanka Ringwald     btstack_assert(connection_max_num > 0u);
581d62aa1c5SMilanka Ringwald 
582d62aa1c5SMilanka Ringwald     // get service handle range
583d62aa1c5SMilanka Ringwald     uint16_t end_handle   = 0xffff;
584d62aa1c5SMilanka Ringwald     uint16_t start_handle = 0;
585d62aa1c5SMilanka Ringwald 
586d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_t it;
587d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &battery_services);
588d62aa1c5SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
589d62aa1c5SMilanka Ringwald         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
590164aeebbSMilanka Ringwald         if (service->service_handler.end_handle > start_handle){
591164aeebbSMilanka Ringwald             start_handle = service->service_handler.end_handle + 1;
592d62aa1c5SMilanka Ringwald         }
593d62aa1c5SMilanka Ringwald     }
594d62aa1c5SMilanka Ringwald 
595d62aa1c5SMilanka Ringwald     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
596d62aa1c5SMilanka Ringwald     btstack_assert(service_found != 0);
597d62aa1c5SMilanka Ringwald     UNUSED(service_found);
598d62aa1c5SMilanka Ringwald 
599f849ede7SMatthias Ringwald     // get next service id
600f368eb91SMatthias Ringwald     bas_service_id_counter = btstack_next_cid_ignoring_zero(bas_service_id_counter);
601f368eb91SMatthias Ringwald     service->service_id = bas_service_id_counter;
602f368eb91SMatthias Ringwald     if (out_service_id != NULL) {
603f368eb91SMatthias Ringwald         *out_service_id = bas_service_id_counter;
604f368eb91SMatthias Ringwald     }
605f849ede7SMatthias Ringwald 
606164aeebbSMilanka Ringwald     service->service_handler.start_handle = start_handle;
607164aeebbSMilanka Ringwald     service->service_handler.end_handle = end_handle;
608164aeebbSMilanka Ringwald     printf("start handle 0x%04X, end0x%04X\n", service->service_handler.start_handle , service->service_handler.end_handle);
609e5ae30daSMilanka Ringwald     uint8_t i;
610e5ae30daSMilanka Ringwald     for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){
611d62aa1c5SMilanka Ringwald         // get characteristic value handle and client configuration handle
612e5ae30daSMilanka Ringwald         service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
613e5ae30daSMilanka Ringwald         service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
614164aeebbSMilanka Ringwald         printf("%30s: 0x%04X, CCC 0x%04X\n", bas_uuid16_name[i], service->characteristics[i].value_handle , service->characteristics[i].client_configuration_handle );
615e5ae30daSMilanka Ringwald     }
616d62aa1c5SMilanka Ringwald 
617164aeebbSMilanka Ringwald     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);
618164aeebbSMilanka Ringwald 
619d62aa1c5SMilanka Ringwald     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
620d62aa1c5SMilanka Ringwald     for (i = 0; i < connection_max_num; i++){
621d62aa1c5SMilanka Ringwald         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
622d62aa1c5SMilanka Ringwald     }
623d62aa1c5SMilanka Ringwald     service->connections_max_num = connection_max_num;
624d62aa1c5SMilanka Ringwald     service->connections = connections;
625d62aa1c5SMilanka Ringwald 
626164aeebbSMilanka Ringwald     service->service_handler.start_handle = start_handle;
627164aeebbSMilanka Ringwald     service->service_handler.end_handle = end_handle;
628d62aa1c5SMilanka Ringwald     service->service_handler.read_callback  = &battery_service_read_callback;
629d62aa1c5SMilanka Ringwald     service->service_handler.write_callback = &battery_service_write_callback;
630d62aa1c5SMilanka Ringwald     att_server_register_service_handler(&service->service_handler);
631d62aa1c5SMilanka Ringwald 
632d62aa1c5SMilanka Ringwald     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
633d62aa1c5SMilanka Ringwald 
6346a9e015fSMatthias Ringwald     btstack_linked_list_add_tail(&battery_services, (btstack_linked_item_t *) service);
635d62aa1c5SMilanka Ringwald }
636d62aa1c5SMilanka Ringwald 
637d62aa1c5SMilanka Ringwald 
bas_server_set_callback_for_connection(battery_service_v1_server_connection_t * connection,bas_characteristic_index_t index,uint8_t task)638e16068dbSMilanka Ringwald static void bas_server_set_callback_for_connection(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index, uint8_t task){
639133170f2SDirk Helbig     UNUSED(index);
640d62aa1c5SMilanka Ringwald     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
641d62aa1c5SMilanka Ringwald         connection->scheduled_tasks = 0;
642d62aa1c5SMilanka Ringwald         return;
643d62aa1c5SMilanka Ringwald     }
644d62aa1c5SMilanka Ringwald 
645d62aa1c5SMilanka Ringwald     uint8_t scheduled_tasks = connection->scheduled_tasks;
646d62aa1c5SMilanka Ringwald     connection->scheduled_tasks |= task;
647d62aa1c5SMilanka Ringwald     if (scheduled_tasks == 0){
648d62aa1c5SMilanka Ringwald         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
649d62aa1c5SMilanka Ringwald         connection->scheduled_tasks_callback.context  = (void*) connection;
650d62aa1c5SMilanka Ringwald         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
651d62aa1c5SMilanka Ringwald     }
652d62aa1c5SMilanka Ringwald }
653d62aa1c5SMilanka Ringwald 
bas_server_set_callback(battery_service_v1_t * service,bas_characteristic_index_t index)654e16068dbSMilanka Ringwald static void bas_server_set_callback(battery_service_v1_t * service, bas_characteristic_index_t index){
655164aeebbSMilanka Ringwald     // if battery is removed, no indications or notification should be sent
656164aeebbSMilanka Ringwald 
657164aeebbSMilanka Ringwald     if (service->level_status == NULL){
658164aeebbSMilanka Ringwald         return;
659164aeebbSMilanka Ringwald     }
660164aeebbSMilanka Ringwald     if ((index != BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS) && (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
661164aeebbSMilanka Ringwald         return;
662164aeebbSMilanka Ringwald     }
663e16068dbSMilanka Ringwald     uint8_t task = bas_server_get_task_for_characteristic_index(index);
664d62aa1c5SMilanka Ringwald     uint8_t i;
665d62aa1c5SMilanka Ringwald     for (i = 0; i < service->connections_max_num; i++){
666e16068dbSMilanka Ringwald         if (service->connections[i].configurations[index] > 0u){
667e16068dbSMilanka Ringwald             bas_server_set_callback_for_connection(&service->connections[i], index, task);
668d62aa1c5SMilanka Ringwald         }
669d62aa1c5SMilanka Ringwald     }
670e16068dbSMilanka Ringwald }
671e16068dbSMilanka Ringwald 
battery_service_v1_server_set_battery_level(battery_service_v1_t * service,uint8_t battery_level)672e16068dbSMilanka Ringwald uint8_t battery_service_v1_server_set_battery_level(battery_service_v1_t * service, uint8_t battery_level){
673e16068dbSMilanka Ringwald     btstack_assert(service != NULL);
674e16068dbSMilanka Ringwald     if (battery_level > 100){
675e16068dbSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
676e16068dbSMilanka Ringwald     }
677e16068dbSMilanka Ringwald     if (service->battery_level != battery_level){
678e16068dbSMilanka Ringwald         service->battery_level = battery_level;
679e16068dbSMilanka Ringwald         bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL);
680e16068dbSMilanka Ringwald     }
681ac8c27c4SMilanka Ringwald     return ERROR_CODE_SUCCESS;
682d62aa1c5SMilanka Ringwald }
683d62aa1c5SMilanka Ringwald 
battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service,const battery_level_status_t * battery_level_status)684e16068dbSMilanka Ringwald uint8_t battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service, const battery_level_status_t * battery_level_status){
685e16068dbSMilanka Ringwald     btstack_assert(service != NULL);
686e16068dbSMilanka Ringwald     btstack_assert(battery_level_status != NULL);
687e16068dbSMilanka Ringwald 
688de4a918bSMilanka Ringwald     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_RFU) != 0u){
689e16068dbSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
690e16068dbSMilanka Ringwald     }
691e16068dbSMilanka Ringwald     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
692de4a918bSMilanka Ringwald        if ((battery_level_status->additional_status_flags & BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU) != 0u){
693e16068dbSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
694e16068dbSMilanka Ringwald         }
695e16068dbSMilanka Ringwald     }
696e16068dbSMilanka Ringwald 
697e16068dbSMilanka Ringwald     service->level_status = battery_level_status;
698e16068dbSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS);
699e16068dbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
700e16068dbSMilanka Ringwald }
701e16068dbSMilanka Ringwald 
battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service,uint32_t estimated_service_date_days)702e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service, uint32_t estimated_service_date_days){
703e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
704e24f316cSMilanka Ringwald 
705de4a918bSMilanka Ringwald     if (estimated_service_date_days > 0xFFFFFF){
706de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
707de4a918bSMilanka Ringwald     }
708de4a918bSMilanka Ringwald 
709e24f316cSMilanka Ringwald     service->estimated_service_date_days = estimated_service_date_days;
710e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE);
711e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
712e24f316cSMilanka Ringwald }
713e24f316cSMilanka Ringwald 
battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service,uint8_t critcal_status_flags)714e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service, uint8_t critcal_status_flags){
715e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
716e24f316cSMilanka Ringwald 
717de4a918bSMilanka Ringwald     if ((critcal_status_flags & BATTERY_CRITCAL_STATUS_BITMASK_RFU) != 0u){
718de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
719de4a918bSMilanka Ringwald     }
720de4a918bSMilanka Ringwald 
7216a9e015fSMatthias Ringwald     service->critical_status_flags = critcal_status_flags;
722e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS);
723e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
724e24f316cSMilanka Ringwald }
725e24f316cSMilanka Ringwald 
battery_service_v1_server_set_energy_status(battery_service_v1_t * service,const battery_energy_status_t * energy_status)726e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_energy_status(battery_service_v1_t * service, const battery_energy_status_t * energy_status){
727e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
728e24f316cSMilanka Ringwald     btstack_assert(energy_status != NULL);
729e24f316cSMilanka Ringwald 
730de4a918bSMilanka Ringwald     if ((energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_RFU) != 0u){
731de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
732de4a918bSMilanka Ringwald     }
733de4a918bSMilanka Ringwald 
734e24f316cSMilanka Ringwald     service->energy_status = energy_status;
735e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS);
736e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
737e24f316cSMilanka Ringwald }
738e24f316cSMilanka Ringwald 
battery_service_v1_server_set_time_status(battery_service_v1_t * service,const battery_time_status_t * time_status)739e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_time_status(battery_service_v1_t * service, const battery_time_status_t * time_status){
740e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
741e24f316cSMilanka Ringwald     btstack_assert(time_status != NULL);
742e24f316cSMilanka Ringwald 
743de4a918bSMilanka Ringwald     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_RFU) != 0u){
744de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
745de4a918bSMilanka Ringwald     }
746de4a918bSMilanka Ringwald     if (time_status->time_until_discharged_minutes > 0xFFFFFF){
747de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
748de4a918bSMilanka Ringwald     }
749de4a918bSMilanka Ringwald     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
750de4a918bSMilanka Ringwald         if (time_status->time_until_discharged_on_standby_minutes > 0xFFFFFF){
751de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
752de4a918bSMilanka Ringwald         }
753de4a918bSMilanka Ringwald     }
754de4a918bSMilanka Ringwald     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
755de4a918bSMilanka Ringwald         if (time_status->time_until_recharged_minutes > 0xFFFFFF){
756de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
757de4a918bSMilanka Ringwald         }
758de4a918bSMilanka Ringwald     }
759de4a918bSMilanka Ringwald 
760e24f316cSMilanka Ringwald     service->time_status = time_status;
761e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS);
762e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
763e24f316cSMilanka Ringwald }
764e24f316cSMilanka Ringwald 
battery_service_v1_server_set_health_status(battery_service_v1_t * service,const battery_health_status_t * health_status)765e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_health_status(battery_service_v1_t * service, const battery_health_status_t * health_status){
766e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
767e24f316cSMilanka Ringwald     btstack_assert(health_status != NULL);
768e24f316cSMilanka Ringwald 
769de4a918bSMilanka Ringwald     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_RFU) != 0u){
770de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
771de4a918bSMilanka Ringwald     }
772de4a918bSMilanka Ringwald     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
773de4a918bSMilanka Ringwald         if (health_status->summary > 100){
774de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
775de4a918bSMilanka Ringwald         }
776de4a918bSMilanka Ringwald     }
777de4a918bSMilanka Ringwald 
778e24f316cSMilanka Ringwald     service->health_status = health_status;
779e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS);
780e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
781e24f316cSMilanka Ringwald }
782e24f316cSMilanka Ringwald 
battery_service_v1_server_set_health_information(battery_service_v1_t * service,const battery_health_information_t * health_information)783e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_health_information(battery_service_v1_t * service, const battery_health_information_t * health_information){
784e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
785e24f316cSMilanka Ringwald     btstack_assert(health_information != NULL);
786e24f316cSMilanka Ringwald 
787de4a918bSMilanka Ringwald     if ((health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_RFU) != 0u){
788de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
789de4a918bSMilanka Ringwald     }
790de4a918bSMilanka Ringwald 
791e24f316cSMilanka Ringwald     service->health_information = health_information;
792e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION);
793e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
794e24f316cSMilanka Ringwald }
795e24f316cSMilanka Ringwald 
battery_service_v1_server_set_information(battery_service_v1_t * service,const battery_information_t * information)796e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_information(battery_service_v1_t * service, const battery_information_t * information){
797e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
798e24f316cSMilanka Ringwald     btstack_assert(information != NULL);
799e24f316cSMilanka Ringwald 
800164aeebbSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_RFU) != 0u){
801de4a918bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
802de4a918bSMilanka Ringwald     }
803de4a918bSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
804de4a918bSMilanka Ringwald         if (information->manufacture_date_days > 0xFFFFFF){
805de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
806de4a918bSMilanka Ringwald         }
807de4a918bSMilanka Ringwald     }
808de4a918bSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
809de4a918bSMilanka Ringwald         if (information->expiration_date_days > 0xFFFFFF){
810de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
811de4a918bSMilanka Ringwald         }
812de4a918bSMilanka Ringwald     }
813de4a918bSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
814164aeebbSMilanka Ringwald         if (information->chemistry >= BATTERY_CHEMISTRY_RFU_START && information->chemistry <= BATTERY_CHEMISTRY_RFU_END){
815de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
816de4a918bSMilanka Ringwald         }
817de4a918bSMilanka Ringwald     }
818de4a918bSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
819de4a918bSMilanka Ringwald         if (information->aggregation_group == 0xFF){
820de4a918bSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
821de4a918bSMilanka Ringwald         }
822de4a918bSMilanka Ringwald     }
823164aeebbSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
824164aeebbSMilanka Ringwald         if (!bas_server_medfloat16_is_real_number(information->designed_capacity_kWh_medfloat16)){
825164aeebbSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
826164aeebbSMilanka Ringwald         }
827164aeebbSMilanka Ringwald     }
828164aeebbSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
829164aeebbSMilanka Ringwald         if (!bas_server_medfloat16_is_real_number(information->low_energy_kWh_medfloat16)){
830164aeebbSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
831164aeebbSMilanka Ringwald         }
832164aeebbSMilanka Ringwald     }
833164aeebbSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
834164aeebbSMilanka Ringwald         if (!bas_server_medfloat16_is_real_number(information->critical_energy_kWh_medfloat16)){
835164aeebbSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
836164aeebbSMilanka Ringwald         }
837164aeebbSMilanka Ringwald     }
838164aeebbSMilanka Ringwald     if ((information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
839164aeebbSMilanka Ringwald         if (!bas_server_medfloat16_is_real_number(information->nominal_voltage_medfloat16)){
840164aeebbSMilanka Ringwald             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
841164aeebbSMilanka Ringwald         }
842164aeebbSMilanka Ringwald     }
843e24f316cSMilanka Ringwald     service->information = information;
844e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION);
845e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
846e24f316cSMilanka Ringwald }
847e24f316cSMilanka Ringwald 
battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service,const char * manufacturer_name)848e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service, const char * manufacturer_name){
849e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
850e24f316cSMilanka Ringwald     btstack_assert(manufacturer_name != NULL);
851e24f316cSMilanka Ringwald 
852e24f316cSMilanka Ringwald     service->manufacturer_name = manufacturer_name;
853e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING);
854e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
855e24f316cSMilanka Ringwald }
856e24f316cSMilanka Ringwald 
battery_service_v1_server_set_model_number(battery_service_v1_t * service,const char * model_number)857e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_model_number(battery_service_v1_t * service, const char * model_number){
858e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
859e24f316cSMilanka Ringwald     btstack_assert(model_number != NULL);
860e24f316cSMilanka Ringwald 
861e24f316cSMilanka Ringwald     service->model_number = model_number;
862e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING);
863e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
864e24f316cSMilanka Ringwald }
865e24f316cSMilanka Ringwald 
battery_service_v1_server_set_serial_number(battery_service_v1_t * service,const char * serial_number)866e24f316cSMilanka Ringwald uint8_t battery_service_v1_server_set_serial_number(battery_service_v1_t * service, const char * serial_number){
867e24f316cSMilanka Ringwald     btstack_assert(service != NULL);
868e24f316cSMilanka Ringwald     btstack_assert(serial_number != NULL);
869e24f316cSMilanka Ringwald 
870e24f316cSMilanka Ringwald     service->serial_number = serial_number;
871e24f316cSMilanka Ringwald     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING);
872e24f316cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
873e24f316cSMilanka Ringwald }
874e16068dbSMilanka Ringwald 
battery_service_v1_server_set_packet_handler(btstack_packet_handler_t callback)875feeb8459SMatthias Ringwald void battery_service_v1_server_set_packet_handler(btstack_packet_handler_t callback){
876feeb8459SMatthias Ringwald     btstack_assert(callback != NULL);
877feeb8459SMatthias Ringwald     battery_service_app_callback = callback;
878feeb8459SMatthias Ringwald }
879feeb8459SMatthias Ringwald 
battery_service_v1_server_deregister(battery_service_v1_t * service)880d62aa1c5SMilanka Ringwald void battery_service_v1_server_deregister(battery_service_v1_t *service){
881e5ae30daSMilanka Ringwald     btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service);
882d62aa1c5SMilanka Ringwald     // TODO cansel can send now
883d62aa1c5SMilanka Ringwald }
884d62aa1c5SMilanka Ringwald 
battery_service_v1_server_deinit(void)885d62aa1c5SMilanka Ringwald void battery_service_v1_server_deinit(void){
886d62aa1c5SMilanka Ringwald     // deregister listeners
887d62aa1c5SMilanka Ringwald     // empty list
888d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_t it;
889d62aa1c5SMilanka Ringwald     btstack_linked_list_iterator_init(&it, &battery_services);
890d62aa1c5SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
891d62aa1c5SMilanka Ringwald         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
892d62aa1c5SMilanka Ringwald         battery_service_v1_server_deregister(service);
893d62aa1c5SMilanka Ringwald     }
894d62aa1c5SMilanka Ringwald }
8956a9e015fSMatthias Ringwald 
battery_service_v1_server_get_broadcast_advertisement(uint16_t adv_interval,uint8_t * adv_buffer,uint16_t adv_size)8966a9e015fSMatthias Ringwald uint16_t battery_service_v1_server_get_broadcast_advertisement(uint16_t adv_interval, uint8_t * adv_buffer, uint16_t adv_size){
8976a9e015fSMatthias Ringwald 
8986a9e015fSMatthias Ringwald     if (adv_size < 7) return 0u;
8996a9e015fSMatthias Ringwald 
9006a9e015fSMatthias Ringwald     uint16_t pos = 0;
9016a9e015fSMatthias Ringwald     // adv flags
9026a9e015fSMatthias Ringwald     adv_buffer[pos++] = 2;
9036a9e015fSMatthias Ringwald     adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_FLAGS;
9046a9e015fSMatthias Ringwald     adv_buffer[pos++] = 0x04;
9056a9e015fSMatthias Ringwald 
9066a9e015fSMatthias Ringwald     // adv interval
9076a9e015fSMatthias Ringwald     adv_buffer[pos++] = 3;
9086a9e015fSMatthias Ringwald     adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_ADVERTISING_INTERVAL;
9096a9e015fSMatthias Ringwald     little_endian_store_16(adv_buffer, pos, adv_interval);
9106a9e015fSMatthias Ringwald     pos += 2;
9116a9e015fSMatthias Ringwald 
9126a9e015fSMatthias Ringwald     uint16_t pos_without_data = pos;
9136a9e015fSMatthias Ringwald 
9146a9e015fSMatthias Ringwald     btstack_linked_list_iterator_t it;
9156a9e015fSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &battery_services);
9166a9e015fSMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
9176a9e015fSMatthias Ringwald         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
9186a9e015fSMatthias Ringwald         if ((service->battery_level_status_broadcast_configuration & 1) != 0) {
9196a9e015fSMatthias Ringwald             uint8_t value_buffer[10];
9206a9e015fSMatthias Ringwald             uint16_t value_len = bas_serialize_characteristic(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS, value_buffer, sizeof(value_buffer));
9216a9e015fSMatthias Ringwald             if ((pos + 4 + value_len) <= adv_size) {
9226a9e015fSMatthias Ringwald                 adv_buffer[pos++] = 3 + value_len;
9236a9e015fSMatthias Ringwald                 adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID;
9246a9e015fSMatthias Ringwald                 little_endian_store_16(adv_buffer, pos, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE);
9256a9e015fSMatthias Ringwald                 pos += 2;
9266a9e015fSMatthias Ringwald                 memcpy(&adv_buffer[pos], value_buffer, value_len);
9276a9e015fSMatthias Ringwald                 pos += value_len;
9286a9e015fSMatthias Ringwald             } else {
9296a9e015fSMatthias Ringwald                 break;
9306a9e015fSMatthias Ringwald             }
9316a9e015fSMatthias Ringwald         }
9326a9e015fSMatthias Ringwald     }
9336a9e015fSMatthias Ringwald 
9346a9e015fSMatthias Ringwald     // indicate nothing to broadcast
9356a9e015fSMatthias Ringwald     if (pos == pos_without_data) {
9366a9e015fSMatthias Ringwald         pos = 0;
9376a9e015fSMatthias Ringwald     }
9386a9e015fSMatthias Ringwald 
9396a9e015fSMatthias Ringwald     return pos;
9406a9e015fSMatthias Ringwald }
9416a9e015fSMatthias Ringwald 
battery_service_v1_server_get_broadcast_advertisement_single(battery_service_v1_t * service_single,uint16_t adv_interval,uint8_t * adv_buffer,uint16_t adv_size)9426a9e015fSMatthias Ringwald uint16_t battery_service_v1_server_get_broadcast_advertisement_single(battery_service_v1_t * service_single, uint16_t adv_interval, uint8_t * adv_buffer, uint16_t adv_size){
9436a9e015fSMatthias Ringwald 
9446a9e015fSMatthias Ringwald     if (adv_size < 7) return 0u;
9456a9e015fSMatthias Ringwald 
9466a9e015fSMatthias Ringwald     uint16_t pos = 0;
9476a9e015fSMatthias Ringwald     // adv flags
9486a9e015fSMatthias Ringwald     adv_buffer[pos++] = 2;
9496a9e015fSMatthias Ringwald     adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_FLAGS;
9506a9e015fSMatthias Ringwald     adv_buffer[pos++] = 0x04;
9516a9e015fSMatthias Ringwald 
9526a9e015fSMatthias Ringwald     // adv interval
9536a9e015fSMatthias Ringwald     adv_buffer[pos++] = 3;
9546a9e015fSMatthias Ringwald     adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_ADVERTISING_INTERVAL;
9556a9e015fSMatthias Ringwald     little_endian_store_16(adv_buffer, pos, adv_interval);
9566a9e015fSMatthias Ringwald     pos += 2;
9576a9e015fSMatthias Ringwald 
9586a9e015fSMatthias Ringwald     uint16_t pos_without_data = pos;
9596a9e015fSMatthias Ringwald 
9606a9e015fSMatthias Ringwald     btstack_linked_list_iterator_t it;
9616a9e015fSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &battery_services);
9626a9e015fSMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
9636a9e015fSMatthias Ringwald         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
9646a9e015fSMatthias Ringwald         if (service == service_single) {
9656a9e015fSMatthias Ringwald             if ((service->battery_level_status_broadcast_configuration & 1) != 0) {
9666a9e015fSMatthias Ringwald                 uint8_t value_buffer[10];
9676a9e015fSMatthias Ringwald                 uint16_t value_len = bas_serialize_characteristic(service,
9686a9e015fSMatthias Ringwald                                                                   BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS,
9696a9e015fSMatthias Ringwald                                                                   value_buffer, sizeof(value_buffer));
9706a9e015fSMatthias Ringwald                 if ((pos + 4 + value_len) <= adv_size) {
9716a9e015fSMatthias Ringwald                     adv_buffer[pos++] = 3 + value_len;
9726a9e015fSMatthias Ringwald                     adv_buffer[pos++] = BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID;
9736a9e015fSMatthias Ringwald                     little_endian_store_16(adv_buffer, pos, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE);
9746a9e015fSMatthias Ringwald                     pos += 2;
9756a9e015fSMatthias Ringwald                     memcpy(&adv_buffer[pos], value_buffer, value_len);
9766a9e015fSMatthias Ringwald                     pos += value_len;
9776a9e015fSMatthias Ringwald                 } else {
9786a9e015fSMatthias Ringwald                     break;
9796a9e015fSMatthias Ringwald                 }
9806a9e015fSMatthias Ringwald             }
9816a9e015fSMatthias Ringwald         }
9826a9e015fSMatthias Ringwald     }
9836a9e015fSMatthias Ringwald 
9846a9e015fSMatthias Ringwald     // indicate nothing to broadcast
9856a9e015fSMatthias Ringwald     if (pos == pos_without_data) {
9866a9e015fSMatthias Ringwald         pos = 0;
9876a9e015fSMatthias Ringwald     }
9886a9e015fSMatthias Ringwald 
9896a9e015fSMatthias Ringwald     return pos;
9906a9e015fSMatthias Ringwald }
9916a9e015fSMatthias Ringwald 
992