gap: local name, EIR data, class of device and default link policy can be updated at any time

This commit is contained in:
Matthias Ringwald 2021-09-10 15:50:30 +02:00
parent cdf930c348
commit 59d59ecfa4
4 changed files with 130 additions and 94 deletions

View File

@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed ### Changed
- HCI: provide status instead of undocumented int error code and bool for API functions - HCI: provide status instead of undocumented int error code and bool for API functions
- GAP: local name, EIR data, class of device and default link policy can be updated at any time
- L2CAP: provide status instead of undocumented int error code and bool for API functions - L2CAP: provide status instead of undocumented int error code and bool for API functions
- RFCOMM: `RFCOMM_EVENT_PORT_CONFIGURATION` contains rfcomm_cid and remote flag, emitted for query config - RFCOMM: `RFCOMM_EVENT_PORT_CONFIGURATION` contains rfcomm_cid and remote flag, emitted for query config
- RFCOMM: provide status instead of undocumented int error code and bool for API functions - RFCOMM: provide status instead of undocumented int error code and bool for API functions

View File

@ -184,27 +184,24 @@ hci_role_t gap_get_role(hci_con_handle_t connection_handle);
*/ */
uint8_t gap_request_role(const bd_addr_t addr, hci_role_t role); uint8_t gap_request_role(const bd_addr_t addr, hci_role_t role);
/** /**
* @brief Sets local name. * @brief Sets local name.
* @note has to be done before stack starts up
* @note Default name is 'BTstack 00:00:00:00:00:00' * @note Default name is 'BTstack 00:00:00:00:00:00'
* @note '00:00:00:00:00:00' in local_name will be replaced with actual bd addr * @note '00:00:00:00:00:00' in local_name will be replaced with actual bd addr
* @param name is not copied, make sure memory is accessible during stack startup * @param name is not copied, make sure memory stays valid
*/ */
void gap_set_local_name(const char * local_name); void gap_set_local_name(const char * local_name);
/** /**
* @brief Set Extended Inquiry Response data * @brief Set Extended Inquiry Response data
* @note has to be done before stack starts up
* @note If not set, local name will be used for EIR data (see gap_set_local_name) * @note If not set, local name will be used for EIR data (see gap_set_local_name)
* @note '00:00:00:00:00:00' in local_name will be replaced with actual bd addr * @note '00:00:00:00:00:00' in local_name will be replaced with actual bd addr
* @param eir_data size HCI_EXTENDED_INQUIRY_RESPONSE_DATA_LEN (240) bytes, is not copied make sure memory is accessible during stack startup * @param eir_data size HCI_EXTENDED_INQUIRY_RESPONSE_DATA_LEN (240) bytes, is not copied make sure memory stays valid
*/ */
void gap_set_extended_inquiry_response(const uint8_t * data); void gap_set_extended_inquiry_response(const uint8_t * data);
/** /**
* @brief Set class of device that will be set during Bluetooth init. * @brief Set class of device
* @note has to be done before stack starts up
*/ */
void gap_set_class_of_device(uint32_t class_of_device); void gap_set_class_of_device(uint32_t class_of_device);
@ -212,7 +209,6 @@ void gap_set_class_of_device(uint32_t class_of_device);
* @brief Set default link policy settings for all classic ACL links * @brief Set default link policy settings for all classic ACL links
* @param default_link_policy_settings - see LM_LINK_POLICY_* in bluetooth.h * @param default_link_policy_settings - see LM_LINK_POLICY_* in bluetooth.h
* @note common value: LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE to enable role switch and sniff mode * @note common value: LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE to enable role switch and sniff mode
* @note has to be done before stack starts up
*/ */
void gap_set_default_link_policy_settings(uint16_t default_link_policy_settings); void gap_set_default_link_policy_settings(uint16_t default_link_policy_settings);

193
src/hci.c
View File

@ -1228,6 +1228,68 @@ static void hci_update_advertisements_enabled_for_current_roles(void){
#endif #endif
#endif #endif
#ifdef ENABLE_CLASSIC
static void gap_run_set_local_name(void){
hci_reserve_packet_buffer();
uint8_t * packet = hci_stack->hci_packet_buffer;
// construct HCI Command and send
uint16_t opcode = hci_write_local_name.opcode;
hci_stack->last_cmd_opcode = opcode;
packet[0] = opcode & 0xff;
packet[1] = opcode >> 8;
packet[2] = DEVICE_NAME_LEN;
memset(&packet[3], 0, DEVICE_NAME_LEN);
uint16_t name_len = (uint16_t) strlen(hci_stack->local_name);
uint16_t bytes_to_copy = btstack_min(name_len, DEVICE_NAME_LEN);
// if shorter than DEVICE_NAME_LEN, it's implicitly NULL-terminated by memset call
(void)memcpy(&packet[3], hci_stack->local_name, bytes_to_copy);
// expand '00:00:00:00:00:00' in name with bd_addr
btstack_replace_bd_addr_placeholder(&packet[3], bytes_to_copy, hci_stack->local_bd_addr);
hci_send_cmd_packet(packet, HCI_CMD_HEADER_SIZE + DEVICE_NAME_LEN);
}
static void gap_run_set_eir_data(void){
hci_reserve_packet_buffer();
uint8_t * packet = hci_stack->hci_packet_buffer;
// construct HCI Command in-place and send
uint16_t opcode = hci_write_extended_inquiry_response.opcode;
hci_stack->last_cmd_opcode = opcode;
uint16_t offset = 0;
packet[offset++] = opcode & 0xff;
packet[offset++] = opcode >> 8;
packet[offset++] = 1 + EXTENDED_INQUIRY_RESPONSE_DATA_LEN;
packet[offset++] = 0; // FEC not required
memset(&packet[offset], 0, EXTENDED_INQUIRY_RESPONSE_DATA_LEN);
if (hci_stack->eir_data){
// copy items and expand '00:00:00:00:00:00' in name with bd_addr
ad_context_t context;
for (ad_iterator_init(&context, EXTENDED_INQUIRY_RESPONSE_DATA_LEN, hci_stack->eir_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t size = ad_iterator_get_data_len(&context);
const uint8_t *data = ad_iterator_get_data(&context);
// copy item
packet[offset++] = size + 1;
packet[offset++] = data_type;
memcpy(&packet[offset], data, size);
// update name item
if ((data_type == BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME) || (data_type == BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME)){
btstack_replace_bd_addr_placeholder(&packet[offset], size, hci_stack->local_bd_addr);
}
offset += size;
}
} else {
uint16_t name_len = (uint16_t) strlen(hci_stack->local_name);
uint16_t bytes_to_copy = btstack_min(name_len, EXTENDED_INQUIRY_RESPONSE_DATA_LEN - 2);
packet[offset++] = bytes_to_copy + 1;
packet[offset++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME;
(void)memcpy(&packet[6], hci_stack->local_name, bytes_to_copy);
// expand '00:00:00:00:00:00' in name with bd_addr
btstack_replace_bd_addr_placeholder(&packet[offset], bytes_to_copy, hci_stack->local_bd_addr);
}
hci_send_cmd_packet(packet, HCI_CMD_HEADER_SIZE + 1 + EXTENDED_INQUIRY_RESPONSE_DATA_LEN);
}
#endif
#if !defined(HAVE_PLATFORM_IPHONE_OS) && !defined (HAVE_HOST_CONTROLLER_API) #if !defined(HAVE_PLATFORM_IPHONE_OS) && !defined (HAVE_HOST_CONTROLLER_API)
static uint32_t hci_transport_uart_get_main_baud_rate(void){ static uint32_t hci_transport_uart_get_main_baud_rate(void){
@ -1490,76 +1552,6 @@ static void hci_initializing_run(void){
hci_stack->substate = HCI_INIT_W4_WRITE_PAGE_TIMEOUT; hci_stack->substate = HCI_INIT_W4_WRITE_PAGE_TIMEOUT;
hci_send_cmd(&hci_write_page_timeout, 0x6000); // ca. 15 sec hci_send_cmd(&hci_write_page_timeout, 0x6000); // ca. 15 sec
break; break;
case HCI_INIT_WRITE_DEFAULT_LINK_POLICY_SETTING:
hci_stack->substate = HCI_INIT_W4_WRITE_DEFAULT_LINK_POLICY_SETTING;
hci_send_cmd(&hci_write_default_link_policy_setting, hci_stack->default_link_policy_settings);
break;
case HCI_INIT_WRITE_CLASS_OF_DEVICE:
hci_stack->substate = HCI_INIT_W4_WRITE_CLASS_OF_DEVICE;
hci_send_cmd(&hci_write_class_of_device, hci_stack->class_of_device);
break;
case HCI_INIT_WRITE_LOCAL_NAME: {
hci_stack->substate = HCI_INIT_W4_WRITE_LOCAL_NAME;
hci_reserve_packet_buffer();
uint8_t * packet = hci_stack->hci_packet_buffer;
// construct HCI Command and send
uint16_t opcode = hci_write_local_name.opcode;
hci_stack->last_cmd_opcode = opcode;
packet[0] = opcode & 0xff;
packet[1] = opcode >> 8;
packet[2] = DEVICE_NAME_LEN;
memset(&packet[3], 0, DEVICE_NAME_LEN);
uint16_t name_len = (uint16_t) strlen(hci_stack->local_name);
uint16_t bytes_to_copy = btstack_min(name_len, DEVICE_NAME_LEN);
// if shorter than DEVICE_NAME_LEN, it's implicitly NULL-terminated by memset call
(void)memcpy(&packet[3], hci_stack->local_name, bytes_to_copy);
// expand '00:00:00:00:00:00' in name with bd_addr
btstack_replace_bd_addr_placeholder(&packet[3], bytes_to_copy, hci_stack->local_bd_addr);
hci_send_cmd_packet(packet, HCI_CMD_HEADER_SIZE + DEVICE_NAME_LEN);
break;
}
case HCI_INIT_WRITE_EIR_DATA: {
hci_stack->substate = HCI_INIT_W4_WRITE_EIR_DATA;
hci_reserve_packet_buffer();
uint8_t * packet = hci_stack->hci_packet_buffer;
// construct HCI Command in-place and send
uint16_t opcode = hci_write_extended_inquiry_response.opcode;
hci_stack->last_cmd_opcode = opcode;
uint16_t offset = 0;
packet[offset++] = opcode & 0xff;
packet[offset++] = opcode >> 8;
packet[offset++] = 1 + EXTENDED_INQUIRY_RESPONSE_DATA_LEN;
packet[offset++] = 0; // FEC not required
memset(&packet[offset], 0, EXTENDED_INQUIRY_RESPONSE_DATA_LEN);
if (hci_stack->eir_data){
// copy items and expand '00:00:00:00:00:00' in name with bd_addr
ad_context_t context;
for (ad_iterator_init(&context, EXTENDED_INQUIRY_RESPONSE_DATA_LEN, hci_stack->eir_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t size = ad_iterator_get_data_len(&context);
const uint8_t *data = ad_iterator_get_data(&context);
// copy item
packet[offset++] = size + 1;
packet[offset++] = data_type;
memcpy(&packet[offset], data, size);
// update name item
if ((data_type == BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME) || (data_type == BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME)){
btstack_replace_bd_addr_placeholder(&packet[offset], size, hci_stack->local_bd_addr);
}
offset += size;
}
} else {
uint16_t name_len = (uint16_t) strlen(hci_stack->local_name);
uint16_t bytes_to_copy = btstack_min(name_len, EXTENDED_INQUIRY_RESPONSE_DATA_LEN - 2);
packet[offset++] = bytes_to_copy + 1;
packet[offset++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME;
(void)memcpy(&packet[6], hci_stack->local_name, bytes_to_copy);
// expand '00:00:00:00:00:00' in name with bd_addr
btstack_replace_bd_addr_placeholder(&packet[offset], bytes_to_copy, hci_stack->local_bd_addr);
}
hci_send_cmd_packet(packet, HCI_CMD_HEADER_SIZE + 1 + EXTENDED_INQUIRY_RESPONSE_DATA_LEN);
break;
}
case HCI_INIT_WRITE_INQUIRY_MODE: case HCI_INIT_WRITE_INQUIRY_MODE:
hci_stack->substate = HCI_INIT_W4_WRITE_INQUIRY_MODE; hci_stack->substate = HCI_INIT_W4_WRITE_INQUIRY_MODE;
hci_send_cmd(&hci_write_inquiry_mode, (int) hci_stack->inquiry_mode); hci_send_cmd(&hci_write_inquiry_mode, (int) hci_stack->inquiry_mode);
@ -3399,6 +3391,11 @@ static void hci_state_reset(void){
hci_stack->new_page_scan_window = 0xffff; hci_stack->new_page_scan_window = 0xffff;
hci_stack->new_page_scan_type = 0xff; hci_stack->new_page_scan_type = 0xff;
hci_stack->inquiry_lap = GAP_IAC_GENERAL_INQUIRY; hci_stack->inquiry_lap = GAP_IAC_GENERAL_INQUIRY;
hci_stack->gap_tasks =
GAP_TASK_SET_DEFAULT_LINK_POLICY |
GAP_TASK_SET_CLASS_OF_DEVICE |
GAP_TASK_SET_LOCAL_NAME |
GAP_TASK_SET_EIR_DATA;
#endif #endif
#ifdef ENABLE_CLASSIC_PAIRING_OOB #ifdef ENABLE_CLASSIC_PAIRING_OOB
@ -3659,10 +3656,14 @@ bool gap_get_secure_connections_only_mode(void){
#ifdef ENABLE_CLASSIC #ifdef ENABLE_CLASSIC
void gap_set_class_of_device(uint32_t class_of_device){ void gap_set_class_of_device(uint32_t class_of_device){
hci_stack->class_of_device = class_of_device; hci_stack->class_of_device = class_of_device;
hci_stack->gap_tasks |= GAP_TASK_SET_CLASS_OF_DEVICE;
hci_run();
} }
void gap_set_default_link_policy_settings(uint16_t default_link_policy_settings){ void gap_set_default_link_policy_settings(uint16_t default_link_policy_settings){
hci_stack->default_link_policy_settings = default_link_policy_settings; hci_stack->default_link_policy_settings = default_link_policy_settings;
hci_stack->gap_tasks |= GAP_TASK_SET_DEFAULT_LINK_POLICY;
hci_run();
} }
void gap_set_allow_role_switch(bool allow_role_switch){ void gap_set_allow_role_switch(bool allow_role_switch){
@ -4103,6 +4104,10 @@ static bool hci_run_acl_fragments(void){
#ifdef ENABLE_CLASSIC #ifdef ENABLE_CLASSIC
static bool hci_run_general_gap_classic(void){ static bool hci_run_general_gap_classic(void){
// assert stack is working and classic is active
if (hci_classic_supported() == false) return false;
if (hci_stack->state != HCI_STATE_WORKING) return false;
// decline incoming connections // decline incoming connections
if (hci_stack->decline_reason){ if (hci_stack->decline_reason){
uint8_t reason = hci_stack->decline_reason; uint8_t reason = hci_stack->decline_reason;
@ -4110,25 +4115,51 @@ static bool hci_run_general_gap_classic(void){
hci_send_cmd(&hci_reject_connection_request, hci_stack->decline_addr, reason); hci_send_cmd(&hci_reject_connection_request, hci_stack->decline_addr, reason);
return true; return true;
} }
if ((hci_stack->gap_tasks & GAP_TASK_SET_CLASS_OF_DEVICE) != 0) {
hci_stack->gap_tasks &= ~GAP_TASK_SET_CLASS_OF_DEVICE;
hci_send_cmd(&hci_write_class_of_device, hci_stack->class_of_device);
return true;
}
if ((hci_stack->gap_tasks & GAP_TASK_SET_LOCAL_NAME) != 0) {
hci_stack->gap_tasks &= ~GAP_TASK_SET_LOCAL_NAME;
gap_run_set_local_name();
return true;
}
if ((hci_stack->gap_tasks & GAP_TASK_SET_EIR_DATA) != 0) {
hci_stack->gap_tasks &= ~GAP_TASK_SET_EIR_DATA;
gap_run_set_eir_data();
return true;
}
if ((hci_stack->gap_tasks & GAP_TASK_SET_DEFAULT_LINK_POLICY) != 0) {
hci_stack->gap_tasks &= ~GAP_TASK_SET_DEFAULT_LINK_POLICY;
hci_send_cmd(&hci_write_default_link_policy_setting, hci_stack->default_link_policy_settings);
return true;
}
// write page scan activity // write page scan activity
if ((hci_stack->state == HCI_STATE_WORKING) && (hci_stack->new_page_scan_interval != 0xffff) && hci_classic_supported()){ if (hci_stack->new_page_scan_interval != 0xffff) {
hci_send_cmd(&hci_write_page_scan_activity, hci_stack->new_page_scan_interval, hci_stack->new_page_scan_window); uint16_t new_page_scan_interval = hci_stack->new_page_scan_interval;
uint16_t new_page_scan_window = hci_stack->new_page_scan_window;
hci_stack->new_page_scan_interval = 0xffff; hci_stack->new_page_scan_interval = 0xffff;
hci_stack->new_page_scan_window = 0xffff; hci_stack->new_page_scan_window = 0xffff;
hci_send_cmd(&hci_write_page_scan_activity, new_page_scan_interval, new_page_scan_window);
return true; return true;
} }
// write page scan type // write page scan type
if ((hci_stack->state == HCI_STATE_WORKING) && (hci_stack->new_page_scan_type != 0xff) && hci_classic_supported()){ if (hci_stack->new_page_scan_type != 0xff) {
hci_send_cmd(&hci_write_page_scan_type, hci_stack->new_page_scan_type); uint8_t new_page_scan_type = hci_stack->new_page_scan_type;
hci_stack->new_page_scan_type = 0xff; hci_stack->new_page_scan_type = 0xff;
hci_send_cmd(&hci_write_page_scan_type, new_page_scan_type);
return true; return true;
} }
// send scan enable // send scan enable
if ((hci_stack->state == HCI_STATE_WORKING) && (hci_stack->new_scan_enable_value != 0xff) && hci_classic_supported()){ if (hci_stack->new_scan_enable_value != 0xff) {
hci_send_cmd(&hci_write_scan_enable, hci_stack->new_scan_enable_value); uint8_t new_scan_enable_value = hci_stack->new_scan_enable_value;
hci_stack->new_scan_enable_value = 0xff; hci_stack->new_scan_enable_value = 0xff;
hci_send_cmd(&hci_write_scan_enable, new_scan_enable_value);
return true; return true;
} }
// start/stop inquiry // start/stop inquiry
if ((hci_stack->inquiry_state >= GAP_INQUIRY_DURATION_MIN) && (hci_stack->inquiry_state <= GAP_INQUIRY_DURATION_MAX)){ if ((hci_stack->inquiry_state >= GAP_INQUIRY_DURATION_MIN) && (hci_stack->inquiry_state <= GAP_INQUIRY_DURATION_MAX)){
uint8_t duration = hci_stack->inquiry_state; uint8_t duration = hci_stack->inquiry_state;
@ -4150,7 +4181,7 @@ static bool hci_run_general_gap_classic(void){
} }
#ifdef ENABLE_CLASSIC_PAIRING_OOB #ifdef ENABLE_CLASSIC_PAIRING_OOB
// Local OOB data // Local OOB data
if ((hci_stack->state == HCI_STATE_WORKING) && hci_stack->classic_read_local_oob_data){ if (hci_stack->classic_read_local_oob_data){
hci_stack->classic_read_local_oob_data = false; hci_stack->classic_read_local_oob_data = false;
if (hci_stack->local_supported_commands[1] & 0x10u){ if (hci_stack->local_supported_commands[1] & 0x10u){
hci_send_cmd(&hci_read_local_extended_oob_data); hci_send_cmd(&hci_read_local_extended_oob_data);
@ -5750,6 +5781,12 @@ int gap_dedicated_bonding(bd_addr_t device, int mitm_protection_required){
void gap_set_local_name(const char * local_name){ void gap_set_local_name(const char * local_name){
hci_stack->local_name = local_name; hci_stack->local_name = local_name;
hci_stack->gap_tasks |= GAP_TASK_SET_LOCAL_NAME;
// also update EIR if not set by user
if (hci_stack->eir_data == NULL){
hci_stack->gap_tasks |= GAP_TASK_SET_EIR_DATA;
}
hci_run();
} }
@ -6302,6 +6339,8 @@ uint16_t gap_le_connection_interval(hci_con_handle_t con_handle){
*/ */
void gap_set_extended_inquiry_response(const uint8_t * data){ void gap_set_extended_inquiry_response(const uint8_t * data){
hci_stack->eir_data = data; hci_stack->eir_data = data;
hci_stack->gap_tasks |= GAP_TASK_SET_EIR_DATA;
hci_run();
} }
/** /**

View File

@ -712,14 +712,6 @@ typedef enum hci_init_state{
HCI_INIT_W4_WRITE_SIMPLE_PAIRING_MODE, HCI_INIT_W4_WRITE_SIMPLE_PAIRING_MODE,
HCI_INIT_WRITE_PAGE_TIMEOUT, HCI_INIT_WRITE_PAGE_TIMEOUT,
HCI_INIT_W4_WRITE_PAGE_TIMEOUT, HCI_INIT_W4_WRITE_PAGE_TIMEOUT,
HCI_INIT_WRITE_DEFAULT_LINK_POLICY_SETTING,
HCI_INIT_W4_WRITE_DEFAULT_LINK_POLICY_SETTING,
HCI_INIT_WRITE_CLASS_OF_DEVICE,
HCI_INIT_W4_WRITE_CLASS_OF_DEVICE,
HCI_INIT_WRITE_LOCAL_NAME,
HCI_INIT_W4_WRITE_LOCAL_NAME,
HCI_INIT_WRITE_EIR_DATA,
HCI_INIT_W4_WRITE_EIR_DATA,
HCI_INIT_WRITE_INQUIRY_MODE, HCI_INIT_WRITE_INQUIRY_MODE,
HCI_INIT_W4_WRITE_INQUIRY_MODE, HCI_INIT_W4_WRITE_INQUIRY_MODE,
HCI_INIT_WRITE_SECURE_CONNECTIONS_HOST_ENABLE, HCI_INIT_WRITE_SECURE_CONNECTIONS_HOST_ENABLE,
@ -778,6 +770,11 @@ typedef enum hci_init_state{
} hci_substate_t; } hci_substate_t;
#define GAP_TASK_SET_LOCAL_NAME 1
#define GAP_TASK_SET_EIR_DATA 2
#define GAP_TASK_SET_CLASS_OF_DEVICE 4
#define GAP_TASK_SET_DEFAULT_LINK_POLICY 8
enum { enum {
// Tasks // Tasks
LE_ADVERTISEMENT_TASKS_SET_ADV_DATA = 1 << 0, LE_ADVERTISEMENT_TASKS_SET_ADV_DATA = 1 << 0,
@ -865,6 +862,9 @@ typedef struct {
inquiry_mode_t inquiry_mode; inquiry_mode_t inquiry_mode;
#ifdef ENABLE_CLASSIC #ifdef ENABLE_CLASSIC
/* GAP tasks, see GAP_TASK_* */
uint16_t gap_tasks;
/* write page scan activity, 0xffff is no change */ /* write page scan activity, 0xffff is no change */
uint16_t new_page_scan_interval; uint16_t new_page_scan_interval;
uint16_t new_page_scan_window; uint16_t new_page_scan_window;