1 /*
2 * Copyright (C) 2023 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__ "immediate_alert_service_server.c"
39
40 #ifdef ENABLE_TESTING_SUPPORT
41 #include <stdio.h>
42 #endif
43
44 #include "btstack_defines.h"
45 #include "btstack_event.h"
46 #include "ble/att_db.h"
47 #include "ble/att_server.h"
48 #include "bluetooth_gatt.h"
49 #include "btstack_debug.h"
50
51 #include "ble/gatt-service/immediate_alert_service_server.h"
52 #include "btstack_run_loop.h"
53
54 #ifdef ENABLE_TESTING_SUPPORT
55 #include <stdio.h>
56 #endif
57
58 static att_service_handler_t immediate_alert_service;
59 static btstack_packet_handler_t ias_server_callback;
60
61 typedef enum {
62 IAS_SERVER_ALERT_STATE_IDLE = 0,
63 IAS_SERVER_ALERT_STATE_ALERTING
64 } ias_server_alert_state_t;
65
66 static uint16_t ias_server_alert_level_handle;
67 static ias_alert_level_t ias_server_alert_level;
68
69 static ias_server_alert_state_t ias_server_alert_state;
70 static uint32_t ias_server_alert_timeout_ms;
71 static btstack_timer_source_t ias_server_alert_timer;
72
73
ias_server_emit_alarm_started(void)74 static void ias_server_emit_alarm_started(void){
75 btstack_assert(ias_server_callback != NULL);
76
77 uint8_t event[4];
78 uint8_t pos = 0;
79 event[pos++] = HCI_EVENT_GATTSERVICE_META;
80 event[pos++] = sizeof(event) - 2;
81 event[pos++] = GATTSERVICE_SUBEVENT_IAS_SERVER_START_ALERTING;
82 event[pos++] = (uint8_t)ias_server_alert_level;
83 (*ias_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
84 }
85
ias_server_emit_alarm_stopped(bool stopped_due_timeout)86 static void ias_server_emit_alarm_stopped(bool stopped_due_timeout){
87 btstack_assert(ias_server_callback != NULL);
88
89 uint8_t event[5];
90 uint8_t pos = 0;
91 event[pos++] = HCI_EVENT_GATTSERVICE_META;
92 event[pos++] = sizeof(event) - 2;
93 event[pos++] = GATTSERVICE_SUBEVENT_IAS_SERVER_STOP_ALERTING;
94 event[pos++] = (uint8_t)ias_server_alert_level;
95 event[pos++] = stopped_due_timeout ? 1u : 0u;
96
97 (*ias_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
98 }
99
ias_server_stop_alerting(void)100 static void ias_server_stop_alerting(void){
101 btstack_run_loop_remove_timer(&ias_server_alert_timer);
102
103 if (ias_server_alert_state == IAS_SERVER_ALERT_STATE_ALERTING){
104 ias_server_emit_alarm_stopped(false);
105 ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE;
106 }
107 }
108
ias_server_timer_timeout_handler(btstack_timer_source_t * timer)109 static void ias_server_timer_timeout_handler(btstack_timer_source_t * timer){
110 UNUSED(timer);
111 ias_server_emit_alarm_stopped(true);
112 ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE;
113 }
114
ias_server_start_alerting(void)115 static void ias_server_start_alerting(void){
116 if (ias_server_alert_state == IAS_SERVER_ALERT_STATE_ALERTING){
117 return;
118 }
119
120 ias_server_alert_state = IAS_SERVER_ALERT_STATE_ALERTING;
121 ias_server_emit_alarm_started();
122
123 if (ias_server_alert_timeout_ms == 0){
124 return;
125 }
126
127 btstack_run_loop_set_timer_handler(&ias_server_alert_timer, ias_server_timer_timeout_handler);
128 btstack_run_loop_set_timer(&ias_server_alert_timer, ias_server_alert_timeout_ms);
129 btstack_run_loop_add_timer(&ias_server_alert_timer);
130 }
131
132
ias_server_read_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)133 static uint16_t ias_server_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
134 UNUSED(con_handle);
135
136 if (attribute_handle == ias_server_alert_level_handle){
137 return att_read_callback_handle_byte((uint8_t)ias_server_alert_level, offset, buffer, buffer_size);
138 }
139 return 0;
140 }
141
ias_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)142 static int ias_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){
143 UNUSED(transaction_mode);
144 UNUSED(offset);
145 UNUSED(con_handle);
146
147
148 if (attribute_handle == ias_server_alert_level_handle){
149 if (buffer_size != 1){
150 return 0;
151 }
152
153 ias_alert_level_t alert_level = (ias_alert_level_t)buffer[0];
154 if (alert_level >= IAS_ALERT_LEVEL_RFU){
155 return 0;
156 }
157
158 ias_server_stop_alerting();
159 ias_server_alert_level = alert_level;
160
161 switch (ias_server_alert_level){
162 case IAS_ALERT_LEVEL_NO_ALERT:
163 break;
164 default:
165 ias_server_start_alerting();
166 break;
167 }
168 }
169 return 0;
170 }
171
ias_server_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)172 static void ias_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
173 UNUSED(channel);
174 UNUSED(size);
175
176 if (packet_type != HCI_EVENT_PACKET){
177 return;
178 }
179
180 switch (hci_event_packet_get_type(packet)) {
181 case HCI_EVENT_DISCONNECTION_COMPLETE:
182 ias_server_stop_alerting();
183 break;
184 default:
185 break;
186 }
187 }
188
immediate_alert_service_server_init(void)189 void immediate_alert_service_server_init(void){
190 uint16_t start_handle = 0;
191 uint16_t end_handle = 0xffff;
192 int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_IMMEDIATE_ALERT, &start_handle, &end_handle);
193 btstack_assert(service_found != 0);
194 UNUSED(service_found);
195
196 ias_server_alert_level = IAS_ALERT_LEVEL_NO_ALERT;
197 ias_server_alert_timeout_ms = 0;
198 ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE;
199
200 // get characteristic value handle and client configuration handle
201 ias_server_alert_level_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL);
202
203 #ifdef ENABLE_TESTING_SUPPORT
204 printf("IAS 0x%02x - 0x%02x \n", start_handle, end_handle);
205 printf(" alert_level 0x%02x \n", ias_server_alert_level_handle);
206 #endif
207 log_info("Immediate Alert Service Server 0x%02x-0x%02x", start_handle, end_handle);
208
209 // register service with ATT Server
210 immediate_alert_service.start_handle = start_handle;
211 immediate_alert_service.end_handle = end_handle;
212 immediate_alert_service.read_callback = &ias_server_read_callback;
213 immediate_alert_service.write_callback = &ias_server_write_callback;
214 immediate_alert_service.packet_handler = ias_server_packet_handler;
215 att_server_register_service_handler(&immediate_alert_service);
216 }
217
immediate_alert_service_server_register_packet_handler(btstack_packet_handler_t packet_handler)218 void immediate_alert_service_server_register_packet_handler(btstack_packet_handler_t packet_handler){
219 btstack_assert(packet_handler != NULL);
220 ias_server_callback = packet_handler;
221 }
222
immediate_alert_service_server_set_alert_level(const ias_alert_level_t alert_level)223 uint8_t immediate_alert_service_server_set_alert_level(const ias_alert_level_t alert_level){
224 if (alert_level >= IAS_ALERT_LEVEL_RFU){
225 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
226 }
227
228 ias_server_alert_level = alert_level;
229 return ERROR_CODE_SUCCESS;
230 }
231
immediate_alert_service_server_stop_alerting(void)232 void immediate_alert_service_server_stop_alerting(void){
233 ias_server_stop_alerting();
234 }