Compare commits

..

7 Commits

Author SHA1 Message Date
huangyulong3 fb31f0d842 bluetooth: fix FRAMEWORK_LOCAL mode compilation failures
bug: v/90636

When CONFIG_BLUETOOTH_FRAMEWORK_LOCAL is enabled (no socket IPC),
several compilation errors occur:
- bt_instance_t cookie fields are guarded by SOCKET_IPC ifdef but
  referenced unconditionally in service code. Move cookie fields out
  of the ifdef so they are always available.
- scan_manager.c calls bt_socket_server_is_busy() which only exists
  when SOCKET_IPC is enabled. Add ifdef guard around the call.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:58:55 +08:00
huangyulong3 4950ce00cb bluetooth: implement PAN profile SAL interface stub
bug: v/90635

When CONFIG_BLUETOOTH_PAN is enabled, pan_service.c references
sal_pan_interface.h and bt_sal_pan_* functions which were never
implemented, causing fatal compilation error. Add SAL PAN interface
header and zblue-based stub implementation that returns
BT_STATUS_NOT_SUPPORTED (BNEP not yet available in zblue). Add build
rules to Makefile.

Input-only pointer parameters (dst_addr, src_addr, data) are const
qualified to express intent and prevent accidental mutation.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:58:55 +08:00
huangyulong3 02a0271b76 bluetooth: implement L2CAP dynamic channel SAL interface
bug: v/90634

When CONFIG_BLUETOOTH_L2CAP is enabled, l2cap_service.c references
sal_l2cap_interface.h and bt_sal_l2cap_* functions which were never
implemented, causing fatal compilation error. Implement the SAL L2CAP
interface header and zblue-based implementation supporting channel
listen, connect, disconnect, and data send operations. Add build rules
to Makefile.

Channels and servers are dynamically allocated via calloc/free to
minimize BSS footprint when L2CAP connections are idle. Server entries
are kept allocated after stop_listen since zblue has no unregister API,
and reused on re-listen for the same PSM.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:56:07 +08:00
huangyulong3 9027fe26ea bluetooth: fix const qualifier mismatch in bt_cs_start_distance_measurement
bug: v/90633

bt_cs.h declares bt_cs_start_distance_measurement with const parameter
but the implementation in bt_cs.c and the interface typedef in cs_service.h
omit const, causing -Wdiscarded-qualifiers warnings and potential build
failure with -Werror. Align implementation and interface with the header
declaration.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:55:37 +08:00
huangyulong3 2765a97128 bluetooth: add BT_RFCOMM dependency to BLUETOOTH_SPP Kconfig
bug: v/90632

BLUETOOTH_SPP does not select BT_RFCOMM. When HFP (which normally brings
in RFCOMM via its own select) is disabled but SPP remains enabled, RFCOMM
is not compiled into the zblue library, causing undefined reference errors
for bt_rfcomm_dlc_connect, bt_rfcomm_server_register, etc. Add explicit
select BT_RFCOMM to the BLUETOOTH_SPP menuconfig.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:55:37 +08:00
huangyulong3 49daf01a46 bluetooth: add fallback defaults for GATT client config macros
bug: v/90631

sal_gatt_client_interface.c uses CONFIG_GATT_CLIENT_SERVICE_MAX,
CONFIG_GATT_CLIENT_ELEMENT_MAX, and CONFIG_GATT_CLIENT_CHAR_PER_SERVICE_MAX
as array sizes, but these Kconfig options depend on multiple configs being
enabled simultaneously. In some config combinations they are undefined,
causing compilation failure. Add #ifndef fallback defaults (20, 200, 100).

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:55:37 +08:00
huangyulong3 ed6ec45934 bluetooth: add CONFIG_BT_SIGNING and CONFIG_BT_SMP guards in SAL LE adapter
bug: v/90629

sal_adapter_le_interface.c unconditionally accesses struct bt_keys members
remote_csrk/local_csrk (only exist when CONFIG_BT_SIGNING is enabled) and
references zblue_on_pairing_complete_ctkd (only declared under CONFIG_BT_SMP).
This causes compilation failure in BLE-only configurations where SMP/SIGNING
may not be enabled. Add appropriate ifdef guards.

Signed-off-by: huangyulong3 <huangyulong3@xiaomi.com>
2026-05-11 23:55:37 +08:00
14 changed files with 586 additions and 14 deletions

View File

@ -721,6 +721,7 @@ endif # BLUETOOTH_GATT_SERVER
menuconfig BLUETOOTH_SPP
bool "Serial Port Profile (SPP)"
default n
select BT_RFCOMM
if BLUETOOTH_SPP
config BLUETOOTH_SPP_MAX_CONNECTIONS

View File

@ -237,6 +237,12 @@ ifneq ($(CONFIG_BLUETOOTH_STACK_BREDR_ZBLUE)$(CONFIG_BLUETOOTH_STACK_LE_ZBLUE),)
ifeq ($(CONFIG_BLUETOOTH_CONNECTION_MANAGER), y)
CSRCS += service/stacks/zephyr/sal_connection_manager.c
endif
ifeq ($(CONFIG_BLUETOOTH_SPP), y)
CSRCS += service/stacks/zephyr/sal_spp_interface.c
endif #CONFIG_BLUETOOTH_SPP
ifeq ($(CONFIG_BLUETOOTH_L2CAP), y)
CSRCS += service/stacks/zephyr/sal_l2cap_interface.c
endif #CONFIG_BLUETOOTH_L2CAP
ifeq ($(CONFIG_BLUETOOTH_A2DP), y)
CSRCS += service/stacks/zephyr/sal_a2dp_interface.c
endif #CONFIG_BLUETOOTH_A2DP
@ -244,6 +250,20 @@ ifneq ($(CONFIG_BLUETOOTH_AVRCP_CONTROL)$(CONFIG_BLUETOOTH_AVRCP_TARGET),)
CSRCS += service/stacks/zephyr/sal_avrcp_interface.c
endif #CONFIG_BLUETOOTH_AVRCP_CONTROL/CONFIG_BLUETOOTH_AVRCP_TARGET
ifeq ($(CONFIG_BLUETOOTH_HFP_HF), y)
CSRCS += service/stacks/zephyr/sal_hfp_hf_interface.c
endif #CONFIG_BLUETOOTH_HFP_HF
ifeq ($(CONFIG_BLUETOOTH_HFP_AG), y)
CSRCS += service/stacks/zephyr/sal_hfp_ag_interface.c
endif #CONFIG_BLUETOOTH_HFP_AG
ifeq ($(CONFIG_BLUETOOTH_HID_DEVICE), y)
CSRCS += service/stacks/zephyr/sal_hid_device_interface.c
endif #CONFIG_BLUETOOTH_HID_DEVICE
ifeq ($(CONFIG_BLUETOOTH_PAN), y)
CSRCS += service/stacks/zephyr/sal_pan_interface.c
endif #CONFIG_BLUETOOTH_PAN
ifeq ($(CONFIG_BLUETOOTH_STACK_LE_ZBLUE), y)
CSRCS += service/stacks/zephyr/sal_adapter_le_interface.c
ifeq ($(CONFIG_BLUETOOTH_BLE_ADV), y)

View File

@ -42,7 +42,7 @@ bool BTSYMBOLS(bt_cs_unregister_callbacks)(bt_instance_t* ins, void* cookie)
return profile->unregister_callbacks(NULL, cookie);
}
bt_status_t BTSYMBOLS(bt_cs_start_distance_measurement)(bt_instance_t* ins, bt_distance_measurement_params_t* params)
bt_status_t BTSYMBOLS(bt_cs_start_distance_measurement)(bt_instance_t* ins, const bt_distance_measurement_params_t* params)
{
bt_cs_interface_t* profile = get_profile_service();

View File

@ -488,12 +488,6 @@ typedef struct bt_instance {
callbacks_list_t* avrcp_target_callbacks;
callbacks_list_t* avrcp_control_callbacks;
callbacks_list_t* cs_callbacks;
void* adapter_cookie;
void* a2dp_sink_cookie;
void* a2dp_source_cookie;
void* avrcp_target_cookie;
void* avrcp_control_cookie;
void* cs_cookie;
callbacks_list_t* hfp_ag_callbacks;
callbacks_list_t* hfp_hf_callbacks;
@ -501,17 +495,24 @@ typedef struct bt_instance {
callbacks_list_t* spp_callbacks;
callbacks_list_t* hidd_callbacks;
callbacks_list_t* l2cap_callbacks;
bt_list_t* gattc_remote_list;
bt_list_t* gatts_remote_list;
void* priv;
#endif
void* adapter_cookie;
void* a2dp_sink_cookie;
void* a2dp_source_cookie;
void* avrcp_target_cookie;
void* avrcp_control_cookie;
void* cs_cookie;
void* hfp_ag_cookie;
void* hfp_hf_cookie;
void* panu_cookie;
void* spp_cookie;
void* hidd_cookie;
void* l2cap_cookie;
bt_list_t* gattc_remote_list;
bt_list_t* gatts_remote_list;
void* priv;
#endif
} bt_instance_t;
/**

View File

@ -232,7 +232,7 @@ static bool cs_unregister_callbacks(void** remote, void* cookie)
return bt_remote_callbacks_unregister(g_cs_service.callbacks, remote, cookie);
}
static bt_status_t cs_start_distance_measurement(bt_distance_measurement_params_t* params)
static bt_status_t cs_start_distance_measurement(const bt_distance_measurement_params_t* params)
{
BT_LOGD("cs_start_distance_measurement");
switch (params->method) {

View File

@ -665,7 +665,7 @@ typedef struct {
* @brief Unregister the cs event callback
*/
bool (*unregister_callbacks)(void** remote, void* cookie);
bt_status_t (*start_distance_measurement)(bt_distance_measurement_params_t* params);
bt_status_t (*start_distance_measurement)(const bt_distance_measurement_params_t* params);
bt_status_t (*stop_distance_measurement)(bt_address_t* addr, int method, bool timeout);

View File

@ -29,6 +29,7 @@
#include "bt_list.h"
#include "callbacks_list.h"
#include "netutils/netlib.h"
#include "pan_service.h"
#include "power_manager.h"
#include "sal_pan_interface.h"
#include "service_loop.h"

View File

@ -25,7 +25,9 @@
#include "bt_hash.h"
#include "bt_le_scan.h"
#include "bt_list.h"
#ifdef CONFIG_BLUETOOTH_FRAMEWORK_SOCKET_IPC
#include "bt_socket.h"
#endif
#include "bt_time.h"
#include "sal_interface.h"
#include "scan_filter.h"
@ -249,10 +251,12 @@ static void notify_scanners_scan_result(void* data)
scanner_device_t* device;
uint32_t timestamp_ms;
#ifdef CONFIG_BLUETOOTH_FRAMEWORK_SOCKET_IPC
if (bt_socket_server_is_busy()) {
do_in_service_loop_deffered(notify_scanners_scan_result, data, true);
return;
}
#endif
timestamp_ms = bt_get_os_timestamp_ms();
list_for_every(&scanner_manager.scanning_list, node)

View File

@ -0,0 +1,34 @@
/****************************************************************************
* Copyright (C) 2026 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 __SAL_L2CAP_INTERFACE_H__
#define __SAL_L2CAP_INTERFACE_H__
#include <stdint.h>
#include "bt_addr.h"
#include "bt_l2cap.h"
#include "bt_status.h"
bt_status_t bt_sal_l2cap_init(void);
void bt_sal_l2cap_cleanup(void);
bt_status_t bt_sal_l2cap_listen_channel(l2cap_config_option_t* option);
bt_status_t bt_sal_l2cap_stop_listen_channel(uint16_t psm);
bt_status_t bt_sal_l2cap_connect_channel(bt_address_t* addr, l2cap_config_option_t* option);
bt_status_t bt_sal_l2cap_disconnect_channel(uint16_t local_cid);
bt_status_t bt_sal_l2cap_send_packet(uint16_t local_cid, const uint8_t* data, uint16_t size);
bt_status_t bt_sal_l2cap_give_incoming_credits(bt_address_t* addr, uint16_t local_cid, uint16_t credits);
#endif /* __SAL_L2CAP_INTERFACE_H__ */

View File

@ -0,0 +1,33 @@
/****************************************************************************
* Copyright (C) 2026 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 __SAL_PAN_INTERFACE_H__
#define __SAL_PAN_INTERFACE_H__
#include <stdint.h>
#include "bt_addr.h"
#include "bt_pan.h"
#include "bt_status.h"
bt_status_t bt_sal_pan_init(uint8_t max_connections, pan_role_t role);
void bt_sal_pan_cleanup(void);
bt_status_t bt_sal_pan_connect(bt_address_t* addr, pan_role_t dst_role, pan_role_t src_role);
bt_status_t bt_sal_pan_disconnect(bt_address_t* addr);
bt_status_t bt_sal_pan_write(bt_address_t* addr, uint16_t protocol,
const uint8_t* dst_addr, const uint8_t* src_addr,
const uint8_t* data, uint16_t length);
#endif /* __SAL_PAN_INTERFACE_H__ */

View File

@ -149,7 +149,9 @@ static struct bt_conn_cb g_conn_cbs = {
};
static struct bt_conn_auth_info_cb g_conn_auth_info_cbs = {
#ifdef CONFIG_BT_SMP
.pairing_complete_ctkd = zblue_on_pairing_complete_ctkd,
#endif
.pairing_complete = zblue_on_pairing_complete,
.pairing_failed = zblue_on_pairing_failed,
.bond_deleted = zblue_on_bond_deleted,
@ -325,7 +327,9 @@ static int zblue_on_ltk_notify(uint8_t dev_id, uint8_t id, bt_addr_le_t* addr, c
/* smp[38 ~ 53] IRK */
memcpy(&prop->smp_key[38], keys->irk.val, 16);
/* smp[54 ~ 69] CSRK(remote) */
#ifdef CONFIG_BT_SIGNING
memcpy(&prop->smp_key[54], keys->remote_csrk.val, 16);
#endif
// smp[70 ~ 77] addr { addr[6], type[1], cap[1]/id_num[1] };
memcpy(&prop->smp_key[70], prop->addr.addr, sizeof(prop->addr.addr));
@ -334,7 +338,9 @@ static int zblue_on_ltk_notify(uint8_t dev_id, uint8_t id, bt_addr_le_t* addr, c
/* smp[78 ~ 79] RFU/keys; */
memcpy(&prop->smp_key[78], &keys->keys, 2);
#ifdef CONFIG_BT_SIGNING
memcpy(prop->local_csrk, keys->local_csrk.val, 16);
#endif
adapter_on_le_bonded_device_update(prop, 1);
free(prop);
@ -387,10 +393,12 @@ static int zblue_on_ltk_load(bt_addr_le_t* addr, uint8_t* key_value, uint8_t val
memcpy(keys->irk.rpa.val, remote_addr->addr, sizeof(remote_addr->addr));
#ifdef CONFIG_BT_SIGNING
memcpy(keys->remote_csrk.val, &smp_data[54], 16);
if (local_csrk)
memcpy(keys->local_csrk.val, local_csrk, 16);
#endif
memcpy(key_value, keys->storage_start, value_len);

View File

@ -35,6 +35,16 @@
#ifdef CONFIG_BLUETOOTH_GATT_CLIENT
#define STACK_CALL(func) zblue_##func
#ifndef CONFIG_GATT_CLIENT_SERVICE_MAX
#define CONFIG_GATT_CLIENT_SERVICE_MAX 20
#endif
#ifndef CONFIG_GATT_CLIENT_ELEMENT_MAX
#define CONFIG_GATT_CLIENT_ELEMENT_MAX 200
#endif
#ifndef CONFIG_GATT_CLIENT_CHAR_PER_SERVICE_MAX
#define CONFIG_GATT_CLIENT_CHAR_PER_SERVICE_MAX 100
#endif
typedef void (*sal_func_t)(void* args);
typedef struct {

View File

@ -0,0 +1,407 @@
/****************************************************************************
* Copyright (C) 2026 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 <debug.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include "bt_addr.h"
#include "l2cap_service.h"
#include "sal_interface.h"
#include "sal_l2cap_interface.h"
#include "sal_zblue.h"
#include "service_loop.h"
#include "utils/log.h"
#define L2CAP_MAX_CHANNELS 20
#define L2CAP_MAX_SERVERS 8
#define L2CAP_SDU_BUF_SIZE CONFIG_BLUETOOTH_L2CAP_OUTGOING_MTU
#define L2CAP_TX_BUF_COUNT 8
NET_BUF_POOL_FIXED_DEFINE(l2cap_tx_pool, L2CAP_TX_BUF_COUNT,
BT_L2CAP_SDU_BUF_SIZE(L2CAP_SDU_BUF_SIZE), 0, NULL);
typedef struct {
struct bt_l2cap_le_chan le_chan;
bt_address_t addr;
uint16_t local_cid;
uint16_t psm;
uint16_t id;
bool is_server;
} sal_l2cap_channel_t;
typedef struct {
struct bt_l2cap_server server;
uint16_t psm;
uint16_t mtu;
uint16_t mps;
uint16_t credits;
bool in_use;
} sal_l2cap_server_t;
static sal_l2cap_channel_t* g_channels[L2CAP_MAX_CHANNELS];
static sal_l2cap_server_t* g_servers[L2CAP_MAX_SERVERS];
static sal_l2cap_channel_t* find_channel_by_cid(uint16_t local_cid)
{
for (int i = 0; i < L2CAP_MAX_CHANNELS; i++) {
if (g_channels[i] && g_channels[i]->local_cid == local_cid) {
return g_channels[i];
}
}
return NULL;
}
static sal_l2cap_channel_t* find_channel_by_le_chan(struct bt_l2cap_le_chan* le_chan)
{
for (int i = 0; i < L2CAP_MAX_CHANNELS; i++) {
if (g_channels[i] && &g_channels[i]->le_chan == le_chan) {
return g_channels[i];
}
}
return NULL;
}
static sal_l2cap_channel_t* alloc_channel(void)
{
for (int i = 0; i < L2CAP_MAX_CHANNELS; i++) {
if (!g_channels[i]) {
g_channels[i] = calloc(1, sizeof(sal_l2cap_channel_t));
return g_channels[i];
}
}
return NULL;
}
static void free_channel(sal_l2cap_channel_t* ch)
{
if (!ch) {
return;
}
for (int i = 0; i < L2CAP_MAX_CHANNELS; i++) {
if (g_channels[i] == ch) {
free(g_channels[i]);
g_channels[i] = NULL;
return;
}
}
}
static sal_l2cap_server_t* find_server_by_psm(uint16_t psm)
{
for (int i = 0; i < L2CAP_MAX_SERVERS; i++) {
if (g_servers[i] && g_servers[i]->psm == psm) {
return g_servers[i];
}
}
return NULL;
}
static sal_l2cap_server_t* alloc_server(void)
{
for (int i = 0; i < L2CAP_MAX_SERVERS; i++) {
if (!g_servers[i]) {
g_servers[i] = calloc(1, sizeof(sal_l2cap_server_t));
if (!g_servers[i]) {
return NULL;
}
g_servers[i]->in_use = true;
return g_servers[i];
}
}
return NULL;
}
static void get_addr_from_conn(struct bt_conn* conn, bt_address_t* addr)
{
const bt_addr_le_t* peer = bt_conn_get_dst(conn);
memcpy(addr->addr, peer->a.val, sizeof(addr->addr));
}
static int l2cap_recv_cb(struct bt_l2cap_chan* chan, struct net_buf* buf)
{
sal_l2cap_channel_t* ch = find_channel_by_le_chan(BT_L2CAP_LE_CHAN(chan));
if (!ch) {
return -ENOENT;
}
l2cap_on_packet_received(&ch->addr, ch->local_cid, buf->data, buf->len);
return 0;
}
static void l2cap_connected_cb(struct bt_l2cap_chan* chan)
{
struct bt_l2cap_le_chan* le_chan = BT_L2CAP_LE_CHAN(chan);
sal_l2cap_channel_t* ch = find_channel_by_le_chan(le_chan);
if (!ch) {
return;
}
ch->local_cid = le_chan->tx.cid;
get_addr_from_conn(chan->conn, &ch->addr);
l2cap_channel_param_t param = {
.local_cid = le_chan->tx.cid,
.remote_cid = le_chan->rx.cid,
.psm = ch->psm,
.is_client = !ch->is_server,
.transport = BT_TRANSPORT_BLE,
.incoming = {
.mtu = le_chan->rx.mtu,
.le_mps = le_chan->rx.mps,
.credits = atomic_get(&le_chan->rx.credits),
},
.outgoing = {
.mtu = le_chan->tx.mtu,
.le_mps = le_chan->tx.mps,
.credits = atomic_get(&le_chan->tx.credits),
},
};
l2cap_on_channel_connected(&ch->addr, &param);
}
static void l2cap_disconnected_cb(struct bt_l2cap_chan* chan)
{
sal_l2cap_channel_t* ch = find_channel_by_le_chan(BT_L2CAP_LE_CHAN(chan));
if (!ch) {
return;
}
l2cap_on_channel_disconnected(&ch->addr, ch->local_cid, 0);
free_channel(ch);
}
static void l2cap_sent_cb(struct bt_l2cap_chan* chan)
{
sal_l2cap_channel_t* ch = find_channel_by_le_chan(BT_L2CAP_LE_CHAN(chan));
if (!ch) {
return;
}
l2cap_on_packet_sent(&ch->addr, ch->local_cid);
}
static const struct bt_l2cap_chan_ops l2cap_chan_ops = {
.recv = l2cap_recv_cb,
.connected = l2cap_connected_cb,
.disconnected = l2cap_disconnected_cb,
.sent = l2cap_sent_cb,
};
static int l2cap_accept_cb(struct bt_conn* conn, struct bt_l2cap_server* server,
struct bt_l2cap_chan** chan)
{
sal_l2cap_channel_t* ch = alloc_channel();
if (!ch) {
return -ENOMEM;
}
sal_l2cap_server_t* srv = find_server_by_psm(server->psm);
ch->is_server = true;
ch->psm = server->psm;
get_addr_from_conn(conn, &ch->addr);
ch->le_chan.chan.ops = &l2cap_chan_ops;
if (srv) {
ch->le_chan.rx.mtu = srv->mtu;
ch->le_chan.rx.mps = srv->mps;
}
*chan = &ch->le_chan.chan;
return 0;
}
bt_status_t bt_sal_l2cap_init(void)
{
memset(g_channels, 0, sizeof(g_channels));
memset(g_servers, 0, sizeof(g_servers));
return BT_STATUS_SUCCESS;
}
void bt_sal_l2cap_cleanup(void)
{
for (int i = 0; i < L2CAP_MAX_CHANNELS; i++) {
if (g_channels[i]) {
bt_l2cap_chan_disconnect(&g_channels[i]->le_chan.chan);
free(g_channels[i]);
g_channels[i] = NULL;
}
}
for (int i = 0; i < L2CAP_MAX_SERVERS; i++) {
if (g_servers[i]) {
free(g_servers[i]);
g_servers[i] = NULL;
}
}
}
bt_status_t bt_sal_l2cap_listen_channel(l2cap_config_option_t* option)
{
if (!option) {
return BT_STATUS_PARM_INVALID;
}
sal_l2cap_server_t* srv = find_server_by_psm(option->psm);
if (srv) {
/* Server already registered in zblue, reuse it */
srv->mtu = option->mtu;
srv->mps = option->le_mps;
srv->credits = option->init_credits;
return BT_STATUS_SUCCESS;
}
srv = alloc_server();
if (!srv) {
return BT_STATUS_NOMEM;
}
srv->psm = option->psm;
srv->mtu = option->mtu;
srv->mps = option->le_mps;
srv->credits = option->init_credits;
srv->server.psm = option->psm;
srv->server.accept = l2cap_accept_cb;
int err = bt_l2cap_server_register(&srv->server);
if (err) {
for (int i = 0; i < L2CAP_MAX_SERVERS; i++) {
if (g_servers[i] == srv) {
free(g_servers[i]);
g_servers[i] = NULL;
break;
}
}
BT_LOGE("L2CAP server register failed: %d", err);
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
bt_status_t bt_sal_l2cap_stop_listen_channel(uint16_t psm)
{
sal_l2cap_server_t* srv = find_server_by_psm(psm);
if (!srv) {
return BT_STATUS_NOT_FOUND;
}
/* Keep the server struct allocated since zblue has no unregister API.
* Mark as inactive so it can be reused on re-listen. */
srv->in_use = false;
return BT_STATUS_SUCCESS;
}
bt_status_t bt_sal_l2cap_connect_channel(bt_address_t* addr, l2cap_config_option_t* option)
{
if (!addr || !option) {
return BT_STATUS_PARM_INVALID;
}
sal_l2cap_channel_t* ch = alloc_channel();
if (!ch) {
return BT_STATUS_NOMEM;
}
ch->is_server = false;
ch->psm = option->psm;
ch->id = option->id;
memcpy(&ch->addr, addr, sizeof(bt_address_t));
ch->le_chan.chan.ops = &l2cap_chan_ops;
ch->le_chan.rx.mtu = option->mtu;
ch->le_chan.rx.mps = option->le_mps;
bt_addr_le_t le_addr;
le_addr.type = BT_ADDR_LE_PUBLIC;
memcpy(le_addr.a.val, addr->addr, sizeof(le_addr.a.val));
struct bt_conn* conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &le_addr);
if (!conn) {
free_channel(ch);
return BT_STATUS_NOT_FOUND;
}
int err = bt_l2cap_chan_connect(conn, &ch->le_chan.chan, option->psm);
bt_conn_unref(conn);
if (err) {
free_channel(ch);
BT_LOGE("L2CAP connect failed: %d", err);
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
bt_status_t bt_sal_l2cap_disconnect_channel(uint16_t local_cid)
{
sal_l2cap_channel_t* ch = find_channel_by_cid(local_cid);
if (!ch) {
return BT_STATUS_NOT_FOUND;
}
int err = bt_l2cap_chan_disconnect(&ch->le_chan.chan);
if (err) {
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
bt_status_t bt_sal_l2cap_send_packet(uint16_t local_cid, const uint8_t* data, uint16_t size)
{
sal_l2cap_channel_t* ch = find_channel_by_cid(local_cid);
if (!ch) {
return BT_STATUS_NOT_FOUND;
}
struct net_buf* buf = net_buf_alloc_len(&l2cap_tx_pool, size, K_NO_WAIT);
if (!buf) {
return BT_STATUS_NOMEM;
}
net_buf_add_mem(buf, data, size);
int err = bt_l2cap_chan_send(&ch->le_chan.chan, buf);
if (err < 0) {
net_buf_unref(buf);
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
bt_status_t bt_sal_l2cap_give_incoming_credits(bt_address_t* addr, uint16_t local_cid, uint16_t credits)
{
sal_l2cap_channel_t* ch = find_channel_by_cid(local_cid);
if (!ch) {
return BT_STATUS_NOT_FOUND;
}
/* Credits are managed automatically by the zblue stack when using
* the standard recv callback. Manual credit management via
* bt_l2cap_chan_give_credits is only needed with seg_recv. */
return BT_STATUS_SUCCESS;
}

View File

@ -0,0 +1,53 @@
/****************************************************************************
* Copyright (C) 2026 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 <stdint.h>
#include "bt_addr.h"
#include "bt_pan.h"
#include "bt_status.h"
#include "sal_pan_interface.h"
#include "utils/log.h"
/* PAN profile requires BNEP which is not yet implemented in zblue stack.
* These stubs allow compilation but return NOT_SUPPORTED at runtime. */
bt_status_t bt_sal_pan_init(uint8_t max_connections, pan_role_t role)
{
BT_LOGW("PAN SAL: not implemented (BNEP not available in zblue)");
return BT_STATUS_NOT_SUPPORTED;
}
void bt_sal_pan_cleanup(void)
{
}
bt_status_t bt_sal_pan_connect(bt_address_t* addr, pan_role_t dst_role, pan_role_t src_role)
{
return BT_STATUS_NOT_SUPPORTED;
}
bt_status_t bt_sal_pan_disconnect(bt_address_t* addr)
{
return BT_STATUS_NOT_SUPPORTED;
}
bt_status_t bt_sal_pan_write(bt_address_t* addr, uint16_t protocol,
const uint8_t* dst_addr, const uint8_t* src_addr,
const uint8_t* data, uint16_t length)
{
return BT_STATUS_NOT_SUPPORTED;
}