diff --git a/CHANGELOG.md b/CHANGELOG.md index 75dfdbc3c8..e3aca836f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - att_db_util: added security requirement arguments to characteristic creators - SM: use btstack_crypto for cryptographpic functions +- GAP: security level for Classic protocols (asides SDP) raised to 2 (encryption) ### Fixed - HFP: fix answer call command diff --git a/README.md b/README.md index d8432b7acb..aa02212516 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,16 @@ BTstack is free for non-commercial use. However, for commercial use, contact us. +It has been qualified with the Bluetooth SIG (QDID 110883) for GAP, IOP, HFP, HSP, SPP, PAN, A2DP, AVRCP profiles and +GATT, SM of the Bluetooth 5 specification. For information on MFi/iAP2 support, please contact us. ## Evaluation Platforms @@ -53,6 +53,7 @@ No build Server | [stm32-l053rb-em9304](https://github.com/bluekitchen/btstack/t [](https://buildbot.bluekitchen-gmbh.com/btstack/#/builders/port-wiced-h4-master) | [wiced-h4](https://github.com/bluekitchen/btstack/tree/master/port/wiced-h4) | Broadcom platforms that support the WICED SDK via H4 UART, e.g. [RedBear Duo](https://redbear.cc/product/wifi-ble/redbear-duo.html) with Broadcom BCM43438 A1 or the [Inventek Systems ISM4334x](https://www.inventeksys.com/products-page/wifi-modules/serial-wifi/ism43341-m4g-l44-cu-embedded-serial-to-wifi-ble-nfc-module/) with Broadcom BCM43340 No build server | [wiced-h5](https://github.com/bluekitchen/btstack/tree/master/port/wiced-h5) | Broadcom platforms that support the WICED SDK via H5 UART + #### Other Platforms: Status | Port | Platform -------------------| ------|--------- @@ -69,6 +70,7 @@ No build server | [windows-winusb](https://github.com/bluekitchen/btstack/tree/m [](https://buildbot.bluekitchen-gmbh.com/btstack/#/builders/port-ios-master) | [ios](https://github.com/bluekitchen/btstack/tree/master/port/ios) | daemon for iOS jailbreak devices, C client-server API No build server | [freertos](https://github.com/bluekitchen/btstack/tree/master/platform/freertos) | [FreeRTOS](http://www.freertos.org): Run BTstack on a dedicated thread, not thread-safe. + ## Supported Chipsets Chipset | Type | HCI Transport | SCO over HCI | BTstack folder | Comment diff --git a/src/ble/att_db.c b/src/ble/att_db.c index 101e6c08b9..eed1e985bb 100644 --- a/src/ble/att_db.c +++ b/src/ble/att_db.c @@ -1247,6 +1247,86 @@ uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uui return 0; } +// returns 1 if service found. only primary service. +int gatt_server_get_get_handle_range_for_service_with_uuid128(const uint8_t * uuid128, uint16_t * start_handle, uint16_t * end_handle){ + uint16_t in_group = 0; + uint16_t prev_handle = 0; + + uint8_t attribute_value[16]; + int attribute_len = sizeof(attribute_value); + reverse_128(uuid128, attribute_value); + + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + int new_service_started = att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID); + + // close current tag, if within a group and a new service definition starts or we reach end of att db + if (in_group && + (it.handle == 0 || new_service_started)){ + *end_handle = prev_handle; + return 1; + } + + // keep track of previous handle + prev_handle = it.handle; + + // check if found + if (it.handle && new_service_started && attribute_len == it.value_len && memcmp(attribute_value, it.value, it.value_len) == 0){ + *start_handle = it.handle; + in_group = 1; + } + } + return 0; +} + +// returns 0 if not found +uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){ + uint8_t attribute_value[16]; + reverse_128(uuid128, attribute_value); + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + if (it.handle && it.handle < start_handle) continue; + if (it.handle > end_handle) break; // (1) + if (it.handle == 0) break; + if (att_iterator_match_uuid(&it, attribute_value, 16)) return it.handle; + } + return 0; +} + +// returns 0 if not found +uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128){ + uint8_t attribute_value[16]; + reverse_128(uuid128, attribute_value); + att_iterator_t it; + att_iterator_init(&it); + int characteristic_found = 0; + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + if (it.handle && it.handle < start_handle) continue; + if (it.handle > end_handle) break; // (1) + if (it.handle == 0) break; + if (att_iterator_match_uuid(&it, attribute_value, 16)){ + characteristic_found = 1; + continue; + } + if (att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) + || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID) + || att_iterator_match_uuid16(&it, GATT_CHARACTERISTICS_UUID)){ + if (characteristic_found) break; + continue; + } + if (att_iterator_match_uuid16(&it, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION)){ + return it.handle; + } + } + return 0; +} + + // 1-item cache to optimize query during write_callback static void att_persistent_ccc_cache(att_iterator_t * it){ att_persistent_ccc_handle = it->handle; diff --git a/src/ble/att_db.h b/src/ble/att_db.h index 330d09daa5..0136263937 100644 --- a/src/ble/att_db.h +++ b/src/ble/att_db.h @@ -235,6 +235,16 @@ uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid16(uint16_t st // returns 0 if not found uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(uint16_t start_handle, uint16_t end_handle, uint16_t uuid16); + +// returns 1 if service found. only primary service. +int gatt_server_get_get_handle_range_for_service_with_uuid128(const uint8_t * uuid128, uint16_t * start_handle, uint16_t * end_handle); + +// returns 0 if not found +uint16_t gatt_server_get_value_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); + +// returns 0 if not found +uint16_t gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(uint16_t start_handle, uint16_t end_handle, const uint8_t * uuid128); + // non-user functionality for att_server /* diff --git a/src/ble/att_db_util.c b/src/ble/att_db_util.c index 24e46f8698..af64549e8d 100644 --- a/src/ble/att_db_util.c +++ b/src/ble/att_db_util.c @@ -181,13 +181,13 @@ static uint16_t att_db_util_encode_permissions(uint16_t properties, uint8_t read if (read_permission & 1){ flags |= ATT_PROPERTY_READ_PERMISSION_BIT_0; } - if (read_permission & 1){ + if (read_permission & 2){ flags |= ATT_PROPERTY_READ_PERMISSION_BIT_1; } if (write_permission & 1){ flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_0; } - if (write_permission & 1){ + if (write_permission & 2){ flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_1; } return flags; diff --git a/src/ble/att_server.c b/src/ble/att_server.c index 8f06867f8f..4c61407f18 100644 --- a/src/ble/att_server.c +++ b/src/ble/att_server.c @@ -856,10 +856,14 @@ void att_server_request_can_send_now_event(hci_con_handle_t con_handle){ } void att_server_register_can_send_now_callback(btstack_context_callback_registration_t * callback_registration, hci_con_handle_t con_handle){ - // check if can send already - if (att_dispatch_server_can_send_now(con_handle)){ - callback_registration->callback(callback_registration->context); - return; + // check if valid con handle + switch (gap_get_connection_type(con_handle)){ + case BD_ADDR_TYPE_LE_PUBLIC: + case BD_ADDR_TYPE_LE_RANDOM: + break; + default: + // con handle not valid for att send + return; } callback_registration->context = (void*)(uintptr_t) con_handle; btstack_linked_list_add_tail(&can_send_now_clients, (btstack_linked_item_t*) callback_registration); diff --git a/src/classic/avdtp_sink.c b/src/classic/avdtp_sink.c index 7214fa6fac..f9b5a99a46 100644 --- a/src/classic/avdtp_sink.c +++ b/src/classic/avdtp_sink.c @@ -106,7 +106,6 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe avdtp_packet_handler(packet_type, channel, packet, size, avdtp_sink_context); } -// TODO: find out which security level is needed, and replace LEVEL_0 in avdtp_sink_init void avdtp_sink_init(avdtp_context_t * avdtp_context){ if (!avdtp_context){ log_error("avdtp_source_context is NULL"); @@ -118,7 +117,7 @@ void avdtp_sink_init(avdtp_context_t * avdtp_context){ avdtp_sink_context->stream_endpoints_id_counter = 0; avdtp_sink_context->packet_handler = packet_handler; - l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_0); + l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_2); } avdtp_stream_endpoint_t * avdtp_sink_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type){ diff --git a/src/classic/avdtp_source.c b/src/classic/avdtp_source.c index 3993b38740..cf2ee73158 100644 --- a/src/classic/avdtp_source.c +++ b/src/classic/avdtp_source.c @@ -172,6 +172,6 @@ void avdtp_source_init(avdtp_context_t * avdtp_context){ avdtp_source_context->stream_endpoints_id_counter = 0; avdtp_source_context->packet_handler = packet_handler; - l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_0); + l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_2); } diff --git a/src/classic/avrcp.c b/src/classic/avrcp.c index d7ef79e324..4e1dbf624c 100644 --- a/src/classic/avrcp.c +++ b/src/classic/avrcp.c @@ -619,8 +619,8 @@ void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet connection = get_avrcp_connection_for_bd_addr(event_addr, context); if (!connection){ - log_error("Failed to alloc AVRCP connection structure"); - avrcp_emit_connection_established(context->avrcp_callback, connection->avrcp_cid, event_addr, BTSTACK_MEMORY_ALLOC_FAILED); + // TODO: validate if this cannot happen. If not, drop disconnect call + log_error("AVRCP connection lookup failed"); l2cap_disconnect(local_cid, 0); // reason isn't used break; } diff --git a/src/classic/avrcp_browsing_controller.c b/src/classic/avrcp_browsing_controller.c index a3c7f0191c..ed2bc41c29 100644 --- a/src/classic/avrcp_browsing_controller.c +++ b/src/classic/avrcp_browsing_controller.c @@ -733,7 +733,7 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16 void avrcp_browsing_controller_init(void){ avrcp_controller_context.browsing_packet_handler = avrcp_browsing_controller_packet_handler; - l2cap_register_service(&avrcp_browsing_controller_packet_handler, PSM_AVCTP_BROWSING, 0xffff, LEVEL_0); + l2cap_register_service(&avrcp_browsing_controller_packet_handler, PSM_AVCTP_BROWSING, 0xffff, LEVEL_2); } void avrcp_browsing_controller_register_packet_handler(btstack_packet_handler_t callback){ @@ -985,4 +985,4 @@ uint8_t avrcp_browsing_controller_get_total_nr_items_for_scope(uint16_t avrcp_br connection->get_total_nr_items_scope = scope; avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid); return ERROR_CODE_SUCCESS; -} \ No newline at end of file +} diff --git a/src/classic/avrcp_browsing_target.c b/src/classic/avrcp_browsing_target.c index 7dd28b3219..fd1db9496b 100644 --- a/src/classic/avrcp_browsing_target.c +++ b/src/classic/avrcp_browsing_target.c @@ -424,7 +424,7 @@ static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t c void avrcp_browsing_target_init(void){ avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler; - l2cap_register_service(&avrcp_browsing_target_packet_handler, PSM_AVCTP_BROWSING, 0xffff, LEVEL_0); + l2cap_register_service(&avrcp_browsing_target_packet_handler, PSM_AVCTP_BROWSING, 0xffff, LEVEL_2); } void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){ diff --git a/src/classic/avrcp_controller.c b/src/classic/avrcp_controller.c index de2429c9eb..e5fc88b1de 100644 --- a/src/classic/avrcp_controller.c +++ b/src/classic/avrcp_controller.c @@ -904,7 +904,7 @@ void avrcp_controller_init(void){ avrcp_controller_context.role = AVRCP_CONTROLLER; avrcp_controller_context.connections = NULL; avrcp_controller_context.packet_handler = avrcp_controller_packet_handler; - l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); + l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_2); } void avrcp_controller_register_packet_handler(btstack_packet_handler_t callback){ diff --git a/src/classic/avrcp_target.c b/src/classic/avrcp_target.c index 265e22bc4e..5f0c533fa2 100644 --- a/src/classic/avrcp_target.c +++ b/src/classic/avrcp_target.c @@ -1236,7 +1236,7 @@ void avrcp_target_init(void){ avrcp_target_context.role = AVRCP_TARGET; avrcp_target_context.connections = NULL; avrcp_target_context.packet_handler = avrcp_target_packet_handler; - l2cap_register_service(&avrcp_target_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0); + l2cap_register_service(&avrcp_target_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_2); } void avrcp_target_register_packet_handler(btstack_packet_handler_t callback){ diff --git a/src/classic/bnep.c b/src/classic/bnep.c index 3abe5bb80d..44bcc347b9 100644 --- a/src/classic/bnep.c +++ b/src/classic/bnep.c @@ -1537,7 +1537,7 @@ static void bnep_handle_can_send_now(uint16_t l2cap_cid){ /* BNEP BTStack API */ void bnep_init(void) { - bnep_security_level = LEVEL_0; + bnep_security_level = LEVEL_2; } void bnep_set_required_security_level(gap_security_level_t security_level) diff --git a/src/classic/hid_device.c b/src/classic/hid_device.c index fb7ddf91fc..c15732ea4c 100644 --- a/src/classic/hid_device.c +++ b/src/classic/hid_device.c @@ -329,8 +329,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack void hid_device_init(void){ memset(hid_device, 0, sizeof(hid_device_t)); hid_device->cid = 1; - l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_0); - l2cap_register_service(packet_handler, PSM_HID_CONTROL, 100, LEVEL_0); + l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_2); + l2cap_register_service(packet_handler, PSM_HID_CONTROL, 100, LEVEL_2); } /** diff --git a/src/hci.c b/src/hci.c index f7e2d36753..a62afca70b 100644 --- a/src/hci.c +++ b/src/hci.c @@ -4052,12 +4052,28 @@ void gap_request_security_level(hci_con_handle_t con_handle, gap_security_level_ return; } gap_security_level_t current_level = gap_security_level(con_handle); - log_info("gap_request_security_level %u, current level %u", requested_level, current_level); - if (current_level >= requested_level){ + log_info("gap_request_security_level requested level %u, planned level %u, current level %u", + requested_level, connection->requested_security_level, current_level); + + // assumption: earlier requested security higher than current level => security request is active + if (current_level < connection->requested_security_level){ + if (connection->requested_security_level < requested_level){ + // increase requested level as new level is higher + + // TODO: handle re-authentication when done + + connection->requested_security_level = requested_level; + } + return; + } + + // no request active, notify if security sufficient + if (requested_level <= current_level){ hci_emit_security_level(con_handle, current_level); return; } + // start pairing to increase security level connection->requested_security_level = requested_level; #if 0 @@ -4077,7 +4093,7 @@ void gap_request_security_level(hci_con_handle_t con_handle, gap_security_level_ } #endif - // try to authenticate connection + // start to authenticate connection connection->bonding_flags |= BONDING_SEND_AUTHENTICATE_REQUEST; hci_run(); } diff --git a/src/l2cap.c b/src/l2cap.c index c6d5797e47..8f6c94a01a 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -794,10 +794,12 @@ uint8_t *l2cap_get_outgoing_buffer(void){ return hci_get_outgoing_packet_buffer() + COMPLETE_L2CAP_HEADER; // 8 bytes } +// only for L2CAP Basic Channels int l2cap_reserve_packet_buffer(void){ return hci_reserve_packet_buffer(); } +// only for L2CAP Basic Channels void l2cap_release_packet_buffer(void){ hci_release_packet_buffer(); } @@ -1097,6 +1099,7 @@ static int l2cap_send_signaling_packet(hci_con_handle_t handle, L2CAP_SIGNALING_ } // assumption - only on Classic connections +// cannot be used for L2CAP ERTM int l2cap_send_prepared(uint16_t local_cid, uint16_t len){ if (!hci_is_packet_buffer_reserved()){ diff --git a/src/l2cap.h b/src/l2cap.h index ed4891aae0..527f76506c 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -523,21 +523,25 @@ void l2cap_request_can_send_now_event(uint16_t local_cid); /** * @brief Reserve outgoing buffer + * @note Only for L2CAP Basic Mode Channels */ int l2cap_reserve_packet_buffer(void); /** * @brief Get outgoing buffer and prepare data. + * @note Only for L2CAP Basic Mode Channels */ uint8_t *l2cap_get_outgoing_buffer(void); /** * @brief Send L2CAP packet prepared in outgoing buffer to channel + * @note Only for L2CAP Basic Mode Channels */ int l2cap_send_prepared(uint16_t local_cid, uint16_t len); /** * @brief Release outgoing buffer (only needed if l2cap_send_prepared is not called) + * @note Only for L2CAP Basic Mode Channels */ void l2cap_release_packet_buffer(void);