Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ if(CONFIG_BLUETOOTH)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/ipc/socket/src/bt_socket_le_scan.c)
endif()

if(CONFIG_BLUETOOTH_PA_SYNC)
list(APPEND CSRCS ${BLUETOOTH_DIR}/framework/socket/bt_pa_sync.c)
endif()

if(CONFIG_BLUETOOTH_PAN)
list(APPEND CSRCS ${BLUETOOTH_DIR}/framework/socket/bt_pan.c)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/ipc/socket/src/bt_socket_pan.c)
Expand Down Expand Up @@ -252,6 +256,10 @@ if(CONFIG_BLUETOOTH)
${BLUETOOTH_DIR}/service/src/scan_filter.c)
endif()

if(CONFIG_BLUETOOTH_PA_SYNC)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/src/pa_sync_service.c)
endif()

if(CONFIG_BLUETOOTH_L2CAP)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/src/l2cap_service.c)
endif()
Expand Down Expand Up @@ -318,6 +326,10 @@ if(CONFIG_BLUETOOTH)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/stacks/zephyr/sal_le_scan_interface.c)
endif()

if(CONFIG_BLUETOOTH_PA_SYNC)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/stacks/zephyr/sal_pa_sync_interface.c)
endif()

if(CONFIG_BLUETOOTH_GATT_CLIENT)
list(APPEND CSRCS ${BLUETOOTH_DIR}/service/stacks/zephyr/sal_gatt_client_interface.c)
endif()
Expand Down Expand Up @@ -643,6 +655,10 @@ if(CONFIG_BLUETOOTH)
list(APPEND CSRCS ${BLUETOOTH_DIR}/tools/lea_tbs.c)
endif()

if(CONFIG_BLUETOOTH_AURACAST_SINK)
list(APPEND CSRCS ${BLUETOOTH_DIR}/tools/auracast_sink.c)
endif()

if(CONFIG_BLUETOOTH_STORAGE_UPDATE)
list(APPEND CSRCS ${BLUETOOTH_DIR}/tools/storage_update/storage_tool.c)
endif()
Expand Down
19 changes: 15 additions & 4 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,16 @@ config BLUETOOTH_BLE_SCAN_FILTER
depends on BLUETOOTH_BLE_SCAN

config BLUETOOTH_HCI_FILTER
bool "Enable Bluetooth HCI filter"
default y
help
Enable Bluetooth HCI filter
bool "Enable Bluetooth HCI filter"
default y
help
Enable Bluetooth HCI filter

config BLUETOOTH_PA_SYNC
bool "LE Periodic Advertising sync support"
default n
depends on BLUETOOTH_BLE_SUPPORT
depends on BLUETOOTH_BLE_SCAN

config BLUETOOTH_L2CAP
bool "L2CAP dynamic channel support"
Expand Down Expand Up @@ -614,6 +620,11 @@ config BLUETOOTH_LEAUDIO_SERVER_CSIS_RANK

endif # BLUETOOTH_LEAUDIO_SERVER

config BLUETOOTH_AURACAST_SINK
bool "Auracast Sink role"
default n
depends on BLUETOOTH_PA_SYNC

endmenu #Bluetooth LE Audio

menu "Bluetooth Audio Transport"
Expand Down
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ CSRCS += framework/socket/bt_le_scan.c
CSRCS += service/ipc/socket/src/bt_socket_scan.c
endif #CONFIG_BLUETOOTH_BLE_SCAN

ifeq ($(CONFIG_BLUETOOTH_PA_SYNC), y)
CSRCS += framework/socket/bt_pa_sync.c
endif #CONFIG_BLUETOOTH_PA_SYNC

ifeq ($(CONFIG_BLUETOOTH_PAN), y)
CSRCS += framework/socket/bt_pan.c
CSRCS += service/ipc/socket/src/bt_socket_pan.c
Expand Down Expand Up @@ -212,6 +216,9 @@ ifeq ($(CONFIG_BLUETOOTH_BLE_SCAN), y)
CSRCS += service/src/scan_record.c
CSRCS += service/src/scan_filter.c
endif #CONFIG_BLUETOOTH_BLE_SCAN
ifeq ($(CONFIG_BLUETOOTH_PA_SYNC), y)
CSRCS += service/src/pa_sync_service.c
endif #CONFIG_BLUETOOTH_PA_SYNC
ifeq ($(CONFIG_BLUETOOTH_L2CAP), y)
CSRCS += service/src/l2cap_service.c
endif #CONFIG_BLUETOOTH_L2CAP
Expand Down Expand Up @@ -259,6 +266,9 @@ endif #CONFIG_BLUETOOTH_BLE_ADV
ifeq ($(CONFIG_BLUETOOTH_BLE_SCAN), y)
CSRCS += service/stacks/zephyr/sal_le_scan_interface.c
endif #CONFIG_BLUETOOTH_BLE_SCAN
ifeq ($(CONFIG_BLUETOOTH_PA_SYNC), y)
CSRCS += service/stacks/zephyr/sal_pa_sync_interface.c
endif #CONFIG_BLUETOOTH_PA_SYNC
ifeq ($(CONFIG_BLUETOOTH_GATT_CLIENT), y)
CSRCS += service/stacks/zephyr/sal_gatt_client_interface.c
endif #CONFIG_BLUETOOTH_GATT_CLIENT
Expand Down Expand Up @@ -504,6 +514,10 @@ ifeq ($(CONFIG_BLUETOOTH_LEAUDIO_TBS), y)
CSRCS += tools/lea_tbs.c
endif

ifeq ($(CONFIG_BLUETOOTH_AURACAST_SINK), y)
CSRCS += tools/auracast_sink.c
endif

ifeq ($(CONFIG_BLUETOOTH_STORAGE_UPDATE), y)
CSRCS += tools/storage_update/storage_tool.c
endif #CONFIG_BLUETOOTH_STORAGE_UPDATE
Expand Down
93 changes: 93 additions & 0 deletions framework/common/advertiser_data_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

#include "advertiser_data.h"
#include "bt_debug.h"
#ifdef CONFIG_BLUETOOTH_PA_SYNC
#include "bt_pa_sync.h"
#endif
#include "bt_utils.h"

typedef struct {
uint8_t ad_type;
Expand Down Expand Up @@ -213,3 +217,92 @@ bool advertiser_data_dump(uint8_t* data, uint16_t len, ad_dump_cb_t dump)

return true;
}

bool advertiser_data_parse(const uint8_t* data, uint8_t len, ad_parse_cb_t cb, void* context)
{
adv_data_t* ad;
uint16_t offset = 0;

if (!cb)
return false;

while (offset < len) {
ad = (adv_data_t*)&data[offset];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ad = (adv_data_t*)&data[offset]; This line may cause memory alignment issues.

Here you are casting data[offset] to the adv_data_t* type. Assuming data is just a raw byte array, the adv_data_t structure may have memory alignment problems on certain platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I noticed it as well since there were warnings when printing the data.
However, I’d like to follow the existing code patterns, like advertiser_data_dump.
Otherwise, this PR would include many changes irrelevant to Auracast itself.

if (ad->len == 0) { /**< AD Type does not exist */
offset += sizeof(ad->len); /**< Skip this entry */
continue; /**< Goto the next item */
}

offset += sizeof(ad->len) + ad->len;
if (offset > len)
return false; /**< Incomplete AD Data */

cb(ad, context);
};

return true;
}

#ifdef CONFIG_BLUETOOTH_PA_SYNC
static void adv_data_parse_uuid_16(bt_pa_sync_info_t* info, uint16_t uuid_16,
const adv_data_t* data)
{
const uint8_t* p = data->data + sizeof(uint16_t);

switch (uuid_16) {
#ifdef CONFIG_BLUETOOTH_AURACAST_SINK
case BT_UUID_BROADCAST_AUDIO_ANNOUNCEMENT:
if (data->len < 1 + sizeof(uuid_16) + 3)
Copy link
Contributor

@chengkai15 chengkai15 Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we make the magic number easy to understand? l

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestions?

The name of this uuid is Broadcast Audio Announcement

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest macro for special size or struct size

break; /* less than AD Type (1 octet) + UUID16 (2 octets) + Broadcast ID (3 octets)*/

STREAM_TO_UINT24(info->broadcast_id, p);
break;
#endif
default:
break;
}
}

static void adv_data_parsed(const adv_data_t* data, void* context)
{
bt_pa_sync_info_t* info = (bt_pa_sync_info_t*)context;
const uint8_t* p = data->data;
uint16_t uuid_16;

switch (data->type) {
case BT_AD_NAME_SHORT:
case BT_AD_NAME_COMPLETE:
strlcpy(info->name, (char*)data->data, MIN(sizeof(info->name), data->len));
break;

#ifdef CONFIG_BLUETOOTH_AURACAST_SINK
case BT_AD_BROADCAST_NAME:
strlcpy(info->broadcast_name, (char*)data->data,
MIN(sizeof(info->broadcast_name), data->len));
Comment on lines +275 to +281
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strlcpy function call specifies a maximum copy length using MIN(sizeof(info->name), data->len). However, data->len includes the AD Type byte (1 octet), which should not be counted in the data length for the name. This means an extra byte from the next AD entry could be copied into the name buffer. The correct length should be data->len - 1 to account for the AD Type byte that has already been excluded from data->data.

Suggested change
strlcpy(info->name, (char*)data->data, MIN(sizeof(info->name), data->len));
break;
#ifdef CONFIG_BLUETOOTH_AURACAST_SINK
case BT_AD_BROADCAST_NAME:
strlcpy(info->broadcast_name, (char*)data->data,
MIN(sizeof(info->broadcast_name), data->len));
strlcpy(info->name, (char*)data->data, MIN(sizeof(info->name), data->len - 1));
break;
#ifdef CONFIG_BLUETOOTH_AURACAST_SINK
case BT_AD_BROADCAST_NAME:
strlcpy(info->broadcast_name, (char*)data->data,
MIN(sizeof(info->broadcast_name), data->len - 1));

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ADV data is a string but without an ending '\0'.

Therefore we need the length data->len, which is exactly the length of the string, plus a space for '\0'.

Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strlcpy function call specifies a maximum copy length using MIN(sizeof(info->broadcast_name), data->len). However, data->len includes the AD Type byte (1 octet), which should not be counted in the data length for the broadcast name. This means an extra byte from the next AD entry could be copied into the broadcast_name buffer. The correct length should be data->len - 1 to account for the AD Type byte that has already been excluded from data->data.

Suggested change
MIN(sizeof(info->broadcast_name), data->len));
MIN(sizeof(info->broadcast_name), data->len > 0 ? data->len - 1 : 0));

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data->len does not include the ending '\0', so we do need the orignal data to get the full string.

break;
#endif

case BT_AD_SERVICE_DATA16:
if (data->len < 1 + sizeof(uuid_16))
break; /**< less than AD Type (1 octet) + UUID16 (2 octets) */

STREAM_TO_UINT16(uuid_16, p);
adv_data_parse_uuid_16(info, uuid_16, data);
break;
default:
break;
}
}

bt_status_t bt_pa_sync_parse_adv_data(bt_pa_sync_info_t* info, const ble_scan_result_t* result)
{
memset(info, 0x00, sizeof(bt_pa_sync_info_t));
info->broadcast_id = BT_INVALID_BROADCAST_ID;
if (!advertiser_data_parse(result->adv_data, result->length, adv_data_parsed, info))
return BT_STATUS_FAIL;

return result->flags & SCAN_RESULT_FLAG_PERIODIC_ADVERTISING
? BT_STATUS_SUCCESS
: BT_STATUS_NOT_FOUND;
}
#endif /* CONFIG_BLUETOOTH_PA_SYNC */
23 changes: 23 additions & 0 deletions framework/include/advertiser_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ typedef struct advertiser_data_ advertiser_data_t;
*/
typedef void (*ad_dump_cb_t)(const char* str);

/**
* @brief Advertising data parsing callback function.
*
* Function prototype for parsing advertising data.
*
* @param data - The parsed advertising data
* @param context - User context
*/
typedef void (*ad_parse_cb_t)(const adv_data_t* data, void* context);

/**
* @brief Dump advertising data.
*
Expand All @@ -131,6 +141,19 @@ static void on_scan_result_cb(bt_scanner_t* scanner, ble_scan_result_t* result)
*/
bool advertiser_data_dump(uint8_t* data, uint16_t len, ad_dump_cb_t dump);

/**
* @brief Parse advertising data.
*
* @param data - Pointer to the advertising data buffer
* @param len - Length of the advertising data buffer
* @param cb - Callback function to output the parsed data
* @param context - User context to be carried in `cb`
*
* @return true - Parsing and output were successful
* @return false - Parsing failed
*/
bool advertiser_data_parse(const uint8_t* data, uint8_t len, ad_parse_cb_t cb, void* context);

/**
* @brief Create a new advertiser data object.
*
Expand Down
5 changes: 5 additions & 0 deletions framework/include/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@ typedef uint8_t bt_128key_t[16];
#define BT_UUID_MAX_NUM (32)
#define BT_UUID_128_LEN (16)

/* `Broadcast_ID` is 24-bit (0x000000-0xFFFFFF). Other values are invalid. */
#define BT_INVALID_BROADCAST_ID (0xFFFFFFFF)

#define BT_BROADCAST_NAME_MAX_LEN (128)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change BROADCAST to ADV will be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same reason, I would prefer to keep the official names.

Copy link
Contributor

@liuX10 liuX10 Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The broadcast_name field has a maximum length of 128 Bytes, which should include the terminating null character ‘\0’.
so 127 maybe better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't, because there is actually a Length field for this Broadcast_Name.
Note that any strings in AD data are not necessarily (and usually not) end with a '\0'


typedef struct {
bt_address_t addr;
int8_t rssi;
Expand Down
11 changes: 8 additions & 3 deletions framework/include/bt_le_scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ enum {
#define SCAN_MODE_LOW_LATENCY_INTERVAL 0xA0
#define SCAN_MODE_LOW_LATENCY_WINDOW 0xA0

#define SCAN_RESULT_FLAG_PERIODIC_ADVERTISING 0x01

typedef void bt_scanner_t;

typedef enum {
Expand All @@ -79,11 +81,14 @@ typedef enum {
typedef struct {
bt_address_t addr;
uint8_t dev_type; /* bt_device_type_t */
int8_t rssi;
int8_t rssi; /* RSSI in dBm (-127, +20). 0x7F if unavailable */
uint8_t addr_type; /* ble_addr_type_t */
uint8_t adv_type; /* ble_adv_type_t */
uint8_t length;
uint8_t pad[1];
uint8_t length; /* length of `adv_data` */
uint8_t sid; /* advertising set identifier, valid from 0x00 to 0x0F, 0xFF if not provided */
uint16_t interval; /* periodic advertising interval in 1.25 milliseconds, 0 if not presented */
int8_t tx_power; /* transmit power of the advertiser in dBm (-127, +20). 0x7F if unavailable */
uint8_t flags; /* e.g., `SCAN_RESULT_FLAG_PERIODIC_ADVERTISING` */
uint8_t adv_data[1];
} ble_scan_result_t;

Expand Down
60 changes: 60 additions & 0 deletions framework/include/bt_pa_sync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/****************************************************************************
* Copyright (C) 2025 Xiaomi Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#ifndef __BT_PA_SYNC_H__
#define __BT_PA_SYNC_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "bt_le_scan.h"

#ifndef BTSYMBOLS
#define BTSYMBOLS(s) s
#endif

/**
* @brief Information about the periodic advertising sync.
*/
typedef struct bt_pa_sync_info {
/** Present if available: Broadcast_ID from Broadcast Audio Announcement.
* Otherwise, `BT_INVALID_BROADCAST_ID` */
uint32_t broadcast_id;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change it to aid will be better.

Copy link
Contributor Author

@gzh-terry gzh-terry Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The official name is Broadcast_ID, not a "Advertiser ID"
image
From Basic Audio Profle v1.0.1, Section 3.7.2.1


/** UTF-8 string of the remote device name */
char name[BT_REM_NAME_MAX_LEN + 1];

/** UTF-8 string of the `Broadcast_Name` field in `AdvData` (if present) */
char broadcast_name[BT_BROADCAST_NAME_MAX_LEN + 1];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change it to adv_name will be more easy to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name cames from Assigned Numbers, Section 6.12.6.13
image

} bt_pa_sync_info_t;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why there is no phy, interval and num_subevents, subevent_interval, response_slot_delay, response_slot_spacing when sync rsp is enable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These info is parsed from secondary advertising. Especially from "LE Extended Advertising Report event".

  • phy, yes, we have phy for primary and secondary channels, I can add them later.
  • Interval, or Periodic Advertising Interval. is included in ble_scan_result_tnow (also in this PR). So the upper layer can get the interval before extracting AdvData.
  • num_subeventsand the other parameters cames from "LE Periodic Advertising Sync Established event", which is not included in this PR.


/**
* @brief Parse an advertising report and check if periodic advertising is present.
*
* @param[out] info Buffer to store the parsed periodic advertising info
* @param[in] result Advertising report from the scan result callback
*
* @return `BT_STATUS_SUCCESS` if periodic advertising is found
* @return `BT_STATUS_NOT_FOUND` if no periodic advertising is found
* @return Other negative `bt_status_t` error codes on failure.
*/
bt_status_t bt_pa_sync_parse_adv_data(bt_pa_sync_info_t* info, const ble_scan_result_t* result);

#ifdef __cplusplus
}
#endif

#endif
1 change: 1 addition & 0 deletions framework/include/bt_uuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extern "C" {
#define BT_UUID_A2DP_SNK 0x110B
#define BT_UUID_HFP 0x111E
#define BT_UUID_HFP_AG 0x111F
#define BT_UUID_BROADCAST_AUDIO_ANNOUNCEMENT 0x1852

typedef enum {
BT_UUID16_TYPE = 2,
Expand Down
17 changes: 17 additions & 0 deletions framework/socket/bt_pa_sync.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/****************************************************************************
* Copyright (C) 2025 Xiaomi Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/

#include "bt_pa_sync.h"
Loading
Loading