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 }