Compare commits
7 Commits
dev
...
feature/fi
| Author | SHA1 | Date |
|---|---|---|
|
|
fb31f0d842 | |
|
|
4950ce00cb | |
|
|
02a0271b76 | |
|
|
9027fe26ea | |
|
|
2765a97128 | |
|
|
49daf01a46 | |
|
|
ed6ec45934 |
1
Kconfig
1
Kconfig
|
|
@ -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
|
||||
|
|
|
|||
20
Makefile
20
Makefile
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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, ¶m);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue