diff --git a/doc/manual/docs/how_to.md b/doc/manual/docs/how_to.md index b8a83e154..ac1ea4bd5 100644 --- a/doc/manual/docs/how_to.md +++ b/doc/manual/docs/how_to.md @@ -85,7 +85,7 @@ ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS | Use [micro-ecc library](https://git ENABLE_LE_DATA_CHANNELS | Enable LE Data Channels in credit-based flow control mode ENABLE_LE_DATA_LENGTH_EXTENSION | Enable LE Data Length Extension support ENABLE_LE_SIGNED_WRITE | Enable LE Signed Writes in ATT/GATT -ENABLE_ATT_DELAYED_READ_RESPONSE | Enable support for delayed ATT Read operations, see [GATT Server](profiles/#sec:GATTServerProfile) +ENABLE_ATT_DELAYED_RESPONSE | Enable support for delayed ATT operations, see [GATT Server](profiles/#sec:GATTServerProfile) ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE | Enable L2CAP Enhanced Retransmission Mode. Mandatory for AVRCP Browsing ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL | Enable HCI Controller to Host Flow Control, see below ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND | Enable workaround for bug in CC256x Flow Control during baud rate change, see chipset docs. diff --git a/doc/manual/docs/profiles.md b/doc/manual/docs/profiles.md index 65881b093..57b954e57 100644 --- a/doc/manual/docs/profiles.md +++ b/doc/manual/docs/profiles.md @@ -469,7 +469,7 @@ To send a Notification, you can call *att_server_request_can_send_now* to receive a ATT_EVENT_CAN_SEND_NOW event. If your application cannot handle an ATT Read Request in the *att_read_callback* -in some situations, you can enable support for this by adding ENABLE_ATT_DELAYED_READ_RESPONSE +in some situations, you can enable support for this by adding ENABLE_ATT_DELAYED_RESPONSE to *btstack_config.h*. Now, you can store the requested attribute handle and return *ATT_READ_RESPONSE_PENDING* instead of the length of the provided data when you don't have the data ready. For ATT operations that read more than one attribute, your *att_read_callback* @@ -477,7 +477,7 @@ might get called multiple times as well. To let you know that all necessary attribute handles have been 'requested' by the *att_server*, you'll get a final *att_read_callback* with the attribute handle of *ATT_READ_RESPONSE_PENDING*. When you've got the data for all requested attributes ready, you can call -*att_server_read_response_ready*, which will trigger processing of the current request. +*att_server_response_ready*, which will trigger processing of the current request. Please keep in mind that there is only one active ATT operation and that it has a 30 second timeout after which the ATT server is considered defunct by the GATT Client. diff --git a/example/Makefile.inc b/example/Makefile.inc index d50431115..733ea5989 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -108,7 +108,7 @@ HXCMOD_PLAYER = \ ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c \ EXAMPLES = \ - att_delayed_read_response \ + att_delayed_response \ a2dp_sink_demo \ a2dp_source_demo \ ancs_client_demo \ @@ -207,8 +207,8 @@ 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 $@ +att_delayed_response: att_delayed_response.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} att_delayed_response.c + ${CC} $(filter-out att_delayed_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.gatt b/example/att_delayed_read_response.gatt deleted file mode 100644 index c7a8399fa..000000000 --- a/example/att_delayed_read_response.gatt +++ /dev/null @@ -1,10 +0,0 @@ -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/example/att_delayed_read_response.c b/example/att_delayed_response.c similarity index 74% rename from example/att_delayed_read_response.c rename to example/att_delayed_response.c index 58035a06a..3bde9c51a 100644 --- a/example/att_delayed_read_response.c +++ b/example/att_delayed_response.c @@ -35,14 +35,18 @@ * */ -#define __BTSTACK_FILE__ "att_delayed_read_response.c" +#define __BTSTACK_FILE__ "att_delayed_response.c" // ***************************************************************************** -/* EXAMPLE_START(att_delayed_read_response): LE Peripheral - Delayed Read Response +/* EXAMPLE_START(att_delayed_response): LE Peripheral - Delayed 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. + * @text If the value for a GATT Chararacteristic isn't availabl for read, + * the value ATT_READ_RESPONSE_PENDING can be returned. When the value is available, + * att_server_response_ready is then called to complete the ATT request. + * + * Similarly, the error code ATT_ERROR_WRITE_RESPONSE_PENING can be returned when + * it is unclear if a write can be performed or not. When the decision was made, + * att_server_response_ready is is then called to complete the ATT request. */ // ***************************************************************************** @@ -51,7 +55,7 @@ #include #include -#include "att_delayed_read_response.h" +#include "att_delayed_response.h" #include "btstack.h" #define ATT_VALUE_DELAY_MS 3000 @@ -69,19 +73,20 @@ */ /* LISTING_START(MainConfiguration): Init L2CAP SM ATT Server */ -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE +#ifdef ENABLE_ATT_DELAYED_RESPONSE static btstack_timer_source_t att_timer; static hci_con_handle_t con_handle; static int value_ready; #endif 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); +static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, 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', + 0x08, 0x09, 'D', 'e', 'l', 'a', 'y', 'e', 'd', }; const uint8_t adv_data_len = sizeof(adv_data); @@ -98,7 +103,7 @@ static void example_setup(void){ sm_init(); // setup ATT server - att_server_init(profile_data, att_read_callback, NULL); + att_server_init(profile_data, att_read_callback, att_write_callback); // setup advertisements uint16_t adv_int_min = 0x0030; @@ -117,7 +122,7 @@ static void example_setup(void){ * * @text The att_invalidate_value handler 'invalidates' the value of the single Characteristic provided in this example */ -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE +#ifdef ENABLE_ATT_DELAYED_RESPONSE static void att_invalidate_value(struct btstack_timer_source *ts){ UNUSED(ts); printf("Value got stale\n"); @@ -132,14 +137,13 @@ static void att_invalidate_value(struct btstack_timer_source *ts){ */ /* LISTING_START(att_read_delay): ATT Read Delay Handler */ -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE +#ifdef ENABLE_ATT_DELAYED_RESPONSE 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); + int status = att_server_response_ready(con_handle); printf("Value updated -> complete ATT request - status %02x\n", status); @@ -169,7 +173,7 @@ static void att_update_value(struct btstack_timer_source *ts){ // @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 +#ifdef ENABLE_ATT_DELAYED_RESPONSE switch (att_handle){ case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE: if (value_ready){ @@ -193,15 +197,50 @@ static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t a } #else UNUSED(connection_handle); - // useless code when ENABLE_ATT_DELAYED_READ_RESPONSE is not defined - but avoids built errors + // useless code when ENABLE_ATT_DELAYED_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"); + printf("ENABLE_ATT_DELAYED_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; } + +/* + * @section ATT Write + * */ + +/* LISTING_START(attWrite): ATT Write */ +static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ + UNUSED(transaction_mode); + UNUSED(offset); + UNUSED(buffer_size); + UNUSED(connection_handle); + + if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE) { + printf("Write request, value: "); + printf_hexdump(buffer, buffer_size); +#ifdef ENABLE_ATT_DELAYED_RESPONSE + if (value_ready){ + printf("Write callback, value ready\n"); + return 0; + } else { + printf("Write callback for handle %02x, but not ready -> return response pending\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 ATT_ERROR_WRITE_RESPONSE_PENDING; +#else + printf("ENABLE_ATT_DELAYED_RESPONSE not defined in btstack_config.h, responding right away"); + return 0; +#endif + } + return 0; +} + /* LISTING_END */ int btstack_main(void); diff --git a/example/att_delayed_response.gatt b/example/att_delayed_response.gatt new file mode 100644 index 000000000..268db34a4 --- /dev/null +++ b/example/att_delayed_response.gatt @@ -0,0 +1,10 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "Delayed Response" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_SERVICE_CHANGED, READ, + +// Some Service +PRIMARY_SERVICE, 0000FF10-0000-1000-8000-00805F9B34FB +// Some Characteristic, with read and write +CHARACTERISTIC, 0000FF11-0000-1000-8000-00805F9B34FB, READ | WRITE | DYNAMIC, diff --git a/port/libusb/.gitignore b/port/libusb/.gitignore index 1dbd6f313..99852154a 100644 --- a/port/libusb/.gitignore +++ b/port/libusb/.gitignore @@ -61,6 +61,5 @@ spp_and_le_streamer.h spp_counter spp_streamer spp_streamer_client -spp_streamer_client -att_delayed_read_response -att_delayed_read_response.h +att_delayed_response +att_delayed_response.h diff --git a/port/libusb/btstack_config.h b/port/libusb/btstack_config.h index 7056a8244..66d77ad64 100644 --- a/port/libusb/btstack_config.h +++ b/port/libusb/btstack_config.h @@ -21,7 +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_ATT_DELAYED_RESPONSE #define ENABLE_LOG_ERROR #define ENABLE_LOG_INFO #define ENABLE_SCO_OVER_HCI diff --git a/port/posix-h4-da14581/.gitignore b/port/posix-h4-da14581/.gitignore index eb6db3145..e0485af4e 100644 --- a/port/posix-h4-da14581/.gitignore +++ b/port/posix-h4-da14581/.gitignore @@ -47,5 +47,5 @@ le_streamer_client pbap_client_demo sm_pairing_central sm_pairing_peripheral - +att_delayed_read_response att_delayed_read_response.h diff --git a/port/posix-h4-da14585/.gitignore b/port/posix-h4-da14585/.gitignore index eb6db3145..ec221219e 100644 --- a/port/posix-h4-da14585/.gitignore +++ b/port/posix-h4-da14585/.gitignore @@ -1,5 +1,6 @@ ancs_client_demo ancs_client_demo.h +att_delayed_response.h ble_central_test ble_peripheral ble_peripheral_sm_minimal @@ -16,7 +17,9 @@ gap_inquiry gap_inquiry_and_bond gap_le_advertisements gatt_battery_query +gatt_battery_query.h gatt_browser +gatt_browser.h hfp_ag_demo hfp_hf_demo hsp_ag_demo @@ -28,12 +31,16 @@ le_counter le_counter.h le_streamer le_streamer.h +le_streamer_client led_counter +pbap_client_demo profile.h sco_input.wav sdp_bnep_query sdp_general_query sdp_rfcomm_query +sm_pairing_central +sm_pairing_peripheral sm_pairing_peripheral.h spp_and_le_counter spp_and_le_counter.h @@ -41,11 +48,3 @@ spp_counter spp_streamer TIInit_12.10.28.c TIInit_12.8.32.c -gatt_battery_query.h -gatt_browser.h -le_streamer_client -pbap_client_demo -sm_pairing_central -sm_pairing_peripheral - -att_delayed_read_response.h diff --git a/port/posix-h4/btstack_config.h b/port/posix-h4/btstack_config.h index 3e1d2191c..c80506ea3 100644 --- a/port/posix-h4/btstack_config.h +++ b/port/posix-h4/btstack_config.h @@ -22,7 +22,7 @@ #define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS #define ENABLE_LE_DATA_CHANNELS #define ENABLE_LE_DATA_LENGTH_EXTENSION -#define ENABLE_ATT_DELAYED_READ_RESPONSE +#define ENABLE_ATT_DELAYED_RESPONSE #define ENABLE_LOG_ERROR #define ENABLE_LOG_INFO #define ENABLE_SCO_OVER_HCI diff --git a/src/ble/att_db.h b/src/ble/att_db.h index eb39516e3..bcb745d37 100644 --- a/src/ble/att_db.h +++ b/src/ble/att_db.h @@ -54,8 +54,9 @@ extern "C" { // custom BTstack ATT error codes #define ATT_ERROR_DATA_MISMATCH 0x7e #define ATT_ERROR_TIMEOUT 0x7F +#define ATT_ERROR_WRITE_RESPONSE_PENDING 0x100 -// custom BTstack ATT Response Pending +// custom BTstack ATT Response Pending for att_read_callback #define ATT_READ_RESPONSE_PENDING 0xffff typedef struct att_connection { diff --git a/src/ble/att_server.c b/src/ble/att_server.c index 7f2fcbb2f..e8080f2f3 100644 --- a/src/ble/att_server.c +++ b/src/ble/att_server.c @@ -386,10 +386,10 @@ static int att_server_process_validated_request(att_server_t * att_server){ uint8_t * att_response_buffer = l2cap_get_outgoing_buffer(); uint16_t att_response_size = att_handle_request(&att_server->connection, att_server->request_buffer, att_server->request_size, att_response_buffer); -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE +#ifdef ENABLE_ATT_DELAYED_RESPONSE if (att_response_size == ATT_READ_RESPONSE_PENDING){ // update state - att_server->state = ATT_SERVER_READ_RESPONSE_PENDING; + att_server->state = ATT_SERVER_RESPONSE_PENDING; // callback with handle ATT_READ_RESPONSE_PENDING att_server_client_read_callback(att_server->connection.con_handle, ATT_READ_RESPONSE_PENDING, 0, NULL, 0); @@ -434,11 +434,11 @@ static int att_server_process_validated_request(att_server_t * att_server){ return 1; } -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE -int att_server_read_response_ready(hci_con_handle_t con_handle){ +#ifdef ENABLE_ATT_DELAYED_RESPONSE +int att_server_response_ready(hci_con_handle_t con_handle){ att_server_t * att_server = att_server_for_handle(con_handle); - if (!att_server) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; - if (att_server->state != ATT_SERVER_READ_RESPONSE_PENDING) return ERROR_CODE_COMMAND_DISALLOWED; + if (!att_server) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + if (att_server->state != ATT_SERVER_RESPONSE_PENDING) return ERROR_CODE_COMMAND_DISALLOWED; att_server->state = ATT_SERVER_REQUEST_RECEIVED_AND_VALIDATED; att_dispatch_server_request_can_send_now_event(con_handle); diff --git a/src/ble/att_server.h b/src/ble/att_server.h index 55e4a37c0..c70bed378 100644 --- a/src/ble/att_server.h +++ b/src/ble/att_server.h @@ -135,14 +135,15 @@ int att_server_notify(hci_con_handle_t con_handle, uint16_t attribute_handle, ui */ int att_server_indicate(hci_con_handle_t con_handle, uint16_t attribute_handle, uint8_t *value, uint16_t value_len); -#ifdef ENABLE_ATT_DELAYED_READ_RESPONSE +#ifdef ENABLE_ATT_DELAYED_RESPONSE /* - * @brief read response ready - called after returning ATT_READ_RESPONSE_PENDING in an att_read_callback before + * @brief response ready - called after returning ATT_READ__RESPONSE_PENDING in an att_read_callback or + * ATT_ERROR_WRITE_REQUEST_PENDING IN att_write_callback before to trigger callback again and complete the transaction * @nore The ATT Server will retry handling the current ATT request * @param con_handle * @return 0 if ok, error otherwise */ -int att_server_read_response_ready(hci_con_handle_t con_handle); +int att_server_response_ready(hci_con_handle_t con_handle); #endif /* API_END */ diff --git a/src/hci.h b/src/hci.h index c29e8ccd3..3e7926157 100644 --- a/src/hci.h +++ b/src/hci.h @@ -410,7 +410,7 @@ typedef enum { ATT_SERVER_REQUEST_RECEIVED, ATT_SERVER_W4_SIGNED_WRITE_VALIDATION, ATT_SERVER_REQUEST_RECEIVED_AND_VALIDATED, - ATT_SERVER_READ_RESPONSE_PENDING, + ATT_SERVER_RESPONSE_PENDING, } att_server_state_t; typedef struct {