xref: /btstack/src/ble/gatt-service/link_loss_service_server.c (revision e54a3bca5d33ee0e65d314c9513f358064739602)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
260 void link_loss_service_server_stop_alerting(void){
261     lls_server_stop_alerting();
262 }
263