xref: /btstack/src/ble/gatt-service/immediate_alert_service_server.c (revision 25d5427a345779b159b63c0d8b197d2dee40cf37)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
232 void immediate_alert_service_server_stop_alerting(void){
233     ias_server_stop_alerting();
234 }