diff --git a/CHANGELOG.md b/CHANGELOG.md index 838a48bcd..6d6a5f9d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - HID Parser: introduce HID Descriptor and HID Descriptor Usage iterators - HCI Dump Dispatch: allow to use multiple HCI Dump implementations - HCI Dump: support ISO packets in PacketLogger format +- Immediate Alert Service Server and Client: added - btstack_util: safe wrappers for snprintf ### Fixed diff --git a/src/ble/gatt-service/immediate_alert_service.gatt b/src/ble/gatt-service/immediate_alert_service.gatt new file mode 100644 index 000000000..dde302b01 --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service.gatt @@ -0,0 +1,4 @@ +// Immediate Alert Service 0x1802 +PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_IMMEDIATE_ALERT + +CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL, DYNAMIC | WRITE_WITHOUT_RESPONSE, diff --git a/src/ble/gatt-service/immediate_alert_service_client.c b/src/ble/gatt-service/immediate_alert_service_client.c new file mode 100644 index 000000000..2f8ebd946 --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service_client.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define BTSTACK_FILE__ "immediate_alert_service_client.c" + +#include "btstack_config.h" + +#ifdef ENABLE_TESTING_SUPPORT +#include +#include +#endif + +#include +#include + +#include "ble/gatt_service_client.h" +#include "ble/gatt-service/immediate_alert_service_client.h" + +#include "bluetooth_gatt.h" +#include "btstack_debug.h" +#include "btstack_event.h" + +// IAS Client +static gatt_service_client_t ias_client; +static btstack_linked_list_t ias_connections; + +static btstack_context_callback_registration_t ias_client_handle_can_send_now; + +static void ias_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void ias_client_run_for_connection(void * context); + +// list of uuids +static const uint16_t ias_uuid16s[IMMEDIATE_ALERT_SERVICE_CLIENT_NUM_CHARACTERISTICS] = { + ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL +}; + +typedef enum { + IAS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL = 0, + IAS_CLIENT_CHARACTERISTIC_INDEX_RFU +} ias_client_characteristic_index_t; + +#ifdef ENABLE_TESTING_SUPPORT +static char * ias_client_characteristic_name[] = { + "ALERT_LEVEL", + "RFU" +}; +#endif + +static ias_client_connection_t * ias_client_get_connection_for_cid(uint16_t connection_cid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &ias_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + ias_client_connection_t * connection = (ias_client_connection_t *)btstack_linked_list_iterator_next(&it); + if (gatt_service_client_get_connection_id(&connection->basic_connection) == connection_cid) { + return connection; + } + } + return NULL; +} + +static void ias_client_add_connection(ias_client_connection_t * connection){ + btstack_linked_list_add(&ias_connections, (btstack_linked_item_t*) connection); +} + +static void ias_client_finalize_connection(ias_client_connection_t * connection){ + btstack_linked_list_remove(&ias_connections, (btstack_linked_item_t*) connection); +} + +static void ias_client_replace_subevent_id_and_emit(btstack_packet_handler_t callback, uint8_t * packet, uint16_t size, uint8_t subevent_id){ + UNUSED(size); + btstack_assert(callback != NULL); + // execute callback + packet[2] = subevent_id; + (*callback)(HCI_EVENT_PACKET, 0, packet, size); +} + +static void ias_client_connected(ias_client_connection_t * connection, uint8_t status, uint8_t * packet, uint16_t size) { + if (status == ERROR_CODE_SUCCESS){ + connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_READY; + } else { + connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_IDLE; + } + ias_client_replace_subevent_id_and_emit(connection->packet_handler, packet, size, + GATTSERVICE_SUBEVENT_IAS_CLIENT_CONNECTED); +} + + +static uint16_t ias_client_value_handle_for_index(ias_client_connection_t * connection){ + return connection->basic_connection.characteristics[connection->characteristic_index].value_handle; +} + +static uint8_t ias_client_can_query_characteristic(ias_client_connection_t * connection, ias_client_characteristic_index_t characteristic_index){ + uint8_t status = gatt_service_client_can_query_characteristic(&connection->basic_connection, + (uint8_t) characteristic_index); + if (status != ERROR_CODE_SUCCESS){ + return status; + } + return connection->state == IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_READY ? ERROR_CODE_SUCCESS : ERROR_CODE_CONTROLLER_BUSY; +} + +static uint8_t ias_client_request_send_gatt_query(ias_client_connection_t * connection, ias_client_characteristic_index_t characteristic_index){ + connection->characteristic_index = characteristic_index; + + ias_client_handle_can_send_now.context = (void *)(uintptr_t)connection->basic_connection.cid; + uint8_t status = gatt_client_request_to_send_gatt_query(&ias_client_handle_can_send_now, connection->basic_connection.con_handle); + if (status != ERROR_CODE_SUCCESS){ + connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_READY; + } + return status; +} + +static uint8_t ias_client_request_write_without_response_characteristic(ias_client_connection_t * connection, ias_client_characteristic_index_t characteristic_index){ + connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_W2_WRITE_WITHOUT_RESPONSE_CHARACTERISTIC_VALUE; + return ias_client_request_send_gatt_query(connection, characteristic_index); +} + +static void ias_client_packet_handler_internal(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + if (packet_type != HCI_EVENT_PACKET) return; + ias_client_connection_t * connection; + uint16_t cid; + uint8_t status; + + switch(hci_event_packet_get_type(packet)){ + case HCI_EVENT_GATTSERVICE_META: + switch (hci_event_gattservice_meta_get_subevent_code(packet)) { + case GATTSERVICE_SUBEVENT_CLIENT_CONNECTED: + cid = gattservice_subevent_client_connected_get_cid(packet); + connection = ias_client_get_connection_for_cid(cid); + btstack_assert(connection != NULL); + +#ifdef ENABLE_TESTING_SUPPORT + { + uint8_t i; + for (i = IAS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL; + i < IAS_CLIENT_CHARACTERISTIC_INDEX_RFU; i++) { + printf("0x%04X %s\n", connection->basic_connection.characteristics[i].value_handle, + ias_client_characteristic_name[i]); + } + }; +#endif + status = gattservice_subevent_client_connected_get_status(packet); + ias_client_connected(connection, status, packet, size); + break; + + case GATTSERVICE_SUBEVENT_CLIENT_DISCONNECTED: + cid = gattservice_subevent_client_disconnected_get_cid(packet); + connection = ias_client_get_connection_for_cid(cid); + btstack_assert(connection != NULL); + ias_client_finalize_connection(connection); + ias_client_replace_subevent_id_and_emit(connection->packet_handler, + packet, size, + GATTSERVICE_SUBEVENT_IAS_CLIENT_DISCONNECTED); + break; + + default: + break; + } + break; + + default: + break; + } +} + +static uint16_t ias_client_serialize_characteristic_value_for_write(ias_client_connection_t * connection, uint8_t ** out_value){ + uint16_t characteristic_uuid16 = gatt_service_client_characteristic_uuid16_for_index(&ias_client, + connection->characteristic_index); + *out_value = (uint8_t *)connection->write_buffer; + + switch (characteristic_uuid16){ + case ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL: + return 1; + default: + btstack_assert(false); + break; + } + return 0; +} + +static void ias_client_run_for_connection(void * context){ + uint16_t connection_id = (uint16_t)(uintptr_t)context; + ias_client_connection_t * connection = ias_client_get_connection_for_cid(connection_id); + + btstack_assert(connection != NULL); + uint16_t value_length; + uint8_t * value; + + switch (connection->state){ + case IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_W2_WRITE_WITHOUT_RESPONSE_CHARACTERISTIC_VALUE: + connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_READY; + + value_length = ias_client_serialize_characteristic_value_for_write(connection, &value); + gatt_client_write_value_of_characteristic_without_response( + gatt_service_client_get_con_handle(&connection->basic_connection), + ias_client_value_handle_for_index(connection), + value_length, value); + + break; + + default: + break; + } +} + +void immediate_alert_service_client_init(void){ + gatt_service_client_register_client(&ias_client, &ias_client_packet_handler_internal, ias_uuid16s, sizeof(ias_uuid16s)/sizeof(uint16_t)); + + ias_client_handle_can_send_now.callback = &ias_client_run_for_connection; +} + +uint8_t immediate_alert_service_client_connect(hci_con_handle_t con_handle, + btstack_packet_handler_t packet_handler, + ias_client_connection_t * ias_connection, + uint16_t * ias_cid +){ + + btstack_assert(packet_handler != NULL); + btstack_assert(ias_connection != NULL); + + *ias_cid = 0; + + ias_connection->state = IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_W4_CONNECTION; + ias_connection->packet_handler = packet_handler; + + uint8_t status = gatt_service_client_connect_primary_service_with_uuid16(con_handle, + &ias_client, + &ias_connection->basic_connection, + ORG_BLUETOOTH_SERVICE_IMMEDIATE_ALERT, 0, + ias_connection->characteristics_storage, + IMMEDIATE_ALERT_SERVICE_CLIENT_NUM_CHARACTERISTICS); + + if (status == ERROR_CODE_SUCCESS){ + ias_client_add_connection(ias_connection); + *ias_cid = ias_connection->basic_connection.cid; + } + + return status; +} + +uint8_t immediate_alert_service_client_write_alert_level(uint16_t ias_cid, ias_alert_level_t alert_level){ + ias_client_connection_t * connection = ias_client_get_connection_for_cid(ias_cid); + if (connection == NULL){ + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + if (alert_level >= IAS_ALERT_LEVEL_RFU){ + return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; + } + + ias_client_characteristic_index_t index = IAS_CLIENT_CHARACTERISTIC_INDEX_ALERT_LEVEL; + + uint8_t status = ias_client_can_query_characteristic(connection, index); + if (status != ERROR_CODE_SUCCESS){ + return status; + } + + connection->write_buffer[0] = (uint8_t)alert_level; + return ias_client_request_write_without_response_characteristic(connection, index); +} + +uint8_t immediate_alert_service_client_disconnect(uint16_t ias_cid){ + ias_client_connection_t * connection = ias_client_get_connection_for_cid(ias_cid); + if (connection == NULL){ + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + return gatt_service_client_disconnect(&connection->basic_connection); +} + +void immediate_alert_service_client_deinit(void){ + gatt_service_client_unregister_client(&ias_client); +} + diff --git a/src/ble/gatt-service/immediate_alert_service_client.h b/src/ble/gatt-service/immediate_alert_service_client.h new file mode 100644 index 000000000..1cf2310e3 --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service_client.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/** + * @title Immediate Alert Service Client + * + */ + +#ifndef IMMEDIATE_ALERT_SERVICE_CLIENT_H +#define IMMEDIATE_ALERT_SERVICE_CLIENT_H + +#include +#include "btstack_defines.h" +#include "bluetooth.h" +#include "btstack_linked_list.h" +#include "ble/gatt_client.h" +#include "ble/gatt_service_client.h" +#include "ble/gatt-service/immediate_alert_service_util.h" + +#if defined __cplusplus +extern "C" { +#endif + +/** + * @text The Immediate Alert Service Client + */ +typedef enum { + IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_IDLE = 0, + IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_W4_CONNECTION, + IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_READY, + IMMEDIATE_ALERT_SERVICE_CLIENT_STATE_W2_WRITE_WITHOUT_RESPONSE_CHARACTERISTIC_VALUE +} immediate_alert_service_client_state_t; + +typedef struct { + btstack_linked_item_t item; + + gatt_service_client_connection_t basic_connection; + btstack_packet_handler_t packet_handler; + + gatt_service_client_characteristic_t characteristics_storage[IMMEDIATE_ALERT_SERVICE_CLIENT_NUM_CHARACTERISTICS]; + + immediate_alert_service_client_state_t state; + + // Used for read characteristic queries + uint8_t characteristic_index; + // Used to store parameters for write characteristic + + uint8_t write_buffer[1]; +} ias_client_connection_t; + +/* API_START */ + + +/** + * @brief Initialize Immediate Alert Service. + */ +void immediate_alert_service_client_init(void); + + /** + * @brief Connect to a Immediate Alert Service instance of remote device. The client will automatically register for notifications. + * The mute state is received via GATTSERVICE_SUBEVENT_LLS_CLIENT_MUTE event. + * The mute state can be 0 - off, 1 - on, 2 - disabled and it is valid if the ATT status is equal to ATT_ERROR_SUCCESS, + * see ATT errors (see bluetooth.h) for other values. + * + * Event GATTSERVICE_SUBEVENT_LLS_CLIENT_CONNECTED is emitted with status ERROR_CODE_SUCCESS on success, otherwise + * GATT_CLIENT_IN_WRONG_STATE, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no audio input control service is found, or ATT errors (see bluetooth.h). + * + * @param con_handle + * @param packet_handler + * @param iac_connection + * @param iac_characteristics_storage storage for characteristics + * @param iac_characteristics_num == IMMEDIATE_ALERT_SERVICE_CLIENT_NUM_CHARACTERISTICS + * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_COMMAND_DISALLOWED if there is already a client associated with con_handle, or BTSTACK_MEMORY_ALLOC_FAILED + */ +uint8_t immediate_alert_service_client_connect( + hci_con_handle_t con_handle, + btstack_packet_handler_t packet_handler, + ias_client_connection_t * iac_connection, + uint16_t * ias_cid +); + +uint8_t immediate_alert_service_client_write_alert_level(uint16_t ias_cid, ias_alert_level_t alert_level); + + +/** + * @brief Disconnect. + * @param ias_cid + * @return status + */ +uint8_t immediate_alert_service_client_disconnect(uint16_t ias_cid); + +/** + * @brief De-initialize Immediate Alert Service. + */ +void immediate_alert_service_client_deinit(void); + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif diff --git a/src/ble/gatt-service/immediate_alert_service_server.c b/src/ble/gatt-service/immediate_alert_service_server.c new file mode 100644 index 000000000..a0f30363c --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service_server.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2023 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define BTSTACK_FILE__ "immediate_alert_service_server.c" + +#ifdef ENABLE_TESTING_SUPPORT +#include +#endif + +#include "btstack_defines.h" +#include "btstack_event.h" +#include "ble/att_db.h" +#include "ble/att_server.h" +#include "bluetooth_gatt.h" +#include "btstack_debug.h" + +#include "ble/gatt-service/immediate_alert_service_server.h" +#include "btstack_run_loop.h" + +#ifdef ENABLE_TESTING_SUPPORT +#include +#endif + +static att_service_handler_t immediate_alert_service; +static btstack_packet_handler_t ias_server_callback; + +typedef enum { + IAS_SERVER_ALERT_STATE_IDLE = 0, + IAS_SERVER_ALERT_STATE_ALERTING +} ias_server_alert_state_t; + +static uint16_t ias_server_alert_level_handle; +static ias_alert_level_t ias_server_alert_level; + +static ias_server_alert_state_t ias_server_alert_state; +static uint32_t ias_server_alert_timeout_ms; +static btstack_timer_source_t ias_server_alert_timer; + + +static void ias_server_emit_alarm_started(void){ + btstack_assert(ias_server_callback != NULL); + + uint8_t event[4]; + uint8_t pos = 0; + event[pos++] = HCI_EVENT_GATTSERVICE_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = GATTSERVICE_SUBEVENT_IAS_SERVER_START_ALERTING; + event[pos++] = (uint8_t)ias_server_alert_level; + (*ias_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static void ias_server_emit_alarm_stopped(bool stopped_due_timeout){ + btstack_assert(ias_server_callback != NULL); + + uint8_t event[5]; + uint8_t pos = 0; + event[pos++] = HCI_EVENT_GATTSERVICE_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = GATTSERVICE_SUBEVENT_IAS_SERVER_STOP_ALERTING; + event[pos++] = (uint8_t)ias_server_alert_level; + event[pos++] = stopped_due_timeout ? 1u : 0u; + + (*ias_server_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static void ias_server_stop_alerting(void){ + btstack_run_loop_remove_timer(&ias_server_alert_timer); + + if (ias_server_alert_state == IAS_SERVER_ALERT_STATE_ALERTING){ + ias_server_emit_alarm_stopped(false); + ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE; + } +} + +static void ias_server_timer_timeout_handler(btstack_timer_source_t * timer){ + UNUSED(timer); + ias_server_emit_alarm_stopped(true); + ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE; +} + +static void ias_server_start_alerting(void){ + if (ias_server_alert_state == IAS_SERVER_ALERT_STATE_ALERTING){ + return; + } + + ias_server_alert_state = IAS_SERVER_ALERT_STATE_ALERTING; + ias_server_emit_alarm_started(); + + if (ias_server_alert_timeout_ms == 0){ + return; + } + + btstack_run_loop_set_timer_handler(&ias_server_alert_timer, ias_server_timer_timeout_handler); + btstack_run_loop_set_timer(&ias_server_alert_timer, ias_server_alert_timeout_ms); + btstack_run_loop_add_timer(&ias_server_alert_timer); +} + + +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){ + UNUSED(con_handle); + + if (attribute_handle == ias_server_alert_level_handle){ + return att_read_callback_handle_byte((uint8_t)ias_server_alert_level, offset, buffer, buffer_size); + } + return 0; +} + +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){ + UNUSED(transaction_mode); + UNUSED(offset); + UNUSED(con_handle); + + + if (attribute_handle == ias_server_alert_level_handle){ + if (buffer_size != 1){ + return 0; + } + + ias_alert_level_t alert_level = (ias_alert_level_t)buffer[0]; + if (alert_level >= IAS_ALERT_LEVEL_RFU){ + return 0; + } + + ias_server_stop_alerting(); + ias_server_alert_level = alert_level; + + switch (ias_server_alert_level){ + case IAS_ALERT_LEVEL_NO_ALERT: + break; + default: + ias_server_start_alerting(); + break; + } + } + return 0; +} + +static void ias_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + if (packet_type != HCI_EVENT_PACKET){ + return; + } + + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_DISCONNECTION_COMPLETE: + ias_server_stop_alerting(); + break; + default: + break; + } +} + +void immediate_alert_service_server_init(void){ + uint16_t start_handle = 0; + uint16_t end_handle = 0xffff; + int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_IMMEDIATE_ALERT, &start_handle, &end_handle); + btstack_assert(service_found != 0); + UNUSED(service_found); + + ias_server_alert_level = IAS_ALERT_LEVEL_NO_ALERT; + ias_server_alert_timeout_ms = 0; + ias_server_alert_state = IAS_SERVER_ALERT_STATE_IDLE; + + // get characteristic value handle and client configuration handle + ias_server_alert_level_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_ALERT_LEVEL); + +#ifdef ENABLE_TESTING_SUPPORT + printf("IAS 0x%02x - 0x%02x \n", start_handle, end_handle); + printf(" alert_level 0x%02x \n", ias_server_alert_level_handle); +#endif + log_info("Immediate Alert Service Server 0x%02x-0x%02x", start_handle, end_handle); + + // register service with ATT Server + immediate_alert_service.start_handle = start_handle; + immediate_alert_service.end_handle = end_handle; + immediate_alert_service.read_callback = &ias_server_read_callback; + immediate_alert_service.write_callback = &ias_server_write_callback; + immediate_alert_service.packet_handler = ias_server_packet_handler; + att_server_register_service_handler(&immediate_alert_service); +} + +void immediate_alert_service_server_register_packet_handler(btstack_packet_handler_t packet_handler){ + btstack_assert(packet_handler != NULL); + ias_server_callback = packet_handler; +} + +uint8_t immediate_alert_service_server_set_alert_level(const ias_alert_level_t alert_level){ + if (alert_level >= IAS_ALERT_LEVEL_RFU){ + return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; + } + + ias_server_alert_level = alert_level; + return ERROR_CODE_SUCCESS; +} + +void immediate_alert_service_server_stop_alerting(void){ + ias_server_stop_alerting(); +} \ No newline at end of file diff --git a/src/ble/gatt-service/immediate_alert_service_server.h b/src/ble/gatt-service/immediate_alert_service_server.h new file mode 100644 index 000000000..d379bfe20 --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service_server.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/** + * @title Immediate Alert Service Server + * + */ + +#ifndef IMMEDIATE_ALERT_SERVICE_SERVER_H +#define IMMEDIATE_ALERT_SERVICE_SERVER_H + +#include + +#include "ble/att_db.h" +#include "ble/gatt-service/immediate_alert_service_util.h" + +#if defined __cplusplus +extern "C" { +#endif + +/** + * @text To use with your application, add `#import ` to your .gatt file. + * + */ + +/* API_START */ + +/** + * @brief Init Immediate Alert Service Server with ATT DB + * @param mute_state + */ +void immediate_alert_service_server_init(void); + +/** + * @brief Register packet handler + */ +void immediate_alert_service_server_register_packet_handler(btstack_packet_handler_t packet_handler); + +/** + * @brief Set alert level. + * @param alert_level + */ +uint8_t immediate_alert_service_server_set_alert_level(const ias_alert_level_t alert_level); + +/** + * @brief Set alert timeout. Default is 0 that equals to no timeout. + * @param alert_timeout_ms + */ +uint8_t immediate_alert_service_server_set_alert_timeout(uint32_t alert_timeout_ms); + + +void immediate_alert_service_server_stop_alerting(void); + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif + diff --git a/src/ble/gatt-service/immediate_alert_service_util.h b/src/ble/gatt-service/immediate_alert_service_util.h new file mode 100644 index 000000000..035b0867e --- /dev/null +++ b/src/ble/gatt-service/immediate_alert_service_util.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/** + * @title Immediate Alert Service Util + * + */ + +#ifndef IMMEDIATE_ALERT_SERVICE_UTIL_H +#define IMMEDIATE_ALERT_SERVICE_UTIL_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +// number of characteristics in Immediate Alert Service +#define IMMEDIATE_ALERT_SERVICE_CLIENT_NUM_CHARACTERISTICS 1 + +typedef enum { + IAS_ALERT_LEVEL_NO_ALERT = 0, + IAS_ALERT_LEVEL_MILD_ALERT, + IAS_ALERT_LEVEL_HIGH_ALERT, + IAS_ALERT_LEVEL_RFU, +} ias_alert_level_t; + +#if defined __cplusplus +} +#endif + +#endif diff --git a/src/btstack.h b/src/btstack.h index 95b9526f0..2af62a1df 100644 --- a/src/btstack.h +++ b/src/btstack.h @@ -94,6 +94,9 @@ #include "ble/gatt-service/heart_rate_service_server.h" #include "ble/gatt-service/hids_client.h" #include "ble/gatt-service/hids_device.h" +#include "ble/gatt-service/immediate_alert_service_client.h" +#include "ble/gatt-service/immediate_alert_service_server.h" +#include "ble/gatt-service/immediate_alert_service_util.h" #include "ble/gatt-service/scan_parameters_service_client.h" #include "ble/gatt-service/scan_parameters_service_server.h" #include "ble/gatt-service/tx_power_service_server.h"