diff --git a/CHANGELOG.md b/CHANGELOG.md index de15c4e06..3e404894b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - HCI: ENABLE_HCI_SERIALIZED_CONTROLLER_OPERATIONS serializes Inquiry, Remote Name Request and Create Connection operations - GAP: support extended advertising with ENABLE_LE_EXTENDED_ADVERTISING +- GAP: gap_enable_link_watchdog() uses automatic flush timeout to disconnect if data cannot be sent - ATT DB: provide gatt_server_get_handle_range_for_service_with_uuid16 to find included service within handle range - GATT Service: Audio Input Control Service Server (AICS 1.0) - GATT Service: Volume Control Service Server (VCS 1.0) diff --git a/src/gap.h b/src/gap.h index 94e41073e..0f0704c40 100644 --- a/src/gap.h +++ b/src/gap.h @@ -270,6 +270,13 @@ void gap_set_allow_role_switch(bool allow_role_switch); */ void gap_set_link_supervision_timeout(uint16_t link_supervision_timeout); +/** + * @brief Enable link watchdog. If no ACL packet is sent within timeout_ms, the link will get disconnected + * note: current implementation uses the automatic flush timeout controller feature with a max timeout of 1.28s + * @param timeout_ms + */ +void gap_enable_link_watchdog(uint16_t timeout_ms); + /** * @brief Enable/disable bonding. Default is enabled. * @param enabled @@ -1021,7 +1028,7 @@ uint8_t gap_ssp_io_capabilities_negative(const bd_addr_t addr); /** * Send Link Key Reponse - * @note Link Key (Negative) Reply is sent automaticallyu unless ENABLE_EXPLICIT_LINK_KEY_RESPONSE + * @note Link Key (Negative) Reply is sent automatically unless ENABLE_EXPLICIT_LINK_KEY_RESPONSE * @param addr * @param link_key * @param type or INVALID_LINK_KEY if link key not available diff --git a/src/hci.c b/src/hci.c index aa4d1146e..aadbafa35 100644 --- a/src/hci.c +++ b/src/hci.c @@ -2887,6 +2887,16 @@ static void event_handler(uint8_t *packet, uint16_t size){ } #ifdef ENABLE_CLASSIC + case HCI_EVENT_FLUSH_OCCURRED: + // flush occurs only if automatic flush has been enabled by gap_enable_link_watchdog() + handle = hci_event_flush_occurred_get_handle(packet); + conn = hci_connection_for_handle(handle); + if (conn) { + log_info("Flush occurred, disconnect 0x%04x", handle); + conn->state = SEND_DISCONNECT; + } + break; + case HCI_EVENT_INQUIRY_COMPLETE: if (hci_stack->inquiry_state == GAP_INQUIRY_STATE_ACTIVE){ hci_stack->inquiry_state = GAP_INQUIRY_STATE_IDLE; @@ -2958,6 +2968,11 @@ static void event_handler(uint8_t *packet, uint16_t size){ connectionSetAuthenticationFlags(conn, AUTH_FLAG_WRITE_SUPERVISION_TIMEOUT); } + // queue write automatic flush timeout + if (hci_stack->automatic_flush_timeout != 0){ + conn->gap_connection_tasks |= GAP_CONNECTION_TASK_WRITE_AUTOMATIC_FLUSH_TIMEOUT; + } + // restart timer btstack_run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS); btstack_run_loop_add_timer(&conn->timeout); @@ -3987,6 +4002,14 @@ void gap_set_link_supervision_timeout(uint16_t link_supervision_timeout){ hci_stack->link_supervision_timeout = link_supervision_timeout; } +void gap_enable_link_watchdog(uint16_t timeout_ms){ + hci_stack->automatic_flush_timeout = btstack_min(timeout_ms, 1280) * 8 / 5; // divide by 0.625 +} + +uint16_t hci_automatic_flush_timeout(void){ + return hci_stack->automatic_flush_timeout; +} + void hci_disable_l2cap_timeout_check(void){ disable_l2cap_timeouts = 1; } @@ -5649,6 +5672,14 @@ static bool hci_run_general_pending_commands(void){ hci_send_cmd(&hci_switch_role_command, connection->address, role); return true; } + + if (connection->gap_connection_tasks != 0){ + if ((connection->gap_connection_tasks & GAP_CONNECTION_TASK_WRITE_AUTOMATIC_FLUSH_TIMEOUT) != 0){ + connection->gap_connection_tasks &= ~GAP_CONNECTION_TASK_WRITE_AUTOMATIC_FLUSH_TIMEOUT; + hci_send_cmd(&hci_write_automatic_flush_timeout, connection->con_handle, hci_stack->automatic_flush_timeout); + return true; + } + } #endif #ifdef ENABLE_BLE diff --git a/src/hci.h b/src/hci.h index 5fd646cab..6b09ed1c9 100644 --- a/src/hci.h +++ b/src/hci.h @@ -227,6 +227,9 @@ typedef enum { } hci_authentication_flags_t; +// GAP Connection Tasks +#define GAP_CONNECTION_TASK_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0001u + /** * Connection State */ @@ -596,7 +599,10 @@ typedef struct { #endif /* ENABLE_CLASSIC */ // authentication and other errands - uint32_t authentication_flags; + uint16_t authentication_flags; + + // gap connection tasks, see GAP_CONNECTION_TASK_x + uint16_t gap_connection_tasks; btstack_timer_source_t timeout; @@ -905,7 +911,10 @@ typedef struct { // Errata-11838 mandates 7 bytes for GAP Security Level 1-3, we use 16 as default uint8_t gap_required_encyrption_key_size; + uint16_t link_supervision_timeout; + uint16_t automatic_flush_timeout; + gap_security_level_t gap_security_level; gap_security_level_t gap_minimal_service_security_level; gap_security_mode_t gap_security_mode; @@ -1356,6 +1365,11 @@ uint16_t hci_usable_acl_packet_types(void); */ bool hci_non_flushable_packet_boundary_flag_supported(void); +/** + * Return current automatic flush timeout setting + */ +uint16_t hci_automatic_flush_timeout(void); + /** * Check if remote supported features query has completed */ diff --git a/src/l2cap.c b/src/l2cap.c index 338d292e8..a921df84b 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -1389,7 +1389,7 @@ static void l2cap_rtx_timeout(btstack_timer_source_t * ts){ } static uint8_t l2cap_classic_packet_boundary_flag(void){ - return hci_non_flushable_packet_boundary_flag_supported() ? 0x00 : 0x02; + return (hci_non_flushable_packet_boundary_flag_supported() && (hci_automatic_flush_timeout() == 0)) ? 0x00 : 0x02; } #endif