xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision d62aa1c5d3ca2e81d5ccc8cd6238a39f2faf6875)
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 #define BS_CONNECTIONS_MAX_NUM 10
53 #include "ble/gatt-service/battery_service_v1_server.h"
54 
55 #define BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED                          0x0001
56 
57 static btstack_linked_list_t battery_services;
58 
59 
60 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){
61     if (service == NULL){
62         return NULL;
63     }
64 
65     uint8_t i;
66     for (i = 0; i < service->connections_max_num; i++){
67         if (service->connections[i].con_handle == con_handle){
68             return &service->connections[i];
69         }
70     }
71     return NULL;
72 }
73 
74 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){
75     if (service == NULL){
76         return NULL;
77     }
78 
79     uint8_t i;
80     for (i = 0; i < service->connections_max_num; i++){
81         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
82             service->connections[i].con_handle = con_handle;
83             service->connections[i].service = service;
84             return &service->connections[i];
85         }
86     }
87     return NULL;
88 }
89 
90 
91 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
92     btstack_linked_list_iterator_t it;
93     btstack_linked_list_iterator_init(&it, &battery_services);
94     while (btstack_linked_list_iterator_has_next(&it)){
95         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
96         if (attribute_handle < item->start_handle) continue;
97         if (attribute_handle > item->end_handle)   continue;
98         return item;
99     }
100     return NULL;
101 }
102 
103 
104 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){
105     btstack_linked_list_iterator_t it;
106     btstack_linked_list_iterator_init(&it, &battery_services);
107     while (btstack_linked_list_iterator_has_next(&it)){
108         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
109         uint8_t i;
110         for (i = 0; i < service->connections_max_num; i++){
111             if (service->connections[i].con_handle == con_handle){
112                 return service;
113             }
114         }
115     }
116     return NULL;
117 }
118 
119 
120 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){
121     UNUSED(con_handle);
122 
123     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
124     if (service == NULL){
125         return 0;
126     }
127 
128     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
129     if (connection == NULL){
130         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
131         if (connection == NULL){
132             return 0;
133         }
134     }
135 
136     if (attribute_handle == service->battery_value_handle){
137         return att_read_callback_handle_byte(service->battery_value, offset, buffer, buffer_size);
138     }
139     if (attribute_handle == service->battery_value_client_configuration_handle){
140         return att_read_callback_handle_little_endian_16(connection->battery_value_client_configuration, offset, buffer, buffer_size);
141     }
142     return 0;
143 }
144 
145 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){
146     UNUSED(offset);
147     UNUSED(buffer_size);
148 
149     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
150         return 0;
151     }
152 
153     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
154     if (service == NULL){
155         return 0;
156     }
157 
158     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
159     if (connection == NULL){
160         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
161         if (connection == NULL){
162             return 0;
163         }
164     }
165 
166     if (attribute_handle == service->battery_value_client_configuration_handle){
167         connection->battery_value_client_configuration = little_endian_read_16(buffer, 0);
168     }
169     return 0;
170 }
171 
172 static void battery_service_can_send_now(void * context){
173     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
174     if (connection == NULL){
175         return;
176     }
177     battery_service_v1_t * service = connection->service;
178     if (service == NULL){
179         return;
180     }
181 
182     if ( (connection->scheduled_tasks & BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED) > 0u){
183         connection->scheduled_tasks &= ~BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED;
184         att_server_notify(connection->con_handle, service->battery_value_handle, &service->battery_value, 1);
185     }
186 
187     if (connection->scheduled_tasks > 0u){
188         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
189     }
190 }
191 
192 void battery_service_v1_server_init(void){
193 
194 }
195 
196 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num){
197     btstack_assert(service != NULL);
198     btstack_assert(connections != NULL);
199     btstack_assert(connection_max_num > 0u);
200 
201     // get service handle range
202     uint16_t end_handle   = 0xffff;
203     uint16_t start_handle = 0;
204 
205     btstack_linked_list_iterator_t it;
206     btstack_linked_list_iterator_init(&it, &battery_services);
207     while (btstack_linked_list_iterator_has_next(&it)){
208         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
209         if (service->end_handle > start_handle){
210             start_handle = service->end_handle + 1;
211         }
212     }
213 
214     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
215     btstack_assert(service_found != 0);
216     UNUSED(service_found);
217 
218     service->start_handle = start_handle;
219     service->end_handle   = end_handle;
220 
221     // get characteristic value handle and client configuration handle
222     service->battery_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL);
223     service->battery_value_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL);
224 
225     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
226     uint8_t i;
227     for (i = 0; i < connection_max_num; i++){
228         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
229     }
230     service->connections_max_num = connection_max_num;
231     service->connections = connections;
232 
233 
234     service->service_handler.read_callback  = &battery_service_read_callback;
235     service->service_handler.write_callback = &battery_service_write_callback;
236     att_server_register_service_handler(&service->service_handler);
237 
238     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
239 
240     btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service);
241 }
242 
243 
244 static void battery_service_set_callback(battery_service_v1_server_connection_t * connection, uint8_t task){
245     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
246         connection->scheduled_tasks = 0;
247         return;
248     }
249 
250     uint8_t scheduled_tasks = connection->scheduled_tasks;
251     connection->scheduled_tasks |= task;
252     if (scheduled_tasks == 0){
253         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
254         connection->scheduled_tasks_callback.context  = (void*) connection;
255         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
256     }
257 }
258 
259 
260 void battery_service_v1_server_set_battery_value(battery_service_v1_t * service, uint8_t value){
261     btstack_assert(service != NULL);
262     if (service->battery_value == value){
263         return;
264     }
265 
266     service->battery_value = value;
267 
268     uint8_t i;
269     for (i = 0; i < service->connections_max_num; i++){
270         battery_service_v1_server_connection_t * connection = &service->connections[i];
271         if (connection->battery_value_client_configuration != 0){
272             battery_service_set_callback(connection, BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED);
273         }
274     }
275 }
276 
277 void battery_service_v1_server_deregister(battery_service_v1_t *service){
278     btstack_linked_list_iterator_remove((btstack_linked_item_t * )service);
279     // TODO cansel can send now
280 }
281 
282 void battery_service_v1_server_deinit(void){
283     // deregister listeners
284     // empty list
285     btstack_linked_list_iterator_t it;
286     btstack_linked_list_iterator_init(&it, &battery_services);
287     while (btstack_linked_list_iterator_has_next(&it)){
288         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
289         battery_service_v1_server_deregister(service);
290     }
291 }
292