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);