From 7d405bedbe1e18132075045767b317d7357d0522 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 23 Feb 2018 11:44:48 +0100 Subject: [PATCH] new att_server_delayed_read_response example --- example/Makefile.inc | 6 +- example/att_delayed_read_response.c | 212 +++++++++++++++++++++++++ example/att_delayed_read_response.gatt | 10 ++ port/libusb/.gitignore | 2 + port/libusb/btstack_config.h | 1 + 5 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 example/att_delayed_read_response.c create mode 100644 example/att_delayed_read_response.gatt diff --git a/example/Makefile.inc b/example/Makefile.inc index 9e15e7647..cdc985651 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -108,8 +108,9 @@ HXCMOD_PLAYER = \ ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c \ EXAMPLES = \ + att_delayed_read_response \ a2dp_sink_demo \ - a2dp_source_demo \ + a2dp_source_demo \ ancs_client_demo \ dut_mode_classic \ gap_dedicated_bonding \ @@ -205,6 +206,9 @@ spp_counter: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} spp_counter.c le_counter: le_counter.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} battery_service_server.o le_counter.c ${CC} $(filter-out le_counter.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ +att_delayed_read_response: att_delayed_read_response.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} att_delayed_read_response.c + ${CC} $(filter-out att_delayed_read_response.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ + hog_keyboard_demo: hog_keyboard_demo.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} battery_service_server.o device_information_service_server.o hids_device.o btstack_ring_buffer.o hog_keyboard_demo.c ${CC} $(filter-out hog_keyboard_demo.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ diff --git a/example/att_delayed_read_response.c b/example/att_delayed_read_response.c new file mode 100644 index 000000000..f84298433 --- /dev/null +++ b/example/att_delayed_read_response.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2018 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 MATTHIAS + * RINGWALD 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__ "att_delayed_read_response.c" + +// ***************************************************************************** +/* EXAMPLE_START(att_delayed_read_response): LE Peripheral - Delayed Read Response + * + * @text If the value for a GATT Chararacteristic isn't available, the value + * ATT_READ_RESPONSE_PENDING can be returned. When the value is available, + * att_server_read_response_ready is then called to complete the ATT request. + */ + // ***************************************************************************** + +#include +#include +#include +#include + +#include "att_delayed_read_response.h" +#include "btstack.h" + +#define ATT_VALUE_DELAY_MS 3000 +#define ATT_VALUE_INVALID_MS 5000 + +/* @section Main Application Setup + * + * @text Listing MainConfiguration shows main application code. + * It initializes L2CAP, the Security Manager and configures the ATT Server with the pre-compiled + * ATT Database generated from $le_counter.gatt$. + * Additionally, it enables the Battery Service Server with the current battery level. + * Finally, it configures the advertisements and boots the Bluetooth stack. + * In this example, the Advertisement contains the Flags attribute and the device name. + * The flag 0x06 indicates: LE General Discoverable Mode and BR/EDR not supported. + */ + +/* LISTING_START(MainConfiguration): Init L2CAP SM ATT Server */ +static btstack_timer_source_t att_timer; +static hci_con_handle_t con_handle; +static int value_ready; + +static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); + +const uint8_t adv_data[] = { + // Flags general discoverable, BR/EDR not supported + 0x02, 0x01, 0x06, + // Name + 0x0b, 0x09, 'L', 'E', ' ', 'C', 'o', 'u', 'n', 't', 'e', 'r', +}; +const uint8_t adv_data_len = sizeof(adv_data); + +const char * test_string = "Delayed response"; + +static void example_setup(void){ + + l2cap_init(); + + // setup le device db + le_device_db_init(); + + // setup SM: Display only + sm_init(); + + // setup ATT server + att_server_init(profile_data, att_read_callback, NULL); + + // setup advertisements + uint16_t adv_int_min = 0x0030; + uint16_t adv_int_max = 0x0030; + uint8_t adv_type = 0; + bd_addr_t null_addr; + memset(null_addr, 0, 6); + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); +} +/* LISTING_END */ + +/* + * @section att_invalidate_value Handler + * + * @text The att_invalidate_value handler 'invalidates' the value of the single Characteristic provided in this example + */ + +static void att_invalidate_value(struct btstack_timer_source *ts){ + UNUSED(ts); + printf("Value got stale\n"); + value_ready = 0; +} + +/* + * @section att_update_value Handler + * + * @text The att_update_value handler 'updates' the value of the single Characteristic provided in this example + */ + + /* LISTING_START(att_read_delay): ATT Read Delay Handler */ + +static void att_update_value(struct btstack_timer_source *ts){ + UNUSED(ts); + value_ready = 1; + + + // trigger ATT Server to try request again + int status = att_server_read_response_ready(con_handle); + + printf("Value updated -> complete ATT request - status %02x\n", status); + + // simulated value becoming stale again + att_timer.process = &att_invalidate_value; + btstack_run_loop_set_timer(&att_timer, ATT_VALUE_DELAY_MS); + btstack_run_loop_add_timer(&att_timer); +} + +/* LISTING_END */ + +/* + * @section ATT Read + * + * @text The ATT Server handles all reads to constant data. For dynamic data like the custom characteristic, the registered + * att_read_callback is called. To handle long characteristics and long reads, the att_read_callback is first called + * with buffer == NULL, to request the total value length. Then it will be called again requesting a chunk of the value. + * See Listing attRead. + */ + +/* LISTING_START(attRead): ATT Read */ + +// ATT Client Read Callback for Dynamic Data +// - if buffer == NULL, don't copy data, just return size of value +// - if buffer != NULL, copy data and return number bytes copied +// @param offset defines start of attribute value +static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ + +#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE + switch (att_handle){ + case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE: + if (value_ready){ + return att_read_callback_handle_blob((const uint8_t *)test_string, strlen(test_string), offset, buffer, buffer_size); + } else { + printf("Read callback for handle %02x, but value not ready -> report response pending\n", att_handle); + con_handle = connection_handle; + return ATT_READ_RESPONSE_PENDING; + } + break; + case ATT_READ_RESPONSE_PENDING: + // virtual handle indicating all attributes have been queried + printf("Read callback for virtual handle %02x - all attributes have been queried (important for read multiple or read by type) -> start updating values\n", att_handle); + // simulated delayed response for example + att_timer.process = &att_update_value; + btstack_run_loop_set_timer(&att_timer, ATT_VALUE_DELAY_MS); + btstack_run_loop_add_timer(&att_timer); + return 0; + default: + break; + } +#else + // useless code when ENABLE_ATT_DELAYED_READ_RESPONSE is not defined - but avoids built errors + if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE){ + printf("ENABLE_ATT_DELAYED_READ_RESPONSE not defined in btstack_config.h, responding right away"); + return att_read_callback_handle_blob((const uint8_t *)test_string, strlen(test_string), offset, buffer, buffer_size); + } +#endif + + return 0; +} +/* LISTING_END */ + +int btstack_main(void); +int btstack_main(void) +{ + example_setup(); + + // turn on! + hci_power_control(HCI_POWER_ON); + + return 0; +} +/* EXAMPLE_END */ diff --git a/example/att_delayed_read_response.gatt b/example/att_delayed_read_response.gatt new file mode 100644 index 000000000..c7a8399fa --- /dev/null +++ b/example/att_delayed_read_response.gatt @@ -0,0 +1,10 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "LE Counter" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_SERVICE_CHANGED, READ, + +// Counter Service +PRIMARY_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB +// Counter Characteristic, with read and notify +CHARACTERISTIC, 0000FF11-0000-1000-8000-00805F9B34FB, READ | NOTIFY | DYNAMIC, diff --git a/port/libusb/.gitignore b/port/libusb/.gitignore index e368072fb..1dbd6f313 100644 --- a/port/libusb/.gitignore +++ b/port/libusb/.gitignore @@ -62,3 +62,5 @@ spp_counter spp_streamer spp_streamer_client spp_streamer_client +att_delayed_read_response +att_delayed_read_response.h diff --git a/port/libusb/btstack_config.h b/port/libusb/btstack_config.h index d8b713289..7056a8244 100644 --- a/port/libusb/btstack_config.h +++ b/port/libusb/btstack_config.h @@ -21,6 +21,7 @@ #define ENABLE_LE_DATA_CHANNELS #define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS #define ENABLE_LE_DATA_LENGTH_EXTENSION +#define ENABLE_ATT_DELAYED_READ_RESPONSE #define ENABLE_LOG_ERROR #define ENABLE_LOG_INFO #define ENABLE_SCO_OVER_HCI