1 /*
2 * Copyright (C) 2021 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__ "link_loss_service_server.c"
39
40 /**
41 * Implementation of the GATT Battery Service Server
42 * To use with your application, add '#import <link_loss_service.gatt' to your .gatt file
43 */
44
45 #ifdef ENABLE_TESTING_SUPPORT
46 #include <stdio.h>
47 #endif
48
49 #include "btstack_defines.h"
50 #include "btstack_event.h"
51 #include "ble/att_db.h"
52 #include "ble/att_server.h"
53 #include "btstack_util.h"
54 #include "bluetooth_gatt.h"
55 #include "btstack_debug.h"
56
57 #include "ble/gatt-service/link_loss_service_server.h"
58 #include "btstack_run_loop.h"
59 #include "hci.h"
60
61 static att_service_handler_t link_loss_service;
62 static btstack_packet_handler_t lls_server_callback;
63 static btstack_packet_callback_registration_t lls_server_hci_event_callback_registration;
64
65 typedef enum {
66 LLS_SERVER_ALERT_STATE_IDLE = 0,
67 LLS_SERVER_ALERT_STATE_ALERTING
68 } lls_server_alert_state_t;
69
70 static uint16_t lls_server_alert_level_handle;
71 static lls_alert_level_t lls_server_alert_level;
72
73 static lls_server_alert_state_t lls_server_alert_state;
74 static uint32_t lls_server_alert_timeout_ms;
75 static btstack_timer_source_t lls_server_alert_timer;
76
77
lls_server_emit_alarm_started(void)78 static void lls_server_emit_alarm_started(void){
79 btstack_assert(lls_server_callback != NULL);
80
81 uint8_t event[4];
82 uint8_t pos = 0;
83 event[pos++] = HCI_EVENT_GATTSERVICE_META;
84 event[pos++] = sizeof(event) - 2;
85 event[pos++] = GATTSERVICE_SUBEVENT_LLS_SERVER_START_ALERTING;
86 event[pos++] = (uint8_t)lls_server_alert_level;
87 (*lls_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
88 }
89
lls_server_emit_alarm_stopped(bool stopped_due_timeout)90 static void lls_server_emit_alarm_stopped(bool stopped_due_timeout){
91 btstack_assert(lls_server_callback != NULL);
92
93 uint8_t event[5];
94 uint8_t pos = 0;
95 event[pos++] = HCI_EVENT_GATTSERVICE_META;
96 event[pos++] = sizeof(event) - 2;
97 event[pos++] = GATTSERVICE_SUBEVENT_LLS_SERVER_STOP_ALERTING;
98 event[pos++] = (uint8_t)lls_server_alert_level;
99 event[pos++] = stopped_due_timeout ? 1u : 0u;
100
101 (*lls_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
102 }
103
lls_server_stop_alerting(void)104 static void lls_server_stop_alerting(void){
105 btstack_run_loop_remove_timer(&lls_server_alert_timer);
106
107 if (lls_server_alert_state == LLS_SERVER_ALERT_STATE_ALERTING){
108 lls_server_emit_alarm_stopped(false);
109 lls_server_alert_state = LLS_SERVER_ALERT_STATE_IDLE;
110 }
111 }
112
lls_server_timer_timeout_handler(btstack_timer_source_t * timer)113 static void lls_server_timer_timeout_handler(btstack_timer_source_t * timer){
114 UNUSED(timer);
115 lls_server_emit_alarm_stopped(true);
116 lls_server_alert_state = LLS_SERVER_ALERT_STATE_IDLE;
117 }
118
lls_server_start_alerting(void)119 static void lls_server_start_alerting(void){
120 if (lls_server_alert_state == LLS_SERVER_ALERT_STATE_ALERTING){
121 return;
122 }
123 if (lls_server_alert_level == LLS_ALERT_LEVEL_NO_ALERT){
124 return;
125 }
126
127 lls_server_alert_state = LLS_SERVER_ALERT_STATE_ALERTING;
128 lls_server_emit_alarm_started();
129
130 if (lls_server_alert_timeout_ms == 0){
131 return;
132 }
133
134 btstack_run_loop_set_timer_handler(&lls_server_alert_timer, lls_server_timer_timeout_handler);
135 btstack_run_loop_set_timer(&lls_server_alert_timer, lls_server_alert_timeout_ms);
136 btstack_run_loop_add_timer(&lls_server_alert_timer);
137 }
138
lls_server_read_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)139 static uint16_t lls_server_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
140 UNUSED(con_handle);
141
142 if (attribute_handle == lls_server_alert_level_handle){
143 return att_read_callback_handle_byte((uint8_t)lls_server_alert_level, offset, buffer, buffer_size);
144 }
145 return 0;
146 }
147
lls_server_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)148 static int lls_server_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){
149 UNUSED(transaction_mode);
150 UNUSED(offset);
151 UNUSED(con_handle);
152
153
154 if (attribute_handle == lls_server_alert_level_handle){
155 if (buffer_size != 1){
156 return ATT_ERROR_VALUE_NOT_ALLOWED;
157 }
158
159 lls_alert_level_t alert_level = (lls_alert_level_t)buffer[0];
160 switch (alert_level){
161 case LLS_ALERT_LEVEL_NO_ALERT:
162 case LLS_ALERT_LEVEL_MILD_ALERT:
163 case LLS_ALERT_LEVEL_HIGH_ALERT:
164 lls_server_alert_level = alert_level;
165 break;
166 default:
167 return ATT_ERROR_VALUE_NOT_ALLOWED;
168 }
169 }
170 return 0;
171 }
172
lls_server_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)173 static void lls_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
174 UNUSED(channel);
175 UNUSED(packet);
176 UNUSED(size);
177
178 if (packet_type != HCI_EVENT_PACKET){
179 return;
180 }
181
182 uint8_t reason;
183
184 switch (hci_event_packet_get_type(packet)){
185 case HCI_EVENT_CONNECTION_COMPLETE:
186 // ignore if connection failed
187 if (hci_event_connection_complete_get_status(packet) != ERROR_CODE_SUCCESS){
188 return;
189 }
190 lls_server_stop_alerting();
191 break;
192
193 case HCI_EVENT_DISCONNECTION_COMPLETE:
194 reason = hci_event_disconnection_complete_get_reason(packet);
195 if (reason == ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION){
196 lls_server_stop_alerting();
197 } else {
198 lls_server_start_alerting();
199 }
200 break;
201 default:
202 break;
203 }
204 }
205
link_loss_service_server_init(void)206 void link_loss_service_server_init(void){
207 // get service handle range
208 uint16_t start_handle = 0;
209 uint16_t end_handle = 0xffff;
210 int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_LINK_LOSS, &start_handle, &end_handle);
211 btstack_assert(service_found != 0);
212 UNUSED(service_found);
213
214 lls_server_alert_level = LLS_ALERT_LEVEL_NO_ALERT;
215 lls_server_alert_timeout_ms = 0;
216 lls_server_alert_state = LLS_SERVER_ALERT_STATE_IDLE;
217
218 // get characteristic value handle and client configuration handle
219 lls_server_alert_level_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL);
220
221 #ifdef ENABLE_TESTING_SUPPORT
222 printf("LLS 0x%02x - 0x%02x \n", start_handle, end_handle);
223 printf(" alert_level 0x%02x \n", lls_server_alert_level_handle);
224 #endif
225 log_info("Link Loss Service Server 0x%02x-0x%02x", start_handle, end_handle);
226
227 // register service with ATT Server
228 link_loss_service.start_handle = start_handle;
229 link_loss_service.end_handle = end_handle;
230 link_loss_service.read_callback = &lls_server_read_callback;
231 link_loss_service.write_callback = &lls_server_write_callback;
232
233 att_server_register_service_handler(&link_loss_service);
234
235 lls_server_hci_event_callback_registration.callback = &lls_server_packet_handler;
236 hci_add_event_handler(&lls_server_hci_event_callback_registration);
237 }
238
link_loss_service_server_register_packet_handler(btstack_packet_handler_t packet_handler)239 void link_loss_service_server_register_packet_handler(btstack_packet_handler_t packet_handler){
240 btstack_assert(packet_handler != NULL);
241 lls_server_callback = packet_handler;
242 }
243
link_loss_service_server_set_alert_level(lls_alert_level_t alert_level)244 uint8_t link_loss_service_server_set_alert_level(lls_alert_level_t alert_level){
245 if (alert_level >= LLS_ALERT_LEVEL_RFU){
246 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
247 }
248 lls_server_alert_level = alert_level;
249 return ERROR_CODE_SUCCESS;
250 }
251
link_loss_service_server_set_alert_timeout(uint32_t alert_timeout_ms)252 uint8_t link_loss_service_server_set_alert_timeout(uint32_t alert_timeout_ms){
253 if (lls_server_alert_state != LLS_SERVER_ALERT_STATE_IDLE){
254 return BTSTACK_BUSY;
255 }
256 lls_server_alert_timeout_ms = alert_timeout_ms;
257 return ERROR_CODE_SUCCESS;
258 }
259
link_loss_service_server_stop_alerting(void)260 void link_loss_service_server_stop_alerting(void){
261 lls_server_stop_alerting();
262 }
263