2015-05-06 10:24:52 +03:00
|
|
|
|
/* l2cap.c - L2CAP handling */
|
|
|
|
|
|
|
|
|
|
/*
|
2016-06-10 12:10:18 +03:00
|
|
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
2023-05-15 10:30:41 +02:00
|
|
|
|
* Copyright (c) 2023 Nordic Semiconductor
|
2015-05-06 10:24:52 +03:00
|
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2015-05-06 10:24:52 +03:00
|
|
|
|
*/
|
|
|
|
|
|
includes: prefer <zephyr/kernel.h> over <zephyr/zephyr.h>
As of today <zephyr/zephyr.h> is 100% equivalent to <zephyr/kernel.h>.
This patch proposes to then include <zephyr/kernel.h> instead of
<zephyr/zephyr.h> since it is more clear that you are including the
Kernel APIs and (probably) nothing else. <zephyr/zephyr.h> sounds like a
catch-all header that may be confusing. Most applications need to
include a bunch of other things to compile, e.g. driver headers or
subsystem headers like BT, logging, etc.
The idea of a catch-all header in Zephyr is probably not feasible
anyway. Reason is that Zephyr is not a library, like it could be for
example `libpython`. Zephyr provides many utilities nowadays: a kernel,
drivers, subsystems, etc and things will likely grow. A catch-all header
would be massive, difficult to keep up-to-date. It is also likely that
an application will only build a small subset. Note that subsystem-level
headers may use a catch-all approach to make things easier, though.
NOTE: This patch is **NOT** removing the header, just removing its usage
in-tree. I'd advocate for its deprecation (add a #warning on it), but I
understand many people will have concerns.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-08-25 09:58:46 +02:00
|
|
|
|
#include <zephyr/kernel.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
|
#include <zephyr/sys/atomic.h>
|
2023-05-15 15:50:28 +02:00
|
|
|
|
#include <zephyr/sys/iterable_sections.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
|
#include <zephyr/sys/byteorder.h>
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#include <zephyr/sys/math_extras.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
|
#include <zephyr/sys/util.h>
|
|
|
|
|
|
|
|
|
|
#include <zephyr/bluetooth/hci.h>
|
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
|
|
|
#include <zephyr/bluetooth/conn.h>
|
|
|
|
|
#include <zephyr/bluetooth/l2cap.h>
|
|
|
|
|
#include <zephyr/drivers/bluetooth/hci_driver.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
#define LOG_DBG_ENABLED IS_ENABLED(CONFIG_BT_L2CAP_LOG_LEVEL_DBG)
|
2017-05-10 16:27:16 +02:00
|
|
|
|
|
2015-04-28 10:43:14 +03:00
|
|
|
|
#include "hci_core.h"
|
2015-06-15 11:05:35 +03:00
|
|
|
|
#include "conn_internal.h"
|
2015-10-05 13:53:03 +03:00
|
|
|
|
#include "l2cap_internal.h"
|
2021-11-08 13:11:36 +01:00
|
|
|
|
#include "keys.h"
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
|
LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);
|
|
|
|
|
|
2023-08-29 09:45:51 +00:00
|
|
|
|
#define LE_CHAN_RTX(_w) CONTAINER_OF(k_work_delayable_from_work(_w), \
|
|
|
|
|
struct bt_l2cap_le_chan, rtx_work)
|
2019-11-26 13:08:24 +02:00
|
|
|
|
#define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work)
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
2015-10-29 11:14:33 +02:00
|
|
|
|
#define L2CAP_LE_MIN_MTU 23
|
2020-02-26 10:29:23 -08:00
|
|
|
|
#define L2CAP_ECRED_MIN_MTU 64
|
2017-05-04 14:09:10 +03:00
|
|
|
|
|
2021-04-27 10:38:23 +02:00
|
|
|
|
#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_BUF_ACL_RX_COUNT - 1)
|
2017-05-04 14:09:10 +03:00
|
|
|
|
|
2016-10-18 14:13:54 +02:00
|
|
|
|
#define L2CAP_LE_CID_DYN_START 0x0040
|
|
|
|
|
#define L2CAP_LE_CID_DYN_END 0x007f
|
|
|
|
|
#define L2CAP_LE_CID_IS_DYN(_cid) \
|
|
|
|
|
(_cid >= L2CAP_LE_CID_DYN_START && _cid <= L2CAP_LE_CID_DYN_END)
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2018-05-18 07:43:21 +03:00
|
|
|
|
#define L2CAP_LE_PSM_FIXED_START 0x0001
|
|
|
|
|
#define L2CAP_LE_PSM_FIXED_END 0x007f
|
|
|
|
|
#define L2CAP_LE_PSM_DYN_START 0x0080
|
|
|
|
|
#define L2CAP_LE_PSM_DYN_END 0x00ff
|
2020-02-27 14:55:00 -08:00
|
|
|
|
#define L2CAP_LE_PSM_IS_DYN(_psm) \
|
|
|
|
|
(_psm >= L2CAP_LE_PSM_DYN_START && _psm <= L2CAP_LE_PSM_DYN_END)
|
2015-11-09 16:20:16 +02:00
|
|
|
|
|
2016-11-13 19:18:39 +02:00
|
|
|
|
#define L2CAP_CONN_TIMEOUT K_SECONDS(40)
|
2017-10-11 14:02:25 +03:00
|
|
|
|
#define L2CAP_DISC_TIMEOUT K_SECONDS(2)
|
2019-12-12 08:36:21 -08:00
|
|
|
|
#define L2CAP_RTX_TIMEOUT K_SECONDS(2)
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
2020-10-12 10:12:42 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2019-11-18 13:46:12 +02:00
|
|
|
|
/* Dedicated pool for disconnect buffers so they are guaranteed to be send
|
|
|
|
|
* even in case of data congestion due to flooding.
|
|
|
|
|
*/
|
|
|
|
|
NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
|
2020-10-12 10:12:42 +02:00
|
|
|
|
BT_L2CAP_BUF_SIZE(
|
2021-01-18 10:34:40 +01:00
|
|
|
|
sizeof(struct bt_l2cap_sig_hdr) +
|
2020-10-12 10:12:42 +02:00
|
|
|
|
sizeof(struct bt_l2cap_disconn_req)),
|
2023-07-04 11:17:17 +02:00
|
|
|
|
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
|
2019-11-18 13:46:12 +02:00
|
|
|
|
|
2015-11-04 14:46:56 +02:00
|
|
|
|
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
|
|
|
|
|
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
|
2016-07-19 15:07:26 +02:00
|
|
|
|
|
2024-01-09 14:40:08 +01:00
|
|
|
|
static sys_slist_t servers = SYS_SLIST_STATIC_INIT(&servers);
|
2015-05-21 18:53:13 +03:00
|
|
|
|
|
2023-11-22 16:16:29 +01:00
|
|
|
|
static void l2cap_tx_buf_destroy(struct bt_conn *conn, struct net_buf *buf, int err)
|
|
|
|
|
{
|
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2015-11-05 21:01:20 +02:00
|
|
|
|
|
2015-10-02 16:21:18 +03:00
|
|
|
|
/* L2CAP signalling channel specific context */
|
|
|
|
|
struct bt_l2cap {
|
|
|
|
|
/* The channel this context is associated with */
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_l2cap_le_chan chan;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
};
|
|
|
|
|
|
2022-01-26 15:41:36 +01:00
|
|
|
|
static const struct bt_l2cap_ecred_cb *ecred_cb;
|
2017-08-09 09:21:11 +03:00
|
|
|
|
static struct bt_l2cap bt_l2cap_pool[CONFIG_BT_MAX_CONN];
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2022-01-26 15:41:36 +01:00
|
|
|
|
void bt_l2cap_register_ecred_cb(const struct bt_l2cap_ecred_cb *cb)
|
|
|
|
|
{
|
|
|
|
|
ecred_cb = cb;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static uint8_t get_ident(void)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static uint8_t ident;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
ident++;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
/* handle integer overflow (0 is not valid) */
|
2016-05-26 14:03:04 +02:00
|
|
|
|
if (!ident) {
|
|
|
|
|
ident++;
|
2015-05-05 10:50:14 +03:00
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return ident;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static struct bt_l2cap_le_chan *l2cap_chan_alloc_cid(struct bt_conn *conn,
|
|
|
|
|
struct bt_l2cap_chan *chan)
|
2015-05-21 19:03:17 +03:00
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t cid;
|
2015-05-21 19:03:17 +03:00
|
|
|
|
|
2015-11-02 11:16:37 +02:00
|
|
|
|
/*
|
|
|
|
|
* No action needed if there's already a CID allocated, e.g. in
|
|
|
|
|
* the case of a fixed channel.
|
|
|
|
|
*/
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->rx.cid > 0) {
|
|
|
|
|
return le_chan;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 14:13:54 +02:00
|
|
|
|
for (cid = L2CAP_LE_CID_DYN_START; cid <= L2CAP_LE_CID_DYN_END; cid++) {
|
2019-12-18 23:57:42 +02:00
|
|
|
|
if (!bt_l2cap_le_lookup_rx_cid(conn, cid)) {
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->rx.cid = cid;
|
|
|
|
|
return le_chan;
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
|
|
|
|
return NULL;
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-28 16:15:57 +03:00
|
|
|
|
static struct bt_l2cap_le_chan *
|
2020-05-27 11:26:57 -05:00
|
|
|
|
__l2cap_lookup_ident(struct bt_conn *conn, uint16_t ident, bool remove)
|
2016-06-28 16:15:57 +03:00
|
|
|
|
{
|
2017-03-21 15:48:03 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
sys_snode_t *prev = NULL;
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->ident == ident) {
|
2017-03-21 15:48:03 +02:00
|
|
|
|
if (remove) {
|
|
|
|
|
sys_slist_remove(&conn->channels, prev,
|
|
|
|
|
&chan->node);
|
|
|
|
|
}
|
2016-07-19 15:07:26 +02:00
|
|
|
|
return BT_L2CAP_LE_CHAN(chan);
|
2016-06-28 16:15:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
prev = &chan->node;
|
2016-07-19 15:07:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2016-07-19 15:07:26 +02:00
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
void bt_l2cap_chan_remove(struct bt_conn *conn, struct bt_l2cap_chan *ch)
|
2016-07-19 15:07:26 +02:00
|
|
|
|
{
|
2017-03-21 15:48:03 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
sys_snode_t *prev = NULL;
|
2016-07-19 15:07:26 +02:00
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
|
|
|
|
if (chan == ch) {
|
|
|
|
|
sys_slist_remove(&conn->channels, prev, &chan->node);
|
|
|
|
|
return;
|
2016-07-19 15:07:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
prev = &chan->node;
|
2016-06-28 16:15:57 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 14:48:07 +02:00
|
|
|
|
const char *bt_l2cap_chan_state_str(bt_l2cap_chan_state_t state)
|
|
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
case BT_L2CAP_DISCONNECTED:
|
|
|
|
|
return "disconnected";
|
2022-02-22 15:19:03 +01:00
|
|
|
|
case BT_L2CAP_CONNECTING:
|
|
|
|
|
return "connecting";
|
2016-11-03 14:48:07 +02:00
|
|
|
|
case BT_L2CAP_CONFIG:
|
|
|
|
|
return "config";
|
|
|
|
|
case BT_L2CAP_CONNECTED:
|
|
|
|
|
return "connected";
|
2022-02-22 15:19:03 +01:00
|
|
|
|
case BT_L2CAP_DISCONNECTING:
|
|
|
|
|
return "disconnecting";
|
2016-11-03 14:48:07 +02:00
|
|
|
|
default:
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 15:17:09 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2022-11-02 14:31:13 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_LOG_LEVEL_DBG)
|
2016-11-03 14:48:07 +02:00
|
|
|
|
void bt_l2cap_chan_set_state_debug(struct bt_l2cap_chan *chan,
|
|
|
|
|
bt_l2cap_chan_state_t state,
|
|
|
|
|
const char *func, int line)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p psm 0x%04x %s -> %s", chan, le_chan->psm,
|
|
|
|
|
bt_l2cap_chan_state_str(le_chan->state), bt_l2cap_chan_state_str(state));
|
2016-11-03 14:48:07 +02:00
|
|
|
|
|
|
|
|
|
/* check transitions validness */
|
|
|
|
|
switch (state) {
|
|
|
|
|
case BT_L2CAP_DISCONNECTED:
|
|
|
|
|
/* regardless of old state always allows this state */
|
|
|
|
|
break;
|
2022-02-22 15:19:03 +01:00
|
|
|
|
case BT_L2CAP_CONNECTING:
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->state != BT_L2CAP_DISCONNECTED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("%s()%d: invalid transition", func, line);
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_CONFIG:
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->state != BT_L2CAP_CONNECTING) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("%s()%d: invalid transition", func, line);
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_CONNECTED:
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->state != BT_L2CAP_CONFIG &&
|
|
|
|
|
le_chan->state != BT_L2CAP_CONNECTING) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("%s()%d: invalid transition", func, line);
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
2022-02-22 15:19:03 +01:00
|
|
|
|
case BT_L2CAP_DISCONNECTING:
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->state != BT_L2CAP_CONFIG &&
|
|
|
|
|
le_chan->state != BT_L2CAP_CONNECTED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("%s()%d: invalid transition", func, line);
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("%s()%d: unknown (%u) state was set", func, line, state);
|
2016-11-03 14:48:07 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->state = state;
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
void bt_l2cap_chan_set_state(struct bt_l2cap_chan *chan,
|
|
|
|
|
bt_l2cap_chan_state_t state)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
BT_L2CAP_LE_CHAN(chan)->state = state;
|
2016-11-03 14:48:07 +02:00
|
|
|
|
}
|
2022-11-02 14:31:13 +01:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_LOG_LEVEL_DBG */
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2016-11-03 14:48:07 +02:00
|
|
|
|
|
2016-07-11 13:21:41 +03:00
|
|
|
|
void bt_l2cap_chan_del(struct bt_l2cap_chan *chan)
|
2016-06-28 16:15:57 +03:00
|
|
|
|
{
|
2020-02-26 16:42:26 -08:00
|
|
|
|
const struct bt_l2cap_chan_ops *ops = chan->ops;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p chan %p", chan->conn, chan);
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
|
|
|
|
if (!chan->conn) {
|
|
|
|
|
goto destroy;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 16:42:26 -08:00
|
|
|
|
if (ops->disconnected) {
|
|
|
|
|
ops->disconnected(chan);
|
2016-06-28 16:15:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chan->conn = NULL;
|
|
|
|
|
|
|
|
|
|
destroy:
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* Reset internal members of common channel */
|
2016-11-03 14:48:07 +02:00
|
|
|
|
bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECTED);
|
2022-01-29 09:30:12 +08:00
|
|
|
|
BT_L2CAP_LE_CHAN(chan)->psm = 0U;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
#endif
|
2016-06-28 16:15:57 +03:00
|
|
|
|
if (chan->destroy) {
|
|
|
|
|
chan->destroy(chan);
|
|
|
|
|
}
|
2020-02-26 16:42:26 -08:00
|
|
|
|
|
|
|
|
|
if (ops->released) {
|
|
|
|
|
ops->released(chan);
|
|
|
|
|
}
|
2016-06-28 16:15:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2016-11-10 11:22:07 +02:00
|
|
|
|
static void l2cap_rtx_timeout(struct k_work *work)
|
2016-06-28 16:15:57 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_le_chan *chan = LE_CHAN_RTX(work);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct bt_conn *conn = chan->chan.conn;
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("chan %p timeout", chan);
|
2016-06-28 16:15:57 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* Remove other channels if pending on the same ident */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
while ((chan = l2cap_remove_ident(conn, chan->ident))) {
|
2020-02-26 10:29:23 -08:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
}
|
2016-07-11 13:21:41 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-26 13:08:24 +02:00
|
|
|
|
static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan,
|
|
|
|
|
struct net_buf *buf);
|
2018-10-05 15:47:53 +03:00
|
|
|
|
|
|
|
|
|
static void l2cap_rx_process(struct k_work *work)
|
|
|
|
|
{
|
2019-11-26 13:08:24 +02:00
|
|
|
|
struct bt_l2cap_le_chan *ch = CHAN_RX(work);
|
2018-10-05 15:47:53 +03:00
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
2019-11-26 13:08:24 +02:00
|
|
|
|
while ((buf = net_buf_get(&ch->rx_queue, K_NO_WAIT))) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("ch %p buf %p", ch, buf);
|
2019-11-26 13:08:24 +02:00
|
|
|
|
l2cap_chan_le_recv(ch, buf);
|
2018-10-05 15:47:53 +03:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-26 13:08:24 +02:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2018-10-05 15:47:53 +03:00
|
|
|
|
|
2016-07-11 13:21:41 +03:00
|
|
|
|
void bt_l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
|
|
|
|
bt_l2cap_chan_destroy_t destroy)
|
|
|
|
|
{
|
|
|
|
|
/* Attach channel to the connection */
|
2017-03-21 15:48:03 +02:00
|
|
|
|
sys_slist_append(&conn->channels, &chan->node);
|
2016-07-11 13:21:41 +03:00
|
|
|
|
chan->conn = conn;
|
|
|
|
|
chan->destroy = destroy;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p chan %p", conn, chan);
|
2016-06-28 16:15:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-08 16:30:58 +03:00
|
|
|
|
static bool l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_destroy_t destroy)
|
2015-05-21 19:03:17 +03:00
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2017-03-21 15:48:03 +02:00
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = l2cap_chan_alloc_cid(conn, chan);
|
2017-03-21 15:48:03 +02:00
|
|
|
|
#else
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(chan);
|
2017-03-21 15:48:03 +02:00
|
|
|
|
#endif
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (!le_chan) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to allocate L2CAP channel ID");
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return false;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
atomic_clear(chan->status);
|
|
|
|
|
|
|
|
|
|
bt_l2cap_chan_add(conn, chan, destroy);
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2021-04-12 13:03:22 +02:00
|
|
|
|
/* All dynamic channels have the destroy handler which makes sure that
|
|
|
|
|
* the RTX work structure is properly released with a cancel sync.
|
|
|
|
|
* The fixed signal channel is only removed when disconnected and the
|
|
|
|
|
* disconnected handler is always called from the workqueue itself so
|
|
|
|
|
* canceling from there should always succeed.
|
|
|
|
|
*/
|
2022-01-29 09:30:12 +08:00
|
|
|
|
k_work_init_delayable(&le_chan->rtx_work, l2cap_rtx_timeout);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (L2CAP_LE_CID_IS_DYN(le_chan->rx.cid)) {
|
|
|
|
|
k_work_init(&le_chan->rx_work, l2cap_rx_process);
|
|
|
|
|
k_fifo_init(&le_chan->rx_queue);
|
2022-02-22 15:19:03 +01:00
|
|
|
|
bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECTING);
|
2017-01-17 12:35:35 +02:00
|
|
|
|
}
|
2019-11-26 13:08:24 +02:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2017-01-17 12:35:35 +02:00
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return true;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bt_l2cap_connected(struct bt_conn *conn)
|
|
|
|
|
{
|
2015-05-21 19:03:17 +03:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2017-01-17 19:10:33 +02:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
2016-05-10 17:26:21 +02:00
|
|
|
|
bt_l2cap_br_connected(conn);
|
|
|
|
|
return;
|
2016-01-13 12:10:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 23:05:54 +01:00
|
|
|
|
STRUCT_SECTION_FOREACH(bt_l2cap_fixed_chan, fchan) {
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2016-06-13 14:56:52 +03:00
|
|
|
|
|
2015-10-02 16:21:18 +03:00
|
|
|
|
if (fchan->accept(conn, &chan) < 0) {
|
|
|
|
|
continue;
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(chan);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
/* Fill up remaining fixed channel context attached in
|
|
|
|
|
* fchan->accept()
|
|
|
|
|
*/
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->rx.cid = fchan->cid;
|
|
|
|
|
le_chan->tx.cid = fchan->cid;
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
2020-01-28 09:51:44 +01:00
|
|
|
|
if (!l2cap_chan_add(conn, chan, fchan->destroy)) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
|
|
|
|
if (chan->ops->connected) {
|
|
|
|
|
chan->ops->connected(chan);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
}
|
2019-05-22 22:46:25 +03:00
|
|
|
|
|
|
|
|
|
/* Always set output status to fixed channels */
|
|
|
|
|
atomic_set_bit(chan->status, BT_L2CAP_STATUS_OUT);
|
|
|
|
|
|
|
|
|
|
if (chan->ops->status) {
|
|
|
|
|
chan->ops->status(chan, chan->status);
|
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
}
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-02 16:21:18 +03:00
|
|
|
|
void bt_l2cap_disconnected(struct bt_conn *conn)
|
2015-05-21 19:03:17 +03:00
|
|
|
|
{
|
2017-03-21 15:48:03 +02:00
|
|
|
|
struct bt_l2cap_chan *chan, *next;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2024-01-31 17:20:29 +08:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
|
|
|
|
bt_l2cap_br_disconnected(conn);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) {
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(chan);
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
2015-10-02 16:21:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
static struct net_buf *l2cap_create_le_sig_pdu(uint8_t code, uint8_t ident,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t len)
|
2016-11-08 16:55:09 +02:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
2019-11-18 13:46:12 +02:00
|
|
|
|
struct net_buf_pool *pool = NULL;
|
2023-11-22 16:59:25 +01:00
|
|
|
|
struct net_buf *buf;
|
2019-11-18 13:46:12 +02:00
|
|
|
|
|
2020-10-12 10:12:42 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2019-11-18 13:46:12 +02:00
|
|
|
|
if (code == BT_L2CAP_DISCONN_REQ) {
|
|
|
|
|
pool = &disc_pool;
|
|
|
|
|
}
|
2020-10-12 10:12:42 +02:00
|
|
|
|
#endif
|
2019-08-27 14:52:37 +03:00
|
|
|
|
/* Don't wait more than the minimum RTX timeout of 2 seconds */
|
2019-12-12 08:36:21 -08:00
|
|
|
|
buf = bt_l2cap_create_pdu_timeout(pool, 0, L2CAP_RTX_TIMEOUT);
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
/* If it was not possible to allocate a buffer within the
|
|
|
|
|
* timeout return NULL.
|
|
|
|
|
*/
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to allocate buffer for op 0x%02x", code);
|
2019-08-27 14:52:37 +03:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2016-11-08 16:55:09 +02:00
|
|
|
|
|
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
|
|
|
hdr->code = code;
|
|
|
|
|
hdr->ident = ident;
|
|
|
|
|
hdr->len = sys_cpu_to_le16(len);
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
/* Send the buffer and release it in case of failure.
|
|
|
|
|
* Any other cleanup in failure to send should be handled by the disconnected
|
|
|
|
|
* handler.
|
|
|
|
|
*/
|
2024-01-03 09:37:05 +01:00
|
|
|
|
static inline int l2cap_send(struct bt_conn *conn, uint16_t cid, struct net_buf *buf)
|
2021-04-29 18:06:49 +02:00
|
|
|
|
{
|
2024-01-03 09:37:05 +01:00
|
|
|
|
int err = bt_l2cap_send(conn, cid, buf);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
2021-04-29 18:06:49 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
2024-01-03 09:37:05 +01:00
|
|
|
|
|
|
|
|
|
return err;
|
2021-04-29 18:06:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
static void l2cap_chan_send_req(struct bt_l2cap_chan *chan,
|
2020-04-06 13:33:41 +02:00
|
|
|
|
struct net_buf *buf, k_timeout_t timeout)
|
2016-11-02 13:20:11 +02:00
|
|
|
|
{
|
2024-01-03 09:37:05 +01:00
|
|
|
|
if (l2cap_send(chan->conn, BT_L2CAP_CID_LE_SIG, buf)) {
|
2021-04-29 18:06:49 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126:
|
|
|
|
|
*
|
|
|
|
|
* The value of this timer is implementation-dependent but the minimum
|
|
|
|
|
* initial value is 1 second and the maximum initial value is 60
|
|
|
|
|
* seconds. One RTX timer shall exist for each outstanding signaling
|
|
|
|
|
* request, including each Echo Request. The timer disappears on the
|
|
|
|
|
* final expiration, when the response is received, or the physical
|
|
|
|
|
* link is lost.
|
|
|
|
|
*/
|
2022-01-29 09:30:12 +08:00
|
|
|
|
k_work_reschedule(&(BT_L2CAP_LE_CHAN(chan)->rtx_work), timeout);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
|
|
|
|
|
{
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
struct bt_l2cap_le_conn_req *req;
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->ident = get_ident();
|
2016-11-08 16:55:09 +02:00
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_REQ,
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->ident, sizeof(*req));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2022-01-29 09:30:12 +08:00
|
|
|
|
req->psm = sys_cpu_to_le16(ch->psm);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
req->scid = sys_cpu_to_le16(ch->rx.cid);
|
|
|
|
|
req->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
|
|
|
|
req->mps = sys_cpu_to_le16(ch->rx.mps);
|
2023-05-15 10:30:41 +02:00
|
|
|
|
req->credits = sys_cpu_to_le16(ch->rx.credits);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
l2cap_chan_send_req(&ch->chan, buf, L2CAP_CONN_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
static int l2cap_ecred_conn_req(struct bt_l2cap_chan **chan, int channels)
|
|
|
|
|
{
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
struct bt_l2cap_ecred_conn_req *req;
|
|
|
|
|
struct bt_l2cap_le_chan *ch;
|
|
|
|
|
int i;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint8_t ident;
|
2022-06-14 13:33:20 +02:00
|
|
|
|
uint16_t req_psm;
|
2023-03-02 14:13:17 +01:00
|
|
|
|
uint16_t req_mtu;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (!chan || !channels) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ident = get_ident();
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_CONN_REQ, ident,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
sizeof(*req) +
|
2020-05-27 11:26:57 -05:00
|
|
|
|
(channels * sizeof(uint16_t)));
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
|
|
|
|
|
|
ch = BT_L2CAP_LE_CHAN(chan[0]);
|
|
|
|
|
|
|
|
|
|
/* Init common parameters */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
req->psm = sys_cpu_to_le16(ch->psm);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
req->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
|
|
|
|
req->mps = sys_cpu_to_le16(ch->rx.mps);
|
2023-05-15 10:30:41 +02:00
|
|
|
|
req->credits = sys_cpu_to_le16(ch->rx.credits);
|
2022-06-14 13:33:20 +02:00
|
|
|
|
req_psm = ch->psm;
|
2023-03-02 14:13:17 +01:00
|
|
|
|
req_mtu = ch->tx.mtu;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < channels; i++) {
|
|
|
|
|
ch = BT_L2CAP_LE_CHAN(chan[i]);
|
|
|
|
|
|
2022-06-14 13:33:20 +02:00
|
|
|
|
__ASSERT(ch->psm == req_psm,
|
2022-05-06 10:04:10 +02:00
|
|
|
|
"The PSM shall be the same for channels in the same request.");
|
2023-03-02 14:13:17 +01:00
|
|
|
|
__ASSERT(ch->tx.mtu == req_mtu,
|
|
|
|
|
"The MTU shall be the same for channels in the same request.");
|
2022-05-06 10:04:10 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->ident = ident;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
net_buf_add_le16(buf, ch->rx.cid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l2cap_chan_send_req(*chan, buf, L2CAP_CONN_TIMEOUT);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, uint8_t status)
|
2016-11-02 13:20:11 +02:00
|
|
|
|
{
|
2020-07-30 11:35:25 +02:00
|
|
|
|
int err;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le = BT_L2CAP_LE_CHAN(chan);
|
2020-07-30 11:35:25 +02:00
|
|
|
|
|
2020-04-29 16:22:47 -07:00
|
|
|
|
/* Skip channels that are not pending waiting for encryption */
|
|
|
|
|
if (!atomic_test_and_clear_bit(chan->status,
|
|
|
|
|
BT_L2CAP_STATUS_ENCRYPT_PENDING)) {
|
2016-11-02 13:20:11 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status) {
|
2020-07-30 11:35:25 +02:00
|
|
|
|
goto fail;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le->ident) {
|
2022-01-26 15:45:08 +01:00
|
|
|
|
struct bt_l2cap_chan *echan[L2CAP_ECRED_CHAN_MAX_PER_REQ];
|
2022-02-09 17:00:22 +01:00
|
|
|
|
struct bt_l2cap_chan *ch;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
int i = 0;
|
|
|
|
|
|
2022-02-09 17:00:22 +01:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&chan->conn->channels, ch, node) {
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le->ident == BT_L2CAP_LE_CHAN(ch)->ident) {
|
2022-01-26 15:45:08 +01:00
|
|
|
|
__ASSERT(i < L2CAP_ECRED_CHAN_MAX_PER_REQ,
|
|
|
|
|
"There can only be L2CAP_ECRED_CHAN_MAX_PER_REQ channels "
|
2022-02-09 17:00:22 +01:00
|
|
|
|
"from the same request.");
|
|
|
|
|
atomic_clear_bit(ch->status, BT_L2CAP_STATUS_ENCRYPT_PENDING);
|
|
|
|
|
echan[i++] = ch;
|
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Retry ecred connect */
|
|
|
|
|
l2cap_ecred_conn_req(echan, i);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* Retry to connect */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
err = l2cap_le_conn_req(le);
|
2020-07-30 11:35:25 +02:00
|
|
|
|
if (err) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
fail:
|
|
|
|
|
bt_l2cap_chan_remove(chan->conn, chan);
|
|
|
|
|
bt_l2cap_chan_del(chan);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
2020-07-30 12:56:11 +02:00
|
|
|
|
void bt_l2cap_security_changed(struct bt_conn *conn, uint8_t hci_status)
|
2015-10-02 16:21:18 +03:00
|
|
|
|
{
|
2020-07-30 13:10:56 +02:00
|
|
|
|
struct bt_l2cap_chan *chan, *next;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2017-01-17 19:10:33 +02:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
2016-09-13 09:38:10 +02:00
|
|
|
|
l2cap_br_encrypt_change(conn, hci_status);
|
2016-07-08 16:05:58 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 13:10:56 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) {
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2016-11-02 13:20:11 +02:00
|
|
|
|
l2cap_le_encrypt_change(chan, hci_status);
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-10-02 16:21:18 +03:00
|
|
|
|
if (chan->ops->encrypt_change) {
|
2016-09-13 09:38:10 +02:00
|
|
|
|
chan->ops->encrypt_change(chan, hci_status);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-21 19:03:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-27 14:23:54 +03:00
|
|
|
|
struct net_buf *bt_l2cap_create_pdu_timeout(struct net_buf_pool *pool,
|
2020-04-06 13:33:41 +02:00
|
|
|
|
size_t reserve,
|
|
|
|
|
k_timeout_t timeout)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
2019-08-27 14:23:54 +03:00
|
|
|
|
return bt_conn_create_pdu_timeout(pool,
|
|
|
|
|
sizeof(struct bt_l2cap_hdr) + reserve,
|
|
|
|
|
timeout);
|
2015-05-20 14:58:18 +03:00
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
int bt_l2cap_send_cb(struct bt_conn *conn, uint16_t cid, struct net_buf *buf,
|
2019-12-02 15:46:00 +02:00
|
|
|
|
bt_conn_tx_cb_t cb, void *user_data)
|
2015-05-20 14:58:18 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_hdr *hdr;
|
2015-05-06 10:59:43 +03:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
LOG_DBG("conn %p cid %u len %zu", conn, cid, buf->len);
|
2017-04-04 15:18:18 +03:00
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
hdr = net_buf_push(buf, sizeof(*hdr));
|
2015-05-20 14:58:18 +03:00
|
|
|
|
hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
|
hdr->cid = sys_cpu_to_le16(cid);
|
|
|
|
|
|
2019-12-02 15:46:00 +02:00
|
|
|
|
return bt_conn_send_cb(conn, buf, cb, user_data);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void l2cap_send_reject(struct bt_conn *conn, uint8_t ident,
|
|
|
|
|
uint16_t reason, void *data, uint8_t data_len)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_cmd_reject *rej;
|
2015-10-28 10:41:10 +02:00
|
|
|
|
struct net_buf *buf;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_CMD_REJECT, ident,
|
2016-11-08 16:55:09 +02:00
|
|
|
|
sizeof(*rej) + data_len);
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
rej = net_buf_add(buf, sizeof(*rej));
|
2015-10-28 15:35:53 +02:00
|
|
|
|
rej->reason = sys_cpu_to_le16(reason);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2016-06-06 09:23:20 +02:00
|
|
|
|
if (data) {
|
2016-12-25 09:55:58 +02:00
|
|
|
|
net_buf_add_mem(buf, data, data_len);
|
2016-06-06 09:23:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
static void le_conn_param_rsp(struct bt_l2cap *l2cap, struct net_buf *buf)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_conn_param_rsp *rsp = (void *)buf->data;
|
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn param rsp");
|
2015-04-28 10:43:14 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("LE conn param rsp result %u", sys_le16_to_cpu(rsp->result));
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-10-28 10:41:10 +02:00
|
|
|
|
struct net_buf *buf)
|
2015-06-12 14:47:54 +02:00
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
2017-01-13 13:20:22 +02:00
|
|
|
|
struct bt_le_conn_param param;
|
2015-06-12 14:47:54 +02:00
|
|
|
|
struct bt_l2cap_conn_param_rsp *rsp;
|
|
|
|
|
struct bt_l2cap_conn_param_req *req = (void *)buf->data;
|
2017-01-16 13:26:53 +02:00
|
|
|
|
bool accepted;
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn update param req");
|
2015-06-12 14:47:54 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-11 15:29:40 +02:00
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Not connected");
|
2022-08-11 15:29:40 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 10:43:02 +02:00
|
|
|
|
if (conn->role != BT_HCI_ROLE_CENTRAL) {
|
2016-06-06 09:23:20 +02:00
|
|
|
|
l2cap_send_reject(conn, ident, BT_L2CAP_REJ_NOT_UNDERSTOOD,
|
|
|
|
|
NULL, 0);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-13 13:20:22 +02:00
|
|
|
|
param.interval_min = sys_le16_to_cpu(req->min_interval);
|
|
|
|
|
param.interval_max = sys_le16_to_cpu(req->max_interval);
|
|
|
|
|
param.latency = sys_le16_to_cpu(req->latency);
|
|
|
|
|
param.timeout = sys_le16_to_cpu(req->timeout);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("min 0x%04x max 0x%04x latency: 0x%04x timeout: 0x%04x", param.interval_min,
|
|
|
|
|
param.interval_max, param.latency, param.timeout);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_CONN_PARAM_RSP, ident,
|
2016-11-08 16:55:09 +02:00
|
|
|
|
sizeof(*rsp));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
2017-01-16 13:26:53 +02:00
|
|
|
|
accepted = le_param_req(conn, ¶m);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
2017-01-16 13:26:53 +02:00
|
|
|
|
if (accepted) {
|
2015-07-29 13:45:33 +03:00
|
|
|
|
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED);
|
|
|
|
|
} else {
|
|
|
|
|
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
2017-01-16 13:26:53 +02:00
|
|
|
|
if (accepted) {
|
2017-01-13 13:20:22 +02:00
|
|
|
|
bt_conn_le_conn_update(conn, ¶m);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
struct bt_l2cap_chan *bt_l2cap_le_lookup_tx_cid(struct bt_conn *conn,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t cid)
|
2017-03-21 15:48:03 +02:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->tx.cid == cid) {
|
|
|
|
|
return chan;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t cid)
|
2017-03-21 15:48:03 +02:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->rx.cid == cid) {
|
|
|
|
|
return chan;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2022-10-26 09:34:55 +01:00
|
|
|
|
struct bt_l2cap_server *bt_l2cap_server_lookup_psm(uint16_t psm)
|
2015-10-08 13:45:40 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_server *server;
|
|
|
|
|
|
2017-03-23 15:03:58 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&servers, server, node) {
|
2015-10-08 13:45:40 +03:00
|
|
|
|
if (server->psm == psm) {
|
|
|
|
|
return server;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bt_l2cap_server_register(struct bt_l2cap_server *server)
|
|
|
|
|
{
|
2018-05-18 07:43:21 +03:00
|
|
|
|
if (!server->accept) {
|
2015-10-08 13:45:40 +03:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-18 07:43:21 +03:00
|
|
|
|
if (server->psm) {
|
|
|
|
|
if (server->psm < L2CAP_LE_PSM_FIXED_START ||
|
|
|
|
|
server->psm > L2CAP_LE_PSM_DYN_END) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if given PSM is already in use */
|
2022-10-26 09:34:55 +01:00
|
|
|
|
if (bt_l2cap_server_lookup_psm(server->psm)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("PSM already registered");
|
2018-05-18 07:43:21 +03:00
|
|
|
|
return -EADDRINUSE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t psm;
|
2018-05-18 07:43:21 +03:00
|
|
|
|
|
|
|
|
|
for (psm = L2CAP_LE_PSM_DYN_START;
|
|
|
|
|
psm <= L2CAP_LE_PSM_DYN_END; psm++) {
|
2022-10-26 09:34:55 +01:00
|
|
|
|
if (!bt_l2cap_server_lookup_psm(psm)) {
|
2018-05-18 07:43:21 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (psm > L2CAP_LE_PSM_DYN_END) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("No free dynamic PSMs available");
|
2018-05-18 07:43:21 +03:00
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("Allocated PSM 0x%04x for new server", psm);
|
2018-05-18 07:43:21 +03:00
|
|
|
|
server->psm = psm;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 15:50:48 +02:00
|
|
|
|
if (server->sec_level > BT_SECURITY_L4) {
|
2016-11-01 11:05:35 +02:00
|
|
|
|
return -EINVAL;
|
2019-08-26 15:50:48 +02:00
|
|
|
|
} else if (server->sec_level < BT_SECURITY_L1) {
|
2016-11-01 11:05:35 +02:00
|
|
|
|
/* Level 0 is only applicable for BR/EDR */
|
2019-08-26 15:50:48 +02:00
|
|
|
|
server->sec_level = BT_SECURITY_L1;
|
2016-11-01 11:05:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("PSM 0x%04x", server->psm);
|
2015-10-08 13:45:40 +03:00
|
|
|
|
|
2017-03-23 15:03:58 +02:00
|
|
|
|
sys_slist_append(&servers, &server->node);
|
2015-10-08 13:45:40 +03:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
|
|
|
|
|
static void l2cap_chan_seg_recv_rx_init(struct bt_l2cap_le_chan *chan)
|
|
|
|
|
{
|
|
|
|
|
if (chan->rx.mps > BT_L2CAP_RX_MTU) {
|
|
|
|
|
LOG_ERR("Limiting RX MPS by stack buffer size.");
|
|
|
|
|
chan->rx.mps = BT_L2CAP_RX_MTU;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chan->_sdu_len = 0;
|
|
|
|
|
chan->_sdu_len_done = 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_BT_L2CAP_SEG_RECV */
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan)
|
2015-11-16 14:44:44 +02:00
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p", chan);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
/* Redirect to experimental API. */
|
|
|
|
|
IF_ENABLED(CONFIG_BT_L2CAP_SEG_RECV, ({
|
|
|
|
|
if (chan->chan.ops->seg_recv) {
|
|
|
|
|
l2cap_chan_seg_recv_rx_init(chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
2015-11-16 14:44:44 +02:00
|
|
|
|
/* Use existing MTU if defined */
|
|
|
|
|
if (!chan->rx.mtu) {
|
2021-05-03 09:12:49 +02:00
|
|
|
|
/* If application has not provide the incoming L2CAP SDU MTU use
|
|
|
|
|
* an MTU that does not require segmentation.
|
|
|
|
|
*/
|
|
|
|
|
chan->rx.mtu = BT_L2CAP_SDU_RX_MTU;
|
2015-11-16 14:44:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-03 09:12:49 +02:00
|
|
|
|
/* MPS shall not be bigger than MTU + BT_L2CAP_SDU_HDR_SIZE as the
|
|
|
|
|
* remaining bytes cannot be used.
|
2020-09-21 11:20:48 -07:00
|
|
|
|
*/
|
2021-05-03 09:12:49 +02:00
|
|
|
|
chan->rx.mps = MIN(chan->rx.mtu + BT_L2CAP_SDU_HDR_SIZE,
|
|
|
|
|
BT_L2CAP_RX_MTU);
|
2020-09-21 11:20:48 -07:00
|
|
|
|
|
|
|
|
|
/* Truncate MTU if channel have disabled segmentation but still have
|
|
|
|
|
* set an MTU which requires it.
|
|
|
|
|
*/
|
2021-05-03 09:12:49 +02:00
|
|
|
|
if (!chan->chan.ops->alloc_buf &&
|
|
|
|
|
(chan->rx.mps < chan->rx.mtu + BT_L2CAP_SDU_HDR_SIZE)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Segmentation disabled but MTU > MPS, truncating MTU");
|
2021-05-03 09:12:49 +02:00
|
|
|
|
chan->rx.mtu = chan->rx.mps - BT_L2CAP_SDU_HDR_SIZE;
|
2020-09-21 11:20:48 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 08:18:52 +02:00
|
|
|
|
atomic_set(&chan->rx.credits, 1);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 18:29:45 +02:00
|
|
|
|
static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch)
|
|
|
|
|
{
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
|
|
/* Return current buffer */
|
|
|
|
|
if (ch->tx_buf) {
|
|
|
|
|
buf = ch->tx_buf;
|
|
|
|
|
ch->tx_buf = NULL;
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return net_buf_get(&ch->tx_queue, K_NO_WAIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
|
2024-04-04 17:17:11 +02:00
|
|
|
|
struct net_buf *buf);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
|
|
|
|
static void l2cap_chan_tx_process(struct k_work *work)
|
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_le_chan *ch;
|
|
|
|
|
struct net_buf *buf;
|
2024-01-11 17:51:29 +01:00
|
|
|
|
int ret;
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
2023-08-29 09:45:51 +00:00
|
|
|
|
ch = CONTAINER_OF(k_work_delayable_from_work(work), struct bt_l2cap_le_chan, tx_work);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
|
|
|
|
/* Resume tx in case there are buffers in the queue */
|
|
|
|
|
while ((buf = l2cap_chan_le_get_tx_buf(ch))) {
|
2024-01-11 18:07:48 +01:00
|
|
|
|
/* Here buf is either:
|
|
|
|
|
* - a partially-sent SDU le_chan->tx_buf
|
|
|
|
|
* - a new SDU from the TX queue
|
|
|
|
|
*/
|
|
|
|
|
LOG_DBG("chan %p buf %p", ch, buf);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
ret = l2cap_chan_le_send_sdu(ch, buf);
|
2024-01-11 17:51:29 +01:00
|
|
|
|
if (ret < 0) {
|
|
|
|
|
if (ret == -EAGAIN) {
|
2019-11-18 18:29:45 +02:00
|
|
|
|
ch->tx_buf = buf;
|
2022-09-20 16:05:31 +02:00
|
|
|
|
/* If we don't reschedule, and the app doesn't nudge l2cap (e.g. by
|
|
|
|
|
* sending another SDU), the channel will be stuck in limbo. To
|
2023-03-06 16:54:58 +01:00
|
|
|
|
* prevent this, we reschedule with a configurable delay.
|
2022-09-20 16:05:31 +02:00
|
|
|
|
*/
|
2023-03-06 16:54:58 +01:00
|
|
|
|
k_work_schedule(&ch->tx_work, K_MSEC(CONFIG_BT_L2CAP_RESCHED_MS));
|
2021-06-08 11:32:29 +02:00
|
|
|
|
} else {
|
2024-01-11 18:07:48 +01:00
|
|
|
|
LOG_WRN("Failed to send (err %d), dropping buf %p", ret, buf);
|
2024-01-11 17:51:29 +01:00
|
|
|
|
l2cap_tx_buf_destroy(ch->chan.conn, buf, ret);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan)
|
2015-11-16 14:44:44 +02:00
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p", chan);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
|
2018-09-11 19:09:03 -07:00
|
|
|
|
(void)memset(&chan->tx, 0, sizeof(chan->tx));
|
2019-12-18 13:27:36 -08:00
|
|
|
|
atomic_set(&chan->tx.credits, 0);
|
2017-03-07 10:20:15 +02:00
|
|
|
|
k_fifo_init(&chan->tx_queue);
|
2023-03-06 16:54:58 +01:00
|
|
|
|
k_work_init_delayable(&chan->tx_work, l2cap_chan_tx_process);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t credits)
|
2015-11-16 14:44:44 +02:00
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p credits %u", chan, credits);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
|
2019-12-18 13:27:36 -08:00
|
|
|
|
atomic_add(&chan->tx.credits, credits);
|
2019-05-22 22:46:25 +03:00
|
|
|
|
|
2024-01-04 09:55:47 +01:00
|
|
|
|
if (!atomic_test_and_set_bit(chan->chan.status, BT_L2CAP_STATUS_OUT)) {
|
|
|
|
|
LOG_DBG("chan %p unpaused", chan);
|
|
|
|
|
if (chan->chan.ops->status) {
|
|
|
|
|
chan->chan.ops->status(&chan->chan, chan->chan.status);
|
|
|
|
|
}
|
2019-05-22 22:46:25 +03:00
|
|
|
|
}
|
2015-11-16 14:44:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-28 16:40:57 +03:00
|
|
|
|
static void l2cap_chan_destroy(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2017-03-07 10:20:15 +02:00
|
|
|
|
struct net_buf *buf;
|
2016-06-28 16:40:57 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p cid 0x%04x", le_chan, le_chan->rx.cid);
|
2016-06-28 16:40:57 +03:00
|
|
|
|
|
2021-04-12 13:03:22 +02:00
|
|
|
|
/* Cancel ongoing work. Since the channel can be re-used after this
|
|
|
|
|
* we need to sync to make sure that the kernel does not have it
|
|
|
|
|
* in its queue anymore.
|
2022-09-26 09:56:46 +02:00
|
|
|
|
*
|
|
|
|
|
* In the case where we are in the context of executing the rtx_work
|
|
|
|
|
* item, we don't sync as it will deadlock the workqueue.
|
2021-04-12 13:03:22 +02:00
|
|
|
|
*/
|
2024-02-06 09:34:16 +01:00
|
|
|
|
struct k_work_q *rtx_work_queue = le_chan->rtx_work.queue;
|
|
|
|
|
|
|
|
|
|
if (rtx_work_queue == NULL || k_current_get() != &rtx_work_queue->thread) {
|
2022-09-26 09:56:46 +02:00
|
|
|
|
k_work_cancel_delayable_sync(&le_chan->rtx_work, &le_chan->rtx_sync);
|
|
|
|
|
} else {
|
|
|
|
|
k_work_cancel_delayable(&le_chan->rtx_work);
|
|
|
|
|
}
|
2016-07-11 13:41:21 +03:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->tx_buf) {
|
2023-11-22 16:16:29 +01:00
|
|
|
|
l2cap_tx_buf_destroy(chan->conn, le_chan->tx_buf, -ESHUTDOWN);
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->tx_buf = NULL;
|
2019-11-19 15:34:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-07 10:20:15 +02:00
|
|
|
|
/* Remove buffers on the TX queue */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
while ((buf = net_buf_get(&le_chan->tx_queue, K_NO_WAIT))) {
|
2023-11-22 16:16:29 +01:00
|
|
|
|
l2cap_tx_buf_destroy(chan->conn, buf, -ESHUTDOWN);
|
2017-03-07 10:20:15 +02:00
|
|
|
|
}
|
2016-06-28 16:40:57 +03:00
|
|
|
|
|
2019-11-26 13:08:24 +02:00
|
|
|
|
/* Remove buffers on the RX queue */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
while ((buf = net_buf_get(&le_chan->rx_queue, K_NO_WAIT))) {
|
2019-11-26 13:08:24 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-28 16:40:57 +03:00
|
|
|
|
/* Destroy segmented SDU if it exists */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->_sdu) {
|
|
|
|
|
net_buf_unref(le_chan->_sdu);
|
|
|
|
|
le_chan->_sdu = NULL;
|
|
|
|
|
le_chan->_sdu_len = 0U;
|
2016-06-28 16:40:57 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static uint16_t le_err_to_result(int err)
|
2018-10-23 11:26:30 +02:00
|
|
|
|
{
|
|
|
|
|
switch (err) {
|
|
|
|
|
case -ENOMEM:
|
|
|
|
|
return BT_L2CAP_LE_ERR_NO_RESOURCES;
|
|
|
|
|
case -EACCES:
|
|
|
|
|
return BT_L2CAP_LE_ERR_AUTHORIZATION;
|
2018-10-26 17:09:58 +02:00
|
|
|
|
case -EPERM:
|
2018-10-23 11:26:30 +02:00
|
|
|
|
return BT_L2CAP_LE_ERR_KEY_SIZE;
|
2019-05-20 15:11:39 +03:00
|
|
|
|
case -ENOTSUP:
|
|
|
|
|
/* This handle the cases where a fixed channel is registered but
|
|
|
|
|
* for some reason (e.g. controller not suporting a feature)
|
|
|
|
|
* cannot be used.
|
|
|
|
|
*/
|
|
|
|
|
return BT_L2CAP_LE_ERR_PSM_NOT_SUPP;
|
2018-10-23 11:26:30 +02:00
|
|
|
|
default:
|
|
|
|
|
return BT_L2CAP_LE_ERR_UNACCEPT_PARAMS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static uint16_t l2cap_chan_accept(struct bt_conn *conn,
|
|
|
|
|
struct bt_l2cap_server *server, uint16_t scid,
|
|
|
|
|
uint16_t mtu, uint16_t mps, uint16_t credits,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct bt_l2cap_chan **chan)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
int err;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p scid 0x%04x chan %p", conn, scid, chan);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (!L2CAP_LE_CID_IS_DYN(scid)) {
|
|
|
|
|
return BT_L2CAP_LE_ERR_INVALID_SCID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*chan = bt_l2cap_le_lookup_tx_cid(conn, scid);
|
|
|
|
|
if (*chan) {
|
|
|
|
|
return BT_L2CAP_LE_ERR_SCID_IN_USE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Request server to accept the new connection and allocate the
|
|
|
|
|
* channel.
|
|
|
|
|
*/
|
2023-07-18 16:44:24 +01:00
|
|
|
|
err = server->accept(conn, server, chan);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
if (err < 0) {
|
|
|
|
|
return le_err_to_result(err);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
|
|
|
|
|
if (!(*chan)->ops->recv == !(*chan)->ops->seg_recv) {
|
|
|
|
|
LOG_ERR("Exactly one of 'recv' or 'seg_recv' must be set");
|
|
|
|
|
return BT_L2CAP_LE_ERR_UNACCEPT_PARAMS;
|
|
|
|
|
}
|
|
|
|
|
#else
|
2021-06-04 16:43:29 +02:00
|
|
|
|
if (!(*chan)->ops->recv) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Mandatory callback 'recv' missing");
|
2021-06-04 16:43:29 +02:00
|
|
|
|
return BT_L2CAP_LE_ERR_UNACCEPT_PARAMS;
|
|
|
|
|
}
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#endif
|
2021-06-04 16:43:29 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(*chan);
|
|
|
|
|
|
|
|
|
|
le_chan->required_sec_level = server->sec_level;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (!l2cap_chan_add(conn, *chan, l2cap_chan_destroy)) {
|
|
|
|
|
return BT_L2CAP_LE_ERR_NO_RESOURCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Init TX parameters */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
l2cap_chan_tx_init(le_chan);
|
|
|
|
|
le_chan->tx.cid = scid;
|
|
|
|
|
le_chan->tx.mps = mps;
|
|
|
|
|
le_chan->tx.mtu = mtu;
|
|
|
|
|
l2cap_chan_tx_give_credits(le_chan, credits);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* Init RX parameters */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
l2cap_chan_rx_init(le_chan);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* Set channel PSM */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->psm = server->psm;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
|
bt_l2cap_chan_set_state(*chan, BT_L2CAP_CONNECTED);
|
|
|
|
|
|
|
|
|
|
return BT_L2CAP_LE_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 13:11:36 +01:00
|
|
|
|
static uint16_t l2cap_check_security(struct bt_conn *conn,
|
2020-07-10 10:46:41 -07:00
|
|
|
|
struct bt_l2cap_server *server)
|
|
|
|
|
{
|
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CONN_DISABLE_SECURITY)) {
|
2021-11-08 13:11:36 +01:00
|
|
|
|
return BT_L2CAP_LE_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (conn->sec_level >= server->sec_level) {
|
|
|
|
|
return BT_L2CAP_LE_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (conn->sec_level > BT_SECURITY_L1) {
|
|
|
|
|
return BT_L2CAP_LE_ERR_AUTHENTICATION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If an LTK or an STK is available and encryption is required
|
|
|
|
|
* (LE security mode 1) but encryption is not enabled, the
|
|
|
|
|
* service request shall be rejected with the error code
|
|
|
|
|
* "Insufficient Encryption".
|
|
|
|
|
*/
|
2023-03-27 14:40:42 +02:00
|
|
|
|
if (bt_conn_ltk_present(conn)) {
|
2021-11-08 13:11:36 +01:00
|
|
|
|
return BT_L2CAP_LE_ERR_ENCRYPTION;
|
2020-07-10 10:46:41 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 13:11:36 +01:00
|
|
|
|
return BT_L2CAP_LE_ERR_AUTHENTICATION;
|
2020-07-10 10:46:41 -07:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-10-28 10:41:10 +02:00
|
|
|
|
struct net_buf *buf)
|
2015-09-30 14:49:25 +03:00
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
2015-09-30 14:49:25 +03:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2015-09-30 14:49:25 +03:00
|
|
|
|
struct bt_l2cap_server *server;
|
|
|
|
|
struct bt_l2cap_le_conn_req *req = (void *)buf->data;
|
|
|
|
|
struct bt_l2cap_le_conn_rsp *rsp;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t psm, scid, mtu, mps, credits;
|
|
|
|
|
uint16_t result;
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn req packet size");
|
2015-09-30 14:49:25 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
psm = sys_le16_to_cpu(req->psm);
|
|
|
|
|
scid = sys_le16_to_cpu(req->scid);
|
|
|
|
|
mtu = sys_le16_to_cpu(req->mtu);
|
|
|
|
|
mps = sys_le16_to_cpu(req->mps);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
credits = sys_le16_to_cpu(req->credits);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u", psm, scid, mtu, mps, credits);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
|
|
|
|
if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) {
|
2023-05-15 10:30:41 +02:00
|
|
|
|
LOG_ERR("Invalid LE-Conn Req params: mtu %u mps %u", mtu, mps);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_RSP, ident,
|
2016-11-08 16:55:09 +02:00
|
|
|
|
sizeof(*rsp));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
2018-09-11 19:09:03 -07:00
|
|
|
|
(void)memset(rsp, 0, sizeof(*rsp));
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
|
|
|
|
/* Check if there is a server registered */
|
2022-10-26 09:34:55 +01:00
|
|
|
|
server = bt_l2cap_server_lookup_psm(psm);
|
2016-02-12 17:42:26 +02:00
|
|
|
|
if (!server) {
|
2023-05-30 17:00:03 +01:00
|
|
|
|
result = BT_L2CAP_LE_ERR_PSM_NOT_SUPP;
|
2015-09-30 14:49:25 +03:00
|
|
|
|
goto rsp;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 14:00:21 +02:00
|
|
|
|
/* Check if connection has minimum required security level */
|
2021-11-08 13:11:36 +01:00
|
|
|
|
result = l2cap_check_security(conn, server);
|
|
|
|
|
if (result != BT_L2CAP_LE_SUCCESS) {
|
2016-11-01 14:00:21 +02:00
|
|
|
|
goto rsp;
|
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
result = l2cap_chan_accept(conn, server, scid, mtu, mps, credits,
|
|
|
|
|
&chan);
|
|
|
|
|
if (result != BT_L2CAP_LE_SUCCESS) {
|
2015-11-02 09:02:59 +02:00
|
|
|
|
goto rsp;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(chan);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
/* Prepare response protocol data */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
rsp->dcid = sys_cpu_to_le16(le_chan->rx.cid);
|
|
|
|
|
rsp->mps = sys_cpu_to_le16(le_chan->rx.mps);
|
|
|
|
|
rsp->mtu = sys_cpu_to_le16(le_chan->rx.mtu);
|
2023-05-15 10:30:41 +02:00
|
|
|
|
rsp->credits = sys_cpu_to_le16(le_chan->rx.credits);
|
2023-04-18 08:18:52 +02:00
|
|
|
|
|
2023-05-30 17:00:03 +01:00
|
|
|
|
result = BT_L2CAP_LE_SUCCESS;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
rsp:
|
2023-05-30 17:00:03 +01:00
|
|
|
|
rsp->result = sys_cpu_to_le16(result);
|
|
|
|
|
|
2024-01-03 09:37:05 +01:00
|
|
|
|
if (l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf)) {
|
2023-05-30 17:00:03 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Raise connected callback on success */
|
|
|
|
|
if ((result == BT_L2CAP_LE_SUCCESS) && (chan->ops->connected != NULL)) {
|
|
|
|
|
chan->ops->connected(chan);
|
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
2022-01-26 15:45:08 +01:00
|
|
|
|
struct bt_l2cap_chan *chan[L2CAP_ECRED_CHAN_MAX_PER_REQ];
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct bt_l2cap_le_chan *ch = NULL;
|
|
|
|
|
struct bt_l2cap_server *server;
|
|
|
|
|
struct bt_l2cap_ecred_conn_req *req;
|
|
|
|
|
struct bt_l2cap_ecred_conn_rsp *rsp;
|
2022-03-02 15:53:59 +01:00
|
|
|
|
uint16_t mtu, mps, credits, result = BT_L2CAP_LE_SUCCESS;
|
|
|
|
|
uint16_t psm = 0x0000;
|
2022-01-26 15:45:08 +01:00
|
|
|
|
uint16_t scid, dcid[L2CAP_ECRED_CHAN_MAX_PER_REQ];
|
2020-02-26 10:29:23 -08:00
|
|
|
|
int i = 0;
|
2021-06-14 08:13:48 +02:00
|
|
|
|
uint8_t req_cid_count;
|
2023-05-30 17:00:03 +01:00
|
|
|
|
bool rsp_queued = false;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2021-04-14 07:48:01 +02:00
|
|
|
|
/* set dcid to zeros here, in case of all connections refused error */
|
|
|
|
|
memset(dcid, 0, sizeof(dcid));
|
2020-02-26 10:29:23 -08:00
|
|
|
|
if (buf->len < sizeof(*req)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn req packet size");
|
2020-02-26 10:29:23 -08:00
|
|
|
|
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
|
2021-06-14 08:13:48 +02:00
|
|
|
|
req_cid_count = 0;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
goto response;
|
2015-09-30 14:49:25 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
2021-06-14 08:13:48 +02:00
|
|
|
|
req_cid_count = buf->len / sizeof(scid);
|
2021-03-12 09:32:55 -08:00
|
|
|
|
|
|
|
|
|
if (buf->len > sizeof(dcid)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too large LE conn req packet size");
|
2022-01-26 15:45:08 +01:00
|
|
|
|
req_cid_count = L2CAP_ECRED_CHAN_MAX_PER_REQ;
|
2021-03-12 09:32:55 -08:00
|
|
|
|
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
psm = sys_le16_to_cpu(req->psm);
|
|
|
|
|
mtu = sys_le16_to_cpu(req->mtu);
|
|
|
|
|
mps = sys_le16_to_cpu(req->mps);
|
|
|
|
|
credits = sys_le16_to_cpu(req->credits);
|
2016-11-01 11:05:35 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MTU) {
|
2023-05-15 10:30:41 +02:00
|
|
|
|
LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps);
|
2021-04-15 12:22:48 +02:00
|
|
|
|
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
goto response;
|
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
/* Check if there is a server registered */
|
2022-10-26 09:34:55 +01:00
|
|
|
|
server = bt_l2cap_server_lookup_psm(psm);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
if (!server) {
|
|
|
|
|
result = BT_L2CAP_LE_ERR_PSM_NOT_SUPP;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
/* Check if connection has minimum required security level */
|
2021-11-08 13:11:36 +01:00
|
|
|
|
result = l2cap_check_security(conn, server);
|
|
|
|
|
if (result != BT_L2CAP_LE_SUCCESS) {
|
2020-02-26 10:29:23 -08:00
|
|
|
|
goto response;
|
|
|
|
|
}
|
2016-11-03 14:48:07 +02:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
while (buf->len >= sizeof(scid)) {
|
2021-04-14 11:58:47 +02:00
|
|
|
|
uint16_t rc;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
scid = net_buf_pull_le16(buf);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
2021-04-14 11:58:47 +02:00
|
|
|
|
rc = l2cap_chan_accept(conn, server, scid, mtu, mps,
|
|
|
|
|
credits, &chan[i]);
|
|
|
|
|
if (rc != BT_L2CAP_LE_SUCCESS) {
|
|
|
|
|
result = rc;
|
|
|
|
|
}
|
|
|
|
|
switch (rc) {
|
2020-02-26 10:29:23 -08:00
|
|
|
|
case BT_L2CAP_LE_SUCCESS:
|
|
|
|
|
ch = BT_L2CAP_LE_CHAN(chan[i]);
|
|
|
|
|
dcid[i++] = sys_cpu_to_le16(ch->rx.cid);
|
|
|
|
|
continue;
|
|
|
|
|
/* Some connections refused – invalid Source CID */
|
|
|
|
|
/* Some connections refused – Source CID already allocated */
|
2021-03-16 09:46:59 -07:00
|
|
|
|
/* Some connections refused – not enough resources
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
default:
|
2020-02-26 10:29:23 -08:00
|
|
|
|
/* If a Destination CID is 0x0000, the channel was not
|
|
|
|
|
* established.
|
|
|
|
|
*/
|
|
|
|
|
dcid[i++] = 0x0000;
|
|
|
|
|
continue;
|
2016-05-26 14:03:04 +02:00
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response:
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_CONN_RSP, ident,
|
2021-06-14 08:13:48 +02:00
|
|
|
|
sizeof(*rsp) +
|
|
|
|
|
(sizeof(scid) * req_cid_count));
|
2021-04-14 07:48:01 +02:00
|
|
|
|
if (!buf) {
|
2022-01-26 15:41:36 +01:00
|
|
|
|
goto callback;
|
2021-04-14 07:48:01 +02:00
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
|
|
|
|
(void)memset(rsp, 0, sizeof(*rsp));
|
2021-04-14 07:48:01 +02:00
|
|
|
|
if (ch) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
rsp->mps = sys_cpu_to_le16(ch->rx.mps);
|
|
|
|
|
rsp->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
2023-05-15 10:30:41 +02:00
|
|
|
|
rsp->credits = sys_cpu_to_le16(ch->rx.credits);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
rsp->result = sys_cpu_to_le16(result);
|
2021-04-14 07:48:01 +02:00
|
|
|
|
|
2021-06-14 08:13:48 +02:00
|
|
|
|
net_buf_add_mem(buf, dcid, sizeof(scid) * req_cid_count);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2024-01-03 09:37:05 +01:00
|
|
|
|
if (l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf)) {
|
2023-05-30 17:00:03 +01:00
|
|
|
|
goto callback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rsp_queued = true;
|
2022-01-26 15:41:36 +01:00
|
|
|
|
|
|
|
|
|
callback:
|
|
|
|
|
if (ecred_cb && ecred_cb->ecred_conn_req) {
|
2022-03-02 15:53:59 +01:00
|
|
|
|
ecred_cb->ecred_conn_req(conn, result, psm);
|
2022-01-26 15:41:36 +01:00
|
|
|
|
}
|
2023-07-27 11:21:04 +02:00
|
|
|
|
if (rsp_queued) {
|
2023-05-30 17:00:03 +01:00
|
|
|
|
for (i = 0; i < req_cid_count; i++) {
|
|
|
|
|
/* Raise connected callback for established channels */
|
|
|
|
|
if ((dcid[i] != 0x00) && (chan[i]->ops->connected != NULL)) {
|
|
|
|
|
chan[i]->ops->connected(chan[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
2022-01-26 15:45:08 +01:00
|
|
|
|
struct bt_l2cap_chan *chans[L2CAP_ECRED_CHAN_MAX_PER_REQ];
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct bt_l2cap_ecred_reconf_req *req;
|
|
|
|
|
struct bt_l2cap_ecred_reconf_rsp *rsp;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t mtu, mps;
|
2021-04-16 07:25:38 +02:00
|
|
|
|
uint16_t scid, result = BT_L2CAP_RECONF_SUCCESS;
|
2021-07-09 11:04:05 +02:00
|
|
|
|
int chan_count = 0;
|
2021-08-11 14:25:12 +02:00
|
|
|
|
bool mps_reduced = false;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small ecred reconf req packet size");
|
2020-02-26 10:29:23 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req = net_buf_pull_mem(buf, sizeof(*req));
|
|
|
|
|
|
|
|
|
|
mtu = sys_le16_to_cpu(req->mtu);
|
|
|
|
|
mps = sys_le16_to_cpu(req->mps);
|
|
|
|
|
|
2021-04-16 07:25:38 +02:00
|
|
|
|
if (mps < L2CAP_ECRED_MIN_MTU) {
|
|
|
|
|
result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-16 07:25:38 +02:00
|
|
|
|
if (mtu < L2CAP_ECRED_MIN_MTU) {
|
2020-02-26 10:29:23 -08:00
|
|
|
|
result = BT_L2CAP_RECONF_INVALID_MTU;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-07 10:13:56 +02:00
|
|
|
|
/* The specification only allows up to 5 CIDs in this packet */
|
|
|
|
|
if (buf->len > (L2CAP_ECRED_CHAN_MAX_PER_REQ * sizeof(scid))) {
|
|
|
|
|
result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
while (buf->len >= sizeof(scid)) {
|
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
scid = net_buf_pull_le16(buf);
|
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, scid);
|
|
|
|
|
if (!chan) {
|
2021-04-16 07:25:38 +02:00
|
|
|
|
result = BT_L2CAP_RECONF_INVALID_CID;
|
2022-04-27 15:15:42 +02:00
|
|
|
|
goto response;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->tx.mtu > mtu) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("chan %p decreased MTU %u -> %u", chan,
|
|
|
|
|
BT_L2CAP_LE_CHAN(chan)->tx.mtu, mtu);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
result = BT_L2CAP_RECONF_INVALID_MTU;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 11:04:05 +02:00
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->tx.mps > mps) {
|
|
|
|
|
mps_reduced = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chans[chan_count] = chan;
|
|
|
|
|
chan_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* As per BT Core Spec V5.2 Vol. 3, Part A, section 7.11
|
|
|
|
|
* The request (...) shall not decrease the MPS of a channel
|
|
|
|
|
* if more than one channel is specified.
|
|
|
|
|
*/
|
|
|
|
|
if (mps_reduced && chan_count > 1) {
|
|
|
|
|
result = BT_L2CAP_RECONF_INVALID_MPS;
|
|
|
|
|
goto response;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-20 10:44:24 +02:00
|
|
|
|
for (int i = 0; i < chan_count; i++) {
|
|
|
|
|
BT_L2CAP_LE_CHAN(chans[i])->tx.mtu = mtu;
|
|
|
|
|
BT_L2CAP_LE_CHAN(chans[i])->tx.mps = mps;
|
2021-09-17 20:33:53 +02:00
|
|
|
|
|
|
|
|
|
if (chans[i]->ops->reconfigured) {
|
|
|
|
|
chans[i]->ops->reconfigured(chans[i]);
|
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("mtu %u mps %u", mtu, mps);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
response:
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_RECONF_RSP, ident,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
sizeof(*rsp));
|
|
|
|
|
|
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
|
|
|
|
rsp->result = sys_cpu_to_le16(result);
|
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
}
|
2021-09-17 20:33:53 +02:00
|
|
|
|
|
|
|
|
|
static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_ecred_reconf_rsp *rsp;
|
|
|
|
|
struct bt_l2cap_le_chan *ch;
|
|
|
|
|
uint16_t result;
|
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small ecred reconf rsp packet size");
|
2021-09-17 20:33:53 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
|
|
|
|
|
result = sys_le16_to_cpu(rsp->result);
|
|
|
|
|
|
|
|
|
|
while ((ch = l2cap_lookup_ident(conn, ident))) {
|
2022-05-25 15:36:21 +02:00
|
|
|
|
/* Stop timer started on REQ send. The timer is only set on one
|
|
|
|
|
* of the channels, but we don't want to make assumptions on
|
|
|
|
|
* which one it is.
|
|
|
|
|
*/
|
|
|
|
|
k_work_cancel_delayable(&ch->rtx_work);
|
|
|
|
|
|
2021-09-17 20:33:53 +02:00
|
|
|
|
if (result == BT_L2CAP_LE_SUCCESS) {
|
|
|
|
|
ch->rx.mtu = ch->pending_rx_mtu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ch->pending_rx_mtu = 0;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->ident = 0U;
|
2021-09-17 20:33:53 +02:00
|
|
|
|
|
|
|
|
|
if (ch->chan.ops->reconfigured) {
|
|
|
|
|
ch->chan.ops->reconfigured(&ch->chan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
2019-06-11 14:57:33 +03:00
|
|
|
|
static struct bt_l2cap_le_chan *l2cap_remove_rx_cid(struct bt_conn *conn,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t cid)
|
2015-10-28 15:35:53 +02:00
|
|
|
|
{
|
2017-03-21 15:48:03 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
sys_snode_t *prev = NULL;
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
2016-10-12 22:02:28 +02:00
|
|
|
|
/* Protect fixed channels against accidental removal */
|
|
|
|
|
if (!L2CAP_LE_CID_IS_DYN(cid)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->rx.cid == cid) {
|
|
|
|
|
sys_slist_remove(&conn->channels, prev, &chan->node);
|
|
|
|
|
return BT_L2CAP_LE_CHAN(chan);
|
2015-10-28 15:35:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 15:48:03 +02:00
|
|
|
|
prev = &chan->node;
|
2015-10-28 15:35:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_disconn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-10-28 15:35:53 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_le_chan *chan;
|
2015-10-28 15:35:53 +02:00
|
|
|
|
struct bt_l2cap_disconn_req *req = (void *)buf->data;
|
|
|
|
|
struct bt_l2cap_disconn_rsp *rsp;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t dcid;
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn req packet size");
|
2015-10-28 15:35:53 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 14:57:33 +03:00
|
|
|
|
dcid = sys_le16_to_cpu(req->dcid);
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("dcid 0x%04x scid 0x%04x", dcid, sys_le16_to_cpu(req->scid));
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
2019-06-11 14:57:33 +03:00
|
|
|
|
chan = l2cap_remove_rx_cid(conn, dcid);
|
2015-10-28 15:35:53 +02:00
|
|
|
|
if (!chan) {
|
2016-06-06 09:23:20 +02:00
|
|
|
|
struct bt_l2cap_cmd_reject_cid_data data;
|
|
|
|
|
|
|
|
|
|
data.scid = req->scid;
|
|
|
|
|
data.dcid = req->dcid;
|
|
|
|
|
|
|
|
|
|
l2cap_send_reject(conn, ident, BT_L2CAP_REJ_INVALID_CID, &data,
|
|
|
|
|
sizeof(data));
|
2015-10-28 15:35:53 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_RSP, ident,
|
2016-11-08 16:55:09 +02:00
|
|
|
|
sizeof(*rsp));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
|
|
|
|
rsp->dcid = sys_cpu_to_le16(chan->rx.cid);
|
|
|
|
|
rsp->scid = sys_cpu_to_le16(chan->tx.cid);
|
|
|
|
|
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
2015-10-28 15:35:53 +02:00
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static int l2cap_change_security(struct bt_l2cap_le_chan *chan, uint16_t err)
|
2016-11-02 13:20:11 +02:00
|
|
|
|
{
|
2020-07-29 19:58:12 +02:00
|
|
|
|
struct bt_conn *conn = chan->chan.conn;
|
|
|
|
|
bt_security_t sec;
|
2020-04-29 16:22:47 -07:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (atomic_test_bit(chan->chan.status,
|
|
|
|
|
BT_L2CAP_STATUS_ENCRYPT_PENDING)) {
|
|
|
|
|
return -EINPROGRESS;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 13:20:11 +02:00
|
|
|
|
switch (err) {
|
2018-10-23 09:51:20 +02:00
|
|
|
|
case BT_L2CAP_LE_ERR_ENCRYPTION:
|
2020-07-29 19:58:12 +02:00
|
|
|
|
if (conn->sec_level >= BT_SECURITY_L2) {
|
2016-11-02 13:20:11 +02:00
|
|
|
|
return -EALREADY;
|
|
|
|
|
}
|
2020-07-29 19:58:12 +02:00
|
|
|
|
|
|
|
|
|
sec = BT_SECURITY_L2;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
break;
|
2018-10-23 09:51:20 +02:00
|
|
|
|
case BT_L2CAP_LE_ERR_AUTHENTICATION:
|
2020-07-29 19:58:12 +02:00
|
|
|
|
if (conn->sec_level < BT_SECURITY_L2) {
|
|
|
|
|
sec = BT_SECURITY_L2;
|
|
|
|
|
} else if (conn->sec_level < BT_SECURITY_L3) {
|
|
|
|
|
sec = BT_SECURITY_L3;
|
|
|
|
|
} else if (conn->sec_level < BT_SECURITY_L4) {
|
|
|
|
|
sec = BT_SECURITY_L4;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
} else {
|
|
|
|
|
return -EALREADY;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 19:58:12 +02:00
|
|
|
|
ret = bt_conn_set_security(chan->chan.conn, sec);
|
2020-04-29 16:22:47 -07:00
|
|
|
|
if (ret < 0) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
atomic_set_bit(chan->chan.status, BT_L2CAP_STATUS_ENCRYPT_PENDING);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
2020-02-26 10:29:23 -08:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_le_chan *chan;
|
|
|
|
|
struct bt_l2cap_ecred_conn_rsp *rsp;
|
2022-03-02 15:53:59 +01:00
|
|
|
|
uint16_t dcid, mtu, mps, credits, result, psm;
|
2022-01-26 15:41:36 +01:00
|
|
|
|
uint8_t attempted = 0;
|
|
|
|
|
uint8_t succeeded = 0;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small ecred conn rsp packet size");
|
2020-02-26 10:29:23 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
|
|
|
|
|
mtu = sys_le16_to_cpu(rsp->mtu);
|
|
|
|
|
mps = sys_le16_to_cpu(rsp->mps);
|
|
|
|
|
credits = sys_le16_to_cpu(rsp->credits);
|
|
|
|
|
result = sys_le16_to_cpu(rsp->result);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("mtu 0x%04x mps 0x%04x credits 0x%04x result %u", mtu, mps, credits, result);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2022-03-02 15:53:59 +01:00
|
|
|
|
chan = l2cap_lookup_ident(conn, ident);
|
|
|
|
|
if (chan) {
|
|
|
|
|
psm = chan->psm;
|
|
|
|
|
} else {
|
|
|
|
|
psm = 0x0000;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
switch (result) {
|
|
|
|
|
case BT_L2CAP_LE_ERR_AUTHENTICATION:
|
|
|
|
|
case BT_L2CAP_LE_ERR_ENCRYPTION:
|
|
|
|
|
while ((chan = l2cap_lookup_ident(conn, ident))) {
|
2022-03-02 15:53:59 +01:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
/* Cancel RTX work */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
k_work_cancel_delayable(&chan->rtx_work);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* If security needs changing wait it to be completed */
|
|
|
|
|
if (!l2cap_change_security(chan, result)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_LE_SUCCESS:
|
|
|
|
|
/* Some connections refused – invalid Source CID */
|
|
|
|
|
case BT_L2CAP_LE_ERR_INVALID_SCID:
|
|
|
|
|
/* Some connections refused – Source CID already allocated */
|
|
|
|
|
case BT_L2CAP_LE_ERR_SCID_IN_USE:
|
|
|
|
|
/* Some connections refused – not enough resources available */
|
|
|
|
|
case BT_L2CAP_LE_ERR_NO_RESOURCES:
|
|
|
|
|
while ((chan = l2cap_lookup_ident(conn, ident))) {
|
|
|
|
|
struct bt_l2cap_chan *c;
|
|
|
|
|
|
|
|
|
|
/* Cancel RTX work */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
k_work_cancel_delayable(&chan->rtx_work);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2022-04-13 16:49:53 +02:00
|
|
|
|
if (buf->len < sizeof(dcid)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Fewer dcid values than expected");
|
2022-04-13 16:49:53 +02:00
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
dcid = net_buf_pull_le16(buf);
|
2022-01-26 15:41:36 +01:00
|
|
|
|
attempted++;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("dcid 0x%04x", dcid);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
/* If a Destination CID is 0x0000, the channel was not
|
|
|
|
|
* established.
|
|
|
|
|
*/
|
|
|
|
|
if (!dcid) {
|
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = bt_l2cap_le_lookup_tx_cid(conn, dcid);
|
|
|
|
|
if (c) {
|
|
|
|
|
/* If a device receives a
|
|
|
|
|
* L2CAP_CREDIT_BASED_CONNECTION_RSP packet
|
|
|
|
|
* with an already assigned Destination CID,
|
|
|
|
|
* then both the original channel and the new
|
|
|
|
|
* channel shall be immediately discarded and
|
|
|
|
|
* not used.
|
|
|
|
|
*/
|
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
bt_l2cap_chan_disconnect(c);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chan->tx.cid = dcid;
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
chan->ident = 0U;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
chan->tx.mtu = mtu;
|
|
|
|
|
chan->tx.mps = mps;
|
|
|
|
|
|
|
|
|
|
/* Update state */
|
|
|
|
|
bt_l2cap_chan_set_state(&chan->chan,
|
|
|
|
|
BT_L2CAP_CONNECTED);
|
|
|
|
|
|
|
|
|
|
if (chan->chan.ops->connected) {
|
|
|
|
|
chan->chan.ops->connected(&chan->chan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Give credits */
|
|
|
|
|
l2cap_chan_tx_give_credits(chan, credits);
|
2022-01-26 15:41:36 +01:00
|
|
|
|
|
|
|
|
|
succeeded++;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_LE_ERR_PSM_NOT_SUPP:
|
|
|
|
|
default:
|
|
|
|
|
while ((chan = l2cap_remove_ident(conn, ident))) {
|
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-26 15:41:36 +01:00
|
|
|
|
|
|
|
|
|
if (ecred_cb && ecred_cb->ecred_conn_rsp) {
|
2022-03-02 15:53:59 +01:00
|
|
|
|
ecred_cb->ecred_conn_rsp(conn, result, attempted, succeeded, psm);
|
2022-01-26 15:41:36 +01:00
|
|
|
|
}
|
2020-02-26 10:29:23 -08:00
|
|
|
|
}
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_ECRED */
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-11-04 14:46:56 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_le_chan *chan;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
struct bt_l2cap_le_conn_rsp *rsp = (void *)buf->data;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t dcid, mtu, mps, credits, result;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE conn rsp packet size");
|
2015-11-04 14:46:56 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dcid = sys_le16_to_cpu(rsp->dcid);
|
|
|
|
|
mtu = sys_le16_to_cpu(rsp->mtu);
|
|
|
|
|
mps = sys_le16_to_cpu(rsp->mps);
|
|
|
|
|
credits = sys_le16_to_cpu(rsp->credits);
|
|
|
|
|
result = sys_le16_to_cpu(rsp->result);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("dcid 0x%04x mtu %u mps %u credits %u result 0x%04x", dcid, mtu, mps, credits,
|
|
|
|
|
result);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* Keep the channel in case of security errors */
|
2018-10-23 09:51:20 +02:00
|
|
|
|
if (result == BT_L2CAP_LE_SUCCESS ||
|
|
|
|
|
result == BT_L2CAP_LE_ERR_AUTHENTICATION ||
|
|
|
|
|
result == BT_L2CAP_LE_ERR_ENCRYPTION) {
|
2015-11-04 14:46:56 +02:00
|
|
|
|
chan = l2cap_lookup_ident(conn, ident);
|
|
|
|
|
} else {
|
|
|
|
|
chan = l2cap_remove_ident(conn, ident);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!chan) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Cannot find channel for ident %u", ident);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* Cancel RTX work */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
k_work_cancel_delayable(&chan->rtx_work);
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
|
|
|
|
/* Reset ident since it got a response */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
chan->ident = 0U;
|
2016-11-02 13:20:11 +02:00
|
|
|
|
|
2015-11-04 14:46:56 +02:00
|
|
|
|
switch (result) {
|
2018-10-23 09:51:20 +02:00
|
|
|
|
case BT_L2CAP_LE_SUCCESS:
|
2015-11-04 14:46:56 +02:00
|
|
|
|
chan->tx.cid = dcid;
|
|
|
|
|
chan->tx.mtu = mtu;
|
|
|
|
|
chan->tx.mps = mps;
|
|
|
|
|
|
2016-11-03 15:16:03 +02:00
|
|
|
|
/* Update state */
|
2016-11-03 14:48:07 +02:00
|
|
|
|
bt_l2cap_chan_set_state(&chan->chan, BT_L2CAP_CONNECTED);
|
2016-11-03 15:16:03 +02:00
|
|
|
|
|
2017-06-01 14:17:27 +03:00
|
|
|
|
if (chan->chan.ops->connected) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
chan->chan.ops->connected(&chan->chan);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-16 14:44:44 +02:00
|
|
|
|
/* Give credits */
|
|
|
|
|
l2cap_chan_tx_give_credits(chan, credits);
|
|
|
|
|
|
2015-11-04 14:46:56 +02:00
|
|
|
|
break;
|
2018-10-23 09:51:20 +02:00
|
|
|
|
case BT_L2CAP_LE_ERR_AUTHENTICATION:
|
|
|
|
|
case BT_L2CAP_LE_ERR_ENCRYPTION:
|
2016-11-02 13:20:11 +02:00
|
|
|
|
/* If security needs changing wait it to be completed */
|
|
|
|
|
if (l2cap_change_security(chan, result) == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-03-21 15:48:03 +02:00
|
|
|
|
bt_l2cap_chan_remove(conn, &chan->chan);
|
2020-08-21 13:45:52 -07:00
|
|
|
|
__fallthrough;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
default:
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
2015-10-28 15:35:53 +02:00
|
|
|
|
}
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_disconn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-11-06 13:12:44 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_le_chan *chan;
|
2015-11-06 13:12:44 +02:00
|
|
|
|
struct bt_l2cap_disconn_rsp *rsp = (void *)buf->data;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t scid;
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE disconn rsp packet size");
|
2015-11-06 13:12:44 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 14:57:33 +03:00
|
|
|
|
scid = sys_le16_to_cpu(rsp->scid);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("dcid 0x%04x scid 0x%04x", sys_le16_to_cpu(rsp->dcid), scid);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
2019-06-11 14:57:33 +03:00
|
|
|
|
chan = l2cap_remove_rx_cid(conn, scid);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
if (!chan) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
}
|
2015-11-12 12:15:56 +02:00
|
|
|
|
|
2024-01-11 18:07:48 +01:00
|
|
|
|
static struct net_buf *l2cap_alloc_seg(struct bt_l2cap_le_chan *ch)
|
2017-04-10 16:41:45 +03:00
|
|
|
|
{
|
2024-01-11 10:47:22 +01:00
|
|
|
|
struct net_buf *seg = NULL;
|
2017-04-10 16:41:45 +03:00
|
|
|
|
|
2024-01-11 10:47:22 +01:00
|
|
|
|
/* Use the user-defined allocator */
|
2022-09-20 15:48:11 +02:00
|
|
|
|
if (ch->chan.ops->alloc_seg) {
|
|
|
|
|
seg = ch->chan.ops->alloc_seg(&ch->chan);
|
|
|
|
|
__ASSERT_NO_MSG(seg);
|
2024-01-11 10:47:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fallback to using global connection tx pool */
|
|
|
|
|
if (!seg) {
|
|
|
|
|
seg = bt_l2cap_create_pdu_timeout(NULL, 0, K_NO_WAIT);
|
2022-09-20 15:48:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 19:16:49 +02:00
|
|
|
|
if (seg) {
|
|
|
|
|
net_buf_reserve(seg, BT_L2CAP_CHAN_SEND_RESERVE);
|
2017-04-10 16:41:45 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 10:47:22 +01:00
|
|
|
|
return seg;
|
2017-04-10 16:41:45 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 18:29:45 +02:00
|
|
|
|
static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
|
|
|
|
|
{
|
2019-12-18 13:27:36 -08:00
|
|
|
|
if (!atomic_get(&ch->tx.credits) ||
|
2019-11-18 18:29:45 +02:00
|
|
|
|
(k_fifo_is_empty(&ch->tx_queue) && !ch->tx_buf)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 16:54:58 +01:00
|
|
|
|
k_work_reschedule(&ch->tx_work, K_NO_WAIT);
|
2022-09-20 16:05:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-31 13:35:34 +02:00
|
|
|
|
static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data, int err)
|
2019-05-21 15:07:43 +03:00
|
|
|
|
{
|
2021-05-25 11:11:36 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
2024-01-09 15:09:10 +01:00
|
|
|
|
uint16_t cid = POINTER_TO_UINT(user_data);
|
2019-05-21 15:07:43 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p CID 0x%04x err %d", conn, cid, err);
|
2021-05-25 11:11:36 +02:00
|
|
|
|
|
2022-05-31 13:56:03 +02:00
|
|
|
|
if (err) {
|
2024-01-09 14:40:50 +01:00
|
|
|
|
LOG_DBG("error %d when sending SDU", err);
|
2022-05-31 13:56:03 +02:00
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, cid);
|
2021-05-25 11:11:36 +02:00
|
|
|
|
if (!chan) {
|
2024-01-09 14:40:50 +01:00
|
|
|
|
LOG_DBG("got SDU sent cb for disconnected chan (CID %u)", cid);
|
|
|
|
|
|
2021-05-25 11:11:36 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-21 15:07:43 +03:00
|
|
|
|
|
|
|
|
|
if (chan->ops->sent) {
|
|
|
|
|
chan->ops->sent(chan);
|
|
|
|
|
}
|
2019-11-13 15:00:23 +02:00
|
|
|
|
|
2022-09-20 16:05:31 +02:00
|
|
|
|
/* Resume the current channel */
|
2019-11-18 18:29:45 +02:00
|
|
|
|
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
2019-11-13 15:00:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-31 13:35:34 +02:00
|
|
|
|
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data, int err)
|
2019-11-13 15:00:23 +02:00
|
|
|
|
{
|
2021-05-25 11:11:36 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
2024-01-09 15:09:10 +01:00
|
|
|
|
uint16_t cid = POINTER_TO_UINT(user_data);
|
2019-11-13 15:00:23 +02:00
|
|
|
|
|
2024-01-09 15:09:10 +01:00
|
|
|
|
LOG_DBG("conn %p CID 0x%04x err %d", conn, cid, err);
|
2022-05-31 13:56:03 +02:00
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-25 11:11:36 +02:00
|
|
|
|
|
2024-01-09 15:09:10 +01:00
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, cid);
|
2021-05-25 11:11:36 +02:00
|
|
|
|
if (!chan) {
|
|
|
|
|
/* Received segment sent callback for disconnected channel */
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-13 15:00:23 +02:00
|
|
|
|
|
2019-11-18 18:29:45 +02:00
|
|
|
|
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
2019-05-21 15:07:43 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-18 13:27:36 -08:00
|
|
|
|
static bool test_and_dec(atomic_t *target)
|
|
|
|
|
{
|
|
|
|
|
atomic_t old_value, new_value;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
old_value = atomic_get(target);
|
|
|
|
|
if (!old_value) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_value = old_value - 1;
|
|
|
|
|
} while (atomic_cas(target, old_value, new_value) == 0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-03 16:22:41 +02:00
|
|
|
|
/* This returns -EAGAIN whenever a segment cannot be send immediately which can
|
|
|
|
|
* happen under the following circuntances:
|
|
|
|
|
*
|
|
|
|
|
* 1. There are no credits
|
|
|
|
|
* 2. There are no buffers
|
|
|
|
|
* 3. There are no TX contexts
|
|
|
|
|
*
|
|
|
|
|
* In all cases the original buffer is unaffected so it can be pushed back to
|
|
|
|
|
* be sent later.
|
|
|
|
|
*/
|
2019-11-29 14:25:06 +02:00
|
|
|
|
static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
struct net_buf *buf, uint16_t sdu_hdr_len)
|
2017-02-16 16:37:41 +02:00
|
|
|
|
{
|
2019-05-21 15:07:43 +03:00
|
|
|
|
struct net_buf *seg;
|
2019-11-29 14:25:06 +02:00
|
|
|
|
struct net_buf_simple_state state;
|
2019-11-18 18:29:45 +02:00
|
|
|
|
int len, err;
|
2024-01-24 08:41:08 +01:00
|
|
|
|
bt_conn_tx_cb_t cb;
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2019-12-18 13:27:36 -08:00
|
|
|
|
if (!test_and_dec(&ch->tx.credits)) {
|
2023-03-07 10:34:33 +01:00
|
|
|
|
LOG_DBG("No credits to transmit packet");
|
2017-02-16 16:37:41 +02:00
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 14:25:06 +02:00
|
|
|
|
/* Save state so it can be restored if we failed to send */
|
|
|
|
|
net_buf_simple_save(&buf->b, &state);
|
|
|
|
|
|
2024-01-11 18:18:10 +01:00
|
|
|
|
if ((buf->len <= ch->tx.mps) &&
|
|
|
|
|
(net_buf_headroom(buf) >= BT_L2CAP_BUF_SIZE(0))) {
|
2024-01-11 18:07:48 +01:00
|
|
|
|
LOG_DBG("len <= MPS, not allocating seg for %p", buf);
|
|
|
|
|
seg = net_buf_ref(buf);
|
|
|
|
|
|
|
|
|
|
len = seg->len;
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DBG("allocating segment for %p (%u bytes left)", buf, buf->len);
|
|
|
|
|
seg = l2cap_alloc_seg(ch);
|
|
|
|
|
if (!seg) {
|
|
|
|
|
LOG_DBG("failed to allocate seg for %p", buf);
|
|
|
|
|
atomic_inc(&ch->tx.credits);
|
|
|
|
|
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Don't send more than TX MPS */
|
|
|
|
|
len = MIN(net_buf_tailroom(seg), ch->tx.mps);
|
|
|
|
|
|
|
|
|
|
/* Limit if original buffer is smaller than the segment */
|
|
|
|
|
len = MIN(buf->len, len);
|
|
|
|
|
|
|
|
|
|
net_buf_add_mem(seg, buf->data, len);
|
|
|
|
|
net_buf_pull(buf, len);
|
2019-11-09 11:42:09 +01:00
|
|
|
|
}
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("ch %p cid 0x%04x len %u credits %lu", ch, ch->tx.cid, seg->len,
|
|
|
|
|
atomic_get(&ch->tx.credits));
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2019-05-21 15:07:43 +03:00
|
|
|
|
len = seg->len - sdu_hdr_len;
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2024-01-11 18:18:10 +01:00
|
|
|
|
/* SDU will be considered sent when there is no data left in the
|
2024-04-04 17:17:11 +02:00
|
|
|
|
* buffer, or if there will be no data left, if we are sending `buf`
|
2024-01-11 18:18:10 +01:00
|
|
|
|
* directly.
|
|
|
|
|
*/
|
2024-04-04 17:17:11 +02:00
|
|
|
|
if (buf->len == 0 || (buf == seg && buf->len == len)) {
|
2024-01-24 08:41:08 +01:00
|
|
|
|
cb = l2cap_chan_sdu_sent;
|
2019-05-21 15:07:43 +03:00
|
|
|
|
} else {
|
2024-01-24 08:41:08 +01:00
|
|
|
|
cb = l2cap_chan_seg_sent;
|
2019-11-18 18:29:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-24 08:41:08 +01:00
|
|
|
|
/* Forward the PDU to the lower layer.
|
|
|
|
|
*
|
|
|
|
|
* Note: after this call, anything in buf->user_data should be
|
|
|
|
|
* considered lost, as the lower layers are free to re-use it as they
|
|
|
|
|
* see fit. Reading from it later is obviously a no-no.
|
|
|
|
|
*/
|
2024-01-09 15:09:10 +01:00
|
|
|
|
err = bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg,
|
|
|
|
|
cb, UINT_TO_POINTER(ch->tx.cid));
|
2024-01-24 08:41:08 +01:00
|
|
|
|
|
2019-11-18 18:29:45 +02:00
|
|
|
|
if (err) {
|
2023-03-07 10:34:33 +01:00
|
|
|
|
LOG_DBG("Unable to send seg %d", err);
|
2019-12-18 13:27:36 -08:00
|
|
|
|
atomic_inc(&ch->tx.credits);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
2022-09-20 15:44:17 +02:00
|
|
|
|
/* The host takes ownership of the reference in seg when
|
|
|
|
|
* bt_l2cap_send_cb is successful. The call returned an error,
|
2024-01-11 18:07:48 +01:00
|
|
|
|
* so we must get rid of the reference that was taken above.
|
2021-03-25 16:51:32 -07:00
|
|
|
|
*/
|
2024-01-11 18:07:48 +01:00
|
|
|
|
LOG_DBG("unref %p (%s)", seg,
|
|
|
|
|
buf == seg ? "orig" : "seg");
|
2022-09-20 15:44:17 +02:00
|
|
|
|
net_buf_unref(seg);
|
2021-03-25 16:51:32 -07:00
|
|
|
|
|
2019-11-18 18:29:45 +02:00
|
|
|
|
if (err == -ENOBUFS) {
|
2019-11-29 14:25:06 +02:00
|
|
|
|
/* Restore state since segment could not be sent */
|
|
|
|
|
net_buf_simple_restore(&buf->b, &state);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
2019-05-21 15:07:43 +03:00
|
|
|
|
}
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2024-01-04 09:55:47 +01:00
|
|
|
|
/* Notify channel user that it can't send anymore on this channel. */
|
2019-12-18 13:27:36 -08:00
|
|
|
|
if (!atomic_get(&ch->tx.credits)) {
|
2024-01-04 09:55:47 +01:00
|
|
|
|
LOG_DBG("chan %p paused", ch);
|
2019-05-22 22:46:25 +03:00
|
|
|
|
atomic_clear_bit(ch->chan.status, BT_L2CAP_STATUS_OUT);
|
2024-01-04 09:55:47 +01:00
|
|
|
|
|
2019-05-22 22:46:25 +03:00
|
|
|
|
if (ch->chan.ops->status) {
|
|
|
|
|
ch->chan.ops->status(&ch->chan, ch->chan.status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 16:37:41 +02:00
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 18:18:10 +01:00
|
|
|
|
/* return next netbuf fragment if present, also assign metadata */
|
2017-02-16 16:37:41 +02:00
|
|
|
|
static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch,
|
2024-04-04 17:17:11 +02:00
|
|
|
|
struct net_buf *buf)
|
2017-02-16 16:37:41 +02:00
|
|
|
|
{
|
2024-01-11 18:18:10 +01:00
|
|
|
|
int ret;
|
2024-04-04 17:17:11 +02:00
|
|
|
|
size_t sent = 0;
|
|
|
|
|
size_t rem_len = buf->len;
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
while (buf && sent != rem_len) {
|
|
|
|
|
ret = l2cap_chan_le_send(ch, buf, 0);
|
2017-02-16 16:37:41 +02:00
|
|
|
|
if (ret < 0) {
|
2024-04-04 17:17:11 +02:00
|
|
|
|
LOG_DBG("failed to send buf (ch %p cid 0x%04x sent %d)",
|
2024-01-11 18:18:10 +01:00
|
|
|
|
ch, ch->tx.cid, sent);
|
|
|
|
|
|
2017-02-16 16:37:41 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 18:18:10 +01:00
|
|
|
|
sent += ret;
|
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
/* If the current buffer has been fully consumed, destroy it */
|
|
|
|
|
if (ret == rem_len) {
|
|
|
|
|
net_buf_unref(buf);
|
2024-01-11 18:18:10 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2024-01-11 18:18:10 +01:00
|
|
|
|
LOG_DBG("ch %p cid 0x%04x sent %u", ch, ch->tx.cid, sent);
|
2017-02-16 16:37:41 +02:00
|
|
|
|
|
2023-08-29 23:48:54 +01:00
|
|
|
|
return sent;
|
2017-02-16 16:37:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void le_credits(struct bt_l2cap *l2cap, uint8_t ident,
|
2015-11-12 12:15:56 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
2015-11-12 12:15:56 +02:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
struct bt_l2cap_le_credits *ev = (void *)buf->data;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t credits, cid;
|
2015-11-12 12:15:56 +02:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*ev)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small LE Credits packet size");
|
2015-11-12 12:15:56 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cid = sys_le16_to_cpu(ev->cid);
|
|
|
|
|
credits = sys_le16_to_cpu(ev->credits);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("cid 0x%04x credits %u", cid, credits);
|
2015-11-12 12:15:56 +02:00
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, cid);
|
2015-11-12 12:15:56 +02:00
|
|
|
|
if (!chan) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to find channel of LE Credits packet");
|
2015-11-12 12:15:56 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(chan);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (atomic_get(&le_chan->tx.credits) + credits > UINT16_MAX) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Credits overflow");
|
2015-11-12 12:15:56 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
l2cap_chan_tx_give_credits(le_chan, credits);
|
2015-11-12 12:15:56 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p total credits %lu", le_chan, atomic_get(&le_chan->tx.credits));
|
2017-03-07 10:20:15 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
l2cap_chan_tx_resume(le_chan);
|
2015-11-12 12:15:56 +02:00
|
|
|
|
}
|
2016-03-01 13:06:31 +02:00
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
|
static void reject_cmd(struct bt_l2cap *l2cap, uint8_t ident,
|
2016-03-01 13:06:31 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2016-05-26 14:03:04 +02:00
|
|
|
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
|
|
|
|
struct bt_l2cap_le_chan *chan;
|
2016-03-01 13:06:31 +02:00
|
|
|
|
|
|
|
|
|
/* Check if there is a outstanding channel */
|
|
|
|
|
chan = l2cap_remove_ident(conn, ident);
|
|
|
|
|
if (!chan) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-11 13:21:41 +03:00
|
|
|
|
bt_l2cap_chan_del(&chan->chan);
|
2016-03-01 13:06:31 +02:00
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
2023-08-29 09:45:51 +00:00
|
|
|
|
struct bt_l2cap_le_chan *l2chan = CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan);
|
|
|
|
|
struct bt_l2cap *l2cap = CONTAINER_OF(l2chan, struct bt_l2cap, chan);
|
2019-01-26 16:51:25 +02:00
|
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t len;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small L2CAP signaling PDU");
|
2018-08-22 13:16:14 +03:00
|
|
|
|
return 0;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-26 16:51:25 +02:00
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
|
len = sys_le16_to_cpu(hdr->len);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("Signaling code 0x%02x ident %u len %u", hdr->code, hdr->ident, len);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
|
|
|
|
if (buf->len != len) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("L2CAP length mismatch (%u != %u)", buf->len, len);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
return 0;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hdr->ident) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Invalid ident value in L2CAP PDU");
|
2018-08-22 13:16:14 +03:00
|
|
|
|
return 0;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (hdr->code) {
|
|
|
|
|
case BT_L2CAP_CONN_PARAM_RSP:
|
2015-10-02 16:21:18 +03:00
|
|
|
|
le_conn_param_rsp(l2cap, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
break;
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2015-09-30 14:49:25 +03:00
|
|
|
|
case BT_L2CAP_LE_CONN_REQ:
|
2015-10-02 16:21:18 +03:00
|
|
|
|
le_conn_req(l2cap, hdr->ident, buf);
|
2015-09-30 14:49:25 +03:00
|
|
|
|
break;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
case BT_L2CAP_LE_CONN_RSP:
|
|
|
|
|
le_conn_rsp(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2015-10-28 15:35:53 +02:00
|
|
|
|
case BT_L2CAP_DISCONN_REQ:
|
|
|
|
|
le_disconn_req(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2015-11-06 13:12:44 +02:00
|
|
|
|
case BT_L2CAP_DISCONN_RSP:
|
|
|
|
|
le_disconn_rsp(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2015-11-12 12:15:56 +02:00
|
|
|
|
case BT_L2CAP_LE_CREDITS:
|
|
|
|
|
le_credits(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2016-03-01 13:06:31 +02:00
|
|
|
|
case BT_L2CAP_CMD_REJECT:
|
|
|
|
|
reject_cmd(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
case BT_L2CAP_ECRED_CONN_REQ:
|
|
|
|
|
le_ecred_conn_req(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_ECRED_CONN_RSP:
|
|
|
|
|
le_ecred_conn_rsp(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
|
|
|
|
case BT_L2CAP_ECRED_RECONF_REQ:
|
|
|
|
|
le_ecred_reconf_req(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2021-09-17 20:33:53 +02:00
|
|
|
|
case BT_L2CAP_ECRED_RECONF_RSP:
|
|
|
|
|
le_ecred_reconf_rsp(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
2016-03-01 13:06:31 +02:00
|
|
|
|
#else
|
|
|
|
|
case BT_L2CAP_CMD_REJECT:
|
|
|
|
|
/* Ignored */
|
|
|
|
|
break;
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2019-10-04 11:44:27 +03:00
|
|
|
|
case BT_L2CAP_CONN_PARAM_REQ:
|
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL)) {
|
|
|
|
|
le_conn_param_update_req(l2cap, hdr->ident, buf);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-21 13:45:52 -07:00
|
|
|
|
__fallthrough;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
default:
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Rejecting unknown L2CAP PDU code 0x%02x", hdr->code);
|
2015-10-28 15:35:53 +02:00
|
|
|
|
l2cap_send_reject(chan->conn, hdr->ident,
|
2016-06-06 09:23:20 +02:00
|
|
|
|
BT_L2CAP_REJ_NOT_UNDERSTOOD, NULL, 0);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
|
|
|
|
return 0;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2019-11-19 15:31:37 +02:00
|
|
|
|
static void l2cap_chan_shutdown(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2019-11-19 15:31:37 +02:00
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p", chan);
|
2019-11-19 15:31:37 +02:00
|
|
|
|
|
|
|
|
|
atomic_set_bit(chan->status, BT_L2CAP_STATUS_SHUTDOWN);
|
|
|
|
|
|
|
|
|
|
/* Destroy segmented SDU if it exists */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->_sdu) {
|
|
|
|
|
net_buf_unref(le_chan->_sdu);
|
|
|
|
|
le_chan->_sdu = NULL;
|
|
|
|
|
le_chan->_sdu_len = 0U;
|
2019-11-19 15:31:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cleanup outstanding request */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->tx_buf) {
|
2023-11-22 16:16:29 +01:00
|
|
|
|
l2cap_tx_buf_destroy(chan->conn, le_chan->tx_buf, -ESHUTDOWN);
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->tx_buf = NULL;
|
2019-11-19 15:31:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove buffers on the TX queue */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
while ((buf = net_buf_get(&le_chan->tx_queue, K_NO_WAIT))) {
|
2023-11-22 16:16:29 +01:00
|
|
|
|
l2cap_tx_buf_destroy(chan->conn, buf, -ESHUTDOWN);
|
2019-11-19 15:31:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove buffers on the RX queue */
|
2022-01-29 09:30:12 +08:00
|
|
|
|
while ((buf = net_buf_get(&le_chan->rx_queue, K_NO_WAIT))) {
|
2019-11-19 15:31:37 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update status */
|
|
|
|
|
if (chan->ops->status) {
|
|
|
|
|
chan->ops->status(chan, chan->status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 16:19:40 +01:00
|
|
|
|
/** @brief Get @c chan->state.
|
|
|
|
|
*
|
|
|
|
|
* This field does not exist when @kconfig{CONFIG_BT_L2CAP_DYNAMIC_CHANNEL} is
|
|
|
|
|
* disabled. In that case, this function returns @ref BT_L2CAP_CONNECTED since
|
|
|
|
|
* the struct can only represent static channels in that case and static
|
|
|
|
|
* channels are always connected.
|
|
|
|
|
*/
|
|
|
|
|
static inline bt_l2cap_chan_state_t bt_l2cap_chan_get_state(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2022-01-29 09:30:12 +08:00
|
|
|
|
return BT_L2CAP_LE_CHAN(chan)->state;
|
2022-02-02 16:19:40 +01:00
|
|
|
|
#else
|
|
|
|
|
return BT_L2CAP_CONNECTED;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan,
|
2023-04-18 08:18:52 +02:00
|
|
|
|
uint16_t credits)
|
2015-10-29 11:14:33 +02:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_le_credits *ev;
|
2023-04-18 08:18:52 +02:00
|
|
|
|
struct net_buf *buf;
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2022-02-02 16:19:40 +01:00
|
|
|
|
__ASSERT_NO_MSG(bt_l2cap_chan_get_state(&chan->chan) == BT_L2CAP_CONNECTED);
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CREDITS, get_ident(),
|
2016-11-08 16:55:09 +02:00
|
|
|
|
sizeof(*ev));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to send credits update");
|
2019-11-19 15:31:37 +02:00
|
|
|
|
/* Disconnect would probably not work either so the only
|
|
|
|
|
* option left is to shutdown the channel.
|
|
|
|
|
*/
|
|
|
|
|
l2cap_chan_shutdown(&chan->chan);
|
2019-08-27 14:52:37 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2023-04-18 08:18:52 +02:00
|
|
|
|
__ASSERT_NO_MSG(atomic_get(&chan->rx.credits) == 0);
|
|
|
|
|
atomic_set(&chan->rx.credits, credits);
|
2019-11-18 18:29:45 +02:00
|
|
|
|
|
2015-10-29 11:14:33 +02:00
|
|
|
|
ev = net_buf_add(buf, sizeof(*ev));
|
2015-11-12 12:12:54 +02:00
|
|
|
|
ev->cid = sys_cpu_to_le16(chan->rx.cid);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
ev->credits = sys_cpu_to_le16(credits);
|
|
|
|
|
|
2021-04-29 18:06:49 +02:00
|
|
|
|
l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p credits %lu", chan, atomic_get(&chan->rx.credits));
|
2015-10-29 11:14:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
|
|
|
|
|
static int l2cap_chan_send_credits_pdu(struct bt_conn *conn, uint16_t cid, uint16_t credits)
|
|
|
|
|
{
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
struct bt_l2cap_le_credits *ev;
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CREDITS, get_ident(), sizeof(*ev));
|
2023-05-15 10:30:41 +02:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ev = net_buf_add(buf, sizeof(*ev));
|
|
|
|
|
*ev = (struct bt_l2cap_le_credits){
|
|
|
|
|
.cid = sys_cpu_to_le16(cid),
|
|
|
|
|
.credits = sys_cpu_to_le16(credits),
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-03 09:37:05 +01:00
|
|
|
|
return l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2023-05-15 10:30:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Combination of @ref atomic_add and @ref u16_add_overflow. Leaves @p
|
|
|
|
|
* target unchanged if an overflow would occur. Assumes the current
|
|
|
|
|
* value of @p target is representable by uint16_t.
|
|
|
|
|
*/
|
|
|
|
|
static bool atomic_add_safe_u16(atomic_t *target, uint16_t addition)
|
|
|
|
|
{
|
|
|
|
|
uint16_t target_old, target_new;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
target_old = atomic_get(target);
|
|
|
|
|
if (u16_add_overflow(target_old, addition, &target_new)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} while (!atomic_cas(target, target_old, target_new));
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bt_l2cap_chan_give_credits(struct bt_l2cap_chan *chan, uint16_t additional_credits)
|
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
|
|
|
|
|
|
|
|
|
if (!chan || !chan->ops) {
|
|
|
|
|
LOG_ERR("%s: Invalid chan object.", __func__);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!chan->ops->seg_recv) {
|
|
|
|
|
LOG_ERR("%s: Available only with seg_recv.", __func__);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (additional_credits == 0) {
|
|
|
|
|
LOG_ERR("%s: Refusing to give 0.", __func__);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bt_l2cap_chan_get_state(chan) == BT_L2CAP_CONNECTING) {
|
|
|
|
|
LOG_ERR("%s: Cannot give credits while connecting.", __func__);
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (atomic_add_safe_u16(&le_chan->rx.credits, additional_credits)) {
|
|
|
|
|
LOG_ERR("%s: Overflow.", __func__);
|
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bt_l2cap_chan_get_state(chan) == BT_L2CAP_CONNECTED) {
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = l2cap_chan_send_credits_pdu(chan->conn, le_chan->rx.cid, additional_credits);
|
|
|
|
|
if (err) {
|
|
|
|
|
LOG_ERR("%s: PDU failed %d.", __func__, err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_BT_L2CAP_SEG_RECV */
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
int bt_l2cap_chan_recv_complete(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
struct bt_conn *conn = chan->conn;
|
|
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(chan);
|
|
|
|
|
__ASSERT_NO_MSG(buf);
|
|
|
|
|
|
2023-04-18 08:18:52 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
if (!conn) {
|
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (conn->type != BT_CONN_TYPE_LE) {
|
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p buf %p", chan, buf);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
2022-04-12 11:24:38 +02:00
|
|
|
|
if (bt_l2cap_chan_get_state(&le_chan->chan) == BT_L2CAP_CONNECTED) {
|
2023-04-18 08:18:52 +02:00
|
|
|
|
l2cap_chan_send_credits(le_chan, 1);
|
2022-04-12 11:24:38 +02:00
|
|
|
|
}
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-06 13:33:41 +02:00
|
|
|
|
static struct net_buf *l2cap_alloc_frag(k_timeout_t timeout, void *user_data)
|
2016-10-06 13:51:31 +03:00
|
|
|
|
{
|
2018-09-11 14:14:20 +03:00
|
|
|
|
struct bt_l2cap_le_chan *chan = user_data;
|
2016-10-06 13:51:31 +03:00
|
|
|
|
struct net_buf *frag = NULL;
|
|
|
|
|
|
2016-10-17 16:08:47 +03:00
|
|
|
|
frag = chan->chan.ops->alloc_buf(&chan->chan);
|
|
|
|
|
if (!frag) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2016-10-06 13:51:31 +03:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("frag %p tailroom %zu", frag, net_buf_tailroom(frag));
|
2016-10-06 13:51:31 +03:00
|
|
|
|
|
|
|
|
|
return frag;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static void l2cap_chan_le_recv_sdu(struct bt_l2cap_le_chan *chan,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
struct net_buf *buf, uint16_t seg)
|
2018-08-22 13:16:14 +03:00
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
LOG_DBG("chan %p len %zu", chan, buf->len);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
2022-02-02 16:19:40 +01:00
|
|
|
|
__ASSERT_NO_MSG(bt_l2cap_chan_get_state(&chan->chan) == BT_L2CAP_CONNECTED);
|
2023-04-18 08:18:52 +02:00
|
|
|
|
__ASSERT_NO_MSG(atomic_get(&chan->rx.credits) == 0);
|
2022-02-02 16:19:40 +01:00
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
/* Receiving complete SDU, notify channel and reset SDU buf */
|
|
|
|
|
err = chan->chan.ops->recv(&chan->chan, buf);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err != -EINPROGRESS) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("err %d", err);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
return;
|
2023-04-18 08:18:52 +02:00
|
|
|
|
} else if (bt_l2cap_chan_get_state(&chan->chan) == BT_L2CAP_CONNECTED) {
|
|
|
|
|
l2cap_chan_send_credits(chan, 1);
|
2022-02-02 16:19:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void l2cap_chan_le_recv_seg(struct bt_l2cap_le_chan *chan,
|
2015-11-16 18:18:45 +02:00
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t len;
|
|
|
|
|
uint16_t seg = 0U;
|
2016-10-06 13:51:31 +03:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
len = chan->_sdu->len;
|
2018-08-22 13:16:14 +03:00
|
|
|
|
if (len) {
|
|
|
|
|
memcpy(&seg, net_buf_user_data(chan->_sdu), sizeof(seg));
|
|
|
|
|
}
|
2015-11-16 18:18:45 +02:00
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
if (len + buf->len > chan->_sdu_len) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("SDU length mismatch");
|
2016-05-26 14:03:04 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
2015-11-16 18:18:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
seg++;
|
|
|
|
|
/* Store received segments in user_data */
|
|
|
|
|
memcpy(net_buf_user_data(chan->_sdu), &seg, sizeof(seg));
|
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
LOG_DBG("chan %p seg %d len %zu", chan, seg, buf->len);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
2018-09-11 14:14:20 +03:00
|
|
|
|
/* Append received segment to SDU */
|
|
|
|
|
len = net_buf_append_bytes(chan->_sdu, buf->len, buf->data, K_NO_WAIT,
|
|
|
|
|
l2cap_alloc_frag, chan);
|
|
|
|
|
if (len != buf->len) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to store SDU");
|
2018-09-11 14:14:20 +03:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
return;
|
2016-10-06 13:51:31 +03:00
|
|
|
|
}
|
2015-11-16 18:18:45 +02:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
if (chan->_sdu->len < chan->_sdu_len) {
|
2018-09-04 11:14:30 +03:00
|
|
|
|
/* Give more credits if remote has run out of them, this
|
|
|
|
|
* should only happen if the remote cannot fully utilize the
|
|
|
|
|
* MPS for some reason.
|
2023-04-18 08:18:52 +02:00
|
|
|
|
*
|
|
|
|
|
* We can't send more than one credit, because if the remote
|
|
|
|
|
* decides to start fully utilizing the MPS for the remainder of
|
|
|
|
|
* the SDU, then the remote will end up with more credits than
|
|
|
|
|
* the app has buffers.
|
2018-09-04 11:14:30 +03:00
|
|
|
|
*/
|
2023-04-18 08:18:52 +02:00
|
|
|
|
if (atomic_get(&chan->rx.credits) == 0) {
|
|
|
|
|
LOG_DBG("remote is not fully utilizing MPS");
|
|
|
|
|
l2cap_chan_send_credits(chan, 1);
|
2018-09-04 11:14:30 +03:00
|
|
|
|
}
|
2023-04-18 08:18:52 +02:00
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
return;
|
2015-11-16 18:18:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
buf = chan->_sdu;
|
|
|
|
|
chan->_sdu = NULL;
|
2019-03-26 19:57:45 -06:00
|
|
|
|
chan->_sdu_len = 0U;
|
2018-08-22 13:16:14 +03:00
|
|
|
|
|
|
|
|
|
l2cap_chan_le_recv_sdu(chan, buf, seg);
|
2015-11-16 18:18:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
|
|
|
|
|
static void l2cap_chan_le_recv_seg_direct(struct bt_l2cap_le_chan *chan, struct net_buf *seg)
|
|
|
|
|
{
|
|
|
|
|
uint16_t seg_offset;
|
|
|
|
|
uint16_t sdu_remaining;
|
|
|
|
|
|
|
|
|
|
if (chan->_sdu_len_done == chan->_sdu_len) {
|
|
|
|
|
|
|
|
|
|
/* This is the first PDU in a SDU. */
|
|
|
|
|
|
|
|
|
|
if (seg->len < 2) {
|
|
|
|
|
LOG_WRN("Missing SDU header");
|
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pop off the "SDU header". */
|
|
|
|
|
chan->_sdu_len = net_buf_pull_le16(seg);
|
|
|
|
|
chan->_sdu_len_done = 0;
|
|
|
|
|
|
|
|
|
|
if (chan->_sdu_len > chan->rx.mtu) {
|
|
|
|
|
LOG_WRN("SDU exceeds MTU");
|
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seg_offset = chan->_sdu_len_done;
|
|
|
|
|
sdu_remaining = chan->_sdu_len - chan->_sdu_len_done;
|
|
|
|
|
|
|
|
|
|
if (seg->len > sdu_remaining) {
|
|
|
|
|
LOG_WRN("L2CAP RX PDU total exceeds SDU");
|
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Commit receive. */
|
|
|
|
|
chan->_sdu_len_done += seg->len;
|
|
|
|
|
|
|
|
|
|
/* Tail call. */
|
|
|
|
|
chan->chan.ops->seg_recv(&chan->chan, chan->_sdu_len, seg_offset, &seg->b);
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_BT_L2CAP_SEG_RECV */
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan,
|
|
|
|
|
struct net_buf *buf)
|
2015-10-29 11:14:33 +02:00
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t sdu_len;
|
2018-08-22 13:16:14 +03:00
|
|
|
|
int err;
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2019-12-18 13:27:36 -08:00
|
|
|
|
if (!test_and_dec(&chan->rx.credits)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("No credits to receive packet");
|
2016-05-26 14:03:04 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-29 13:35:45 +01:00
|
|
|
|
if (buf->len > chan->rx.mps) {
|
|
|
|
|
LOG_WRN("PDU size > MPS (%u > %u)", buf->len, chan->rx.mps);
|
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 10:30:41 +02:00
|
|
|
|
/* Redirect to experimental API. */
|
|
|
|
|
IF_ENABLED(CONFIG_BT_L2CAP_SEG_RECV, (
|
|
|
|
|
if (chan->chan.ops->seg_recv) {
|
|
|
|
|
l2cap_chan_le_recv_seg_direct(chan, buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
))
|
|
|
|
|
|
2015-11-16 18:18:45 +02:00
|
|
|
|
/* Check if segments already exist */
|
|
|
|
|
if (chan->_sdu) {
|
2018-08-22 13:16:14 +03:00
|
|
|
|
l2cap_chan_le_recv_seg(chan, buf);
|
2015-11-16 18:18:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 20:44:01 +02:00
|
|
|
|
if (buf->len < 2) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Too short data packet");
|
2021-02-23 20:44:01 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 11:14:33 +02:00
|
|
|
|
sdu_len = net_buf_pull_le16(buf);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p len %u sdu_len %u", chan, buf->len, sdu_len);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
|
|
|
|
if (sdu_len > chan->rx.mtu) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Invalid SDU length");
|
2016-05-26 14:03:04 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-26 13:25:06 +02:00
|
|
|
|
/* Always allocate buffer from the channel if supported. */
|
2017-06-01 14:17:27 +03:00
|
|
|
|
if (chan->chan.ops->alloc_buf) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
chan->_sdu = chan->chan.ops->alloc_buf(&chan->chan);
|
2015-11-26 13:14:58 +02:00
|
|
|
|
if (!chan->_sdu) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Unable to allocate buffer for SDU");
|
2016-05-26 14:03:04 +02:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
2015-11-16 18:18:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
chan->_sdu_len = sdu_len;
|
2023-04-18 08:18:52 +02:00
|
|
|
|
|
|
|
|
|
/* Send sdu_len/mps worth of credits */
|
|
|
|
|
uint16_t credits = DIV_ROUND_UP(
|
|
|
|
|
MIN(sdu_len - buf->len, net_buf_tailroom(chan->_sdu)),
|
|
|
|
|
chan->rx.mps);
|
|
|
|
|
|
|
|
|
|
if (credits) {
|
|
|
|
|
LOG_DBG("sending %d extra credits (sdu_len %d buf_len %d mps %d)",
|
|
|
|
|
credits,
|
|
|
|
|
sdu_len,
|
|
|
|
|
buf->len,
|
|
|
|
|
chan->rx.mps);
|
|
|
|
|
l2cap_chan_send_credits(chan, credits);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
l2cap_chan_le_recv_seg(chan, buf);
|
2015-11-16 18:18:45 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 13:16:14 +03:00
|
|
|
|
err = chan->chan.ops->recv(&chan->chan, buf);
|
2021-12-29 14:55:41 -08:00
|
|
|
|
if (err < 0) {
|
2018-08-22 13:16:14 +03:00
|
|
|
|
if (err != -EINPROGRESS) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("err %d", err);
|
2018-08-22 13:16:14 +03:00
|
|
|
|
bt_l2cap_chan_disconnect(&chan->chan);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2023-07-11 13:12:19 +00:00
|
|
|
|
/* Only attempt to send credits if the channel wasn't disconnected
|
|
|
|
|
* in the recv() callback above
|
|
|
|
|
*/
|
|
|
|
|
if (bt_l2cap_chan_get_state(&chan->chan) == BT_L2CAP_CONNECTED) {
|
|
|
|
|
l2cap_chan_send_credits(chan, 1);
|
|
|
|
|
}
|
2015-10-29 11:14:33 +02:00
|
|
|
|
}
|
2019-11-18 13:53:57 +02:00
|
|
|
|
|
|
|
|
|
static void l2cap_chan_recv_queue(struct bt_l2cap_le_chan *chan,
|
|
|
|
|
struct net_buf *buf)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (chan->state == BT_L2CAP_DISCONNECTING) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Ignoring data received while disconnecting");
|
2019-11-18 13:53:57 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-19 15:31:37 +02:00
|
|
|
|
if (atomic_test_bit(chan->chan.status, BT_L2CAP_STATUS_SHUTDOWN)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Ignoring data received while channel has shutdown");
|
2019-11-19 15:31:37 +02:00
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (!L2CAP_LE_PSM_IS_DYN(chan->psm)) {
|
2020-02-27 14:55:00 -08:00
|
|
|
|
l2cap_chan_le_recv(chan, buf);
|
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 13:53:57 +02:00
|
|
|
|
net_buf_put(&chan->rx_queue, buf);
|
|
|
|
|
k_work_submit(&chan->rx_work);
|
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2021-11-08 16:29:51 +01:00
|
|
|
|
static void l2cap_chan_recv(struct bt_l2cap_chan *chan, struct net_buf *buf,
|
|
|
|
|
bool complete)
|
2015-10-29 11:14:33 +02:00
|
|
|
|
{
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (L2CAP_LE_CID_IS_DYN(le_chan->rx.cid)) {
|
2021-11-08 16:29:51 +01:00
|
|
|
|
if (complete) {
|
2022-01-29 09:30:12 +08:00
|
|
|
|
l2cap_chan_recv_queue(le_chan, buf);
|
2021-11-08 16:29:51 +01:00
|
|
|
|
} else {
|
|
|
|
|
/* if packet was not complete this means peer device
|
|
|
|
|
* overflowed our RX and channel shall be disconnected
|
|
|
|
|
*/
|
|
|
|
|
bt_l2cap_chan_disconnect(chan);
|
|
|
|
|
net_buf_unref(buf);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 11:14:33 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p len %u", chan, buf->len);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
|
|
|
|
|
chan->ops->recv(chan, buf);
|
2019-11-28 15:21:45 +02:00
|
|
|
|
net_buf_unref(buf);
|
2015-10-29 11:14:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 16:29:51 +01:00
|
|
|
|
void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf, bool complete)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
2019-01-26 16:51:25 +02:00
|
|
|
|
struct bt_l2cap_hdr *hdr;
|
2015-05-21 18:53:13 +03:00
|
|
|
|
struct bt_l2cap_chan *chan;
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t cid;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2017-01-17 19:10:33 +02:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
2016-09-28 16:59:34 +02:00
|
|
|
|
bt_l2cap_br_recv(conn, buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-28 10:43:14 +03:00
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("Too small L2CAP PDU received");
|
2015-10-28 10:41:10 +02:00
|
|
|
|
net_buf_unref(buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-26 16:51:25 +02:00
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
|
cid = sys_le16_to_cpu(hdr->cid);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("Packet for CID %u len %u", cid, buf->len);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2016-09-28 16:59:34 +02:00
|
|
|
|
chan = bt_l2cap_le_lookup_rx_cid(conn, cid);
|
2015-05-21 18:53:13 +03:00
|
|
|
|
if (!chan) {
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_WRN("Ignoring data for unknown channel ID 0x%04x", cid);
|
2015-10-28 10:41:10 +02:00
|
|
|
|
net_buf_unref(buf);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
return;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
|
2021-11-08 16:29:51 +01:00
|
|
|
|
l2cap_chan_recv(chan, buf, complete);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-02 12:48:42 +01:00
|
|
|
|
int bt_l2cap_update_conn_param(struct bt_conn *conn,
|
|
|
|
|
const struct bt_le_conn_param *param)
|
2015-04-28 10:43:14 +03:00
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_conn_param_req *req;
|
2015-10-28 10:41:10 +02:00
|
|
|
|
struct net_buf *buf;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_CONN_PARAM_REQ,
|
2017-04-04 15:18:18 +03:00
|
|
|
|
get_ident(), sizeof(*req));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2015-10-28 10:41:10 +02:00
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2016-02-02 12:48:42 +01:00
|
|
|
|
req->min_interval = sys_cpu_to_le16(param->interval_min);
|
|
|
|
|
req->max_interval = sys_cpu_to_le16(param->interval_max);
|
|
|
|
|
req->latency = sys_cpu_to_le16(param->latency);
|
|
|
|
|
req->timeout = sys_cpu_to_le16(param->timeout);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
2024-01-03 09:37:05 +01:00
|
|
|
|
return l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
|
2015-10-02 16:21:18 +03:00
|
|
|
|
static void l2cap_connected(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("ch %p cid 0x%04x", BT_L2CAP_LE_CHAN(chan), BT_L2CAP_LE_CHAN(chan)->rx.cid);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void l2cap_disconnected(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
2021-04-12 13:03:22 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("ch %p cid 0x%04x", le_chan, le_chan->rx.cid);
|
2022-01-29 09:30:12 +08:00
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2021-04-12 13:03:22 +02:00
|
|
|
|
/* Cancel RTX work on signal channel.
|
2022-03-16 21:07:43 +00:00
|
|
|
|
* Disconnected callback is always called from system workqueue
|
2021-04-12 13:03:22 +02:00
|
|
|
|
* so this should always succeed.
|
|
|
|
|
*/
|
2022-01-29 09:30:12 +08:00
|
|
|
|
(void)k_work_cancel_delayable(&le_chan->rtx_work);
|
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
2015-10-02 16:21:18 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2019-12-18 12:44:56 +02:00
|
|
|
|
static const struct bt_l2cap_chan_ops ops = {
|
2015-10-02 16:21:18 +03:00
|
|
|
|
.connected = l2cap_connected,
|
|
|
|
|
.disconnected = l2cap_disconnected,
|
|
|
|
|
.recv = l2cap_recv,
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p handle %u", conn, conn->handle);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_l2cap_pool); i++) {
|
|
|
|
|
struct bt_l2cap *l2cap = &bt_l2cap_pool[i];
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
if (l2cap->chan.chan.conn) {
|
2015-10-02 16:21:18 +03:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
l2cap->chan.chan.ops = &ops;
|
|
|
|
|
*chan = &l2cap->chan.chan;
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_ERR("No available L2CAP context for conn %p", conn);
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-28 09:51:44 +01:00
|
|
|
|
BT_L2CAP_CHANNEL_DEFINE(le_fixed_chan, BT_L2CAP_CID_LE_SIG, l2cap_accept, NULL);
|
2019-05-20 15:11:39 +03:00
|
|
|
|
|
2015-11-03 17:15:40 +02:00
|
|
|
|
void bt_l2cap_init(void)
|
2015-05-21 18:53:13 +03:00
|
|
|
|
{
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC)) {
|
2017-01-17 19:10:33 +02:00
|
|
|
|
bt_l2cap_br_init();
|
|
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
}
|
2015-10-02 16:21:18 +03:00
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
2016-06-13 15:11:18 +03:00
|
|
|
|
static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t psm)
|
2015-11-04 14:46:56 +02:00
|
|
|
|
{
|
2020-07-30 11:35:25 +02:00
|
|
|
|
int err;
|
|
|
|
|
|
2018-05-18 07:43:21 +03:00
|
|
|
|
if (psm < L2CAP_LE_PSM_FIXED_START || psm > L2CAP_LE_PSM_DYN_END) {
|
2015-11-04 14:46:56 +02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 14:03:04 +02:00
|
|
|
|
l2cap_chan_tx_init(ch);
|
|
|
|
|
l2cap_chan_rx_init(ch);
|
2015-11-16 14:44:44 +02:00
|
|
|
|
|
2016-07-08 16:30:58 +03:00
|
|
|
|
if (!l2cap_chan_add(conn, &ch->chan, l2cap_chan_destroy)) {
|
2015-11-04 14:46:56 +02:00
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->psm = psm;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (conn->sec_level < ch->required_sec_level) {
|
|
|
|
|
err = bt_conn_set_security(conn, ch->required_sec_level);
|
2020-07-29 20:04:03 +02:00
|
|
|
|
if (err) {
|
2020-07-30 11:35:25 +02:00
|
|
|
|
goto fail;
|
2020-07-29 20:04:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
atomic_set_bit(ch->chan.status,
|
|
|
|
|
BT_L2CAP_STATUS_ENCRYPT_PENDING);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 11:35:25 +02:00
|
|
|
|
err = l2cap_le_conn_req(ch);
|
|
|
|
|
if (err) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
bt_l2cap_chan_remove(conn, &ch->chan);
|
|
|
|
|
bt_l2cap_chan_del(&ch->chan);
|
|
|
|
|
return err;
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#if defined(CONFIG_BT_L2CAP_ECRED)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
static int l2cap_ecred_init(struct bt_conn *conn,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
struct bt_l2cap_le_chan *ch, uint16_t psm)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (psm < L2CAP_LE_PSM_FIXED_START || psm > L2CAP_LE_PSM_DYN_END) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l2cap_chan_tx_init(ch);
|
|
|
|
|
l2cap_chan_rx_init(ch);
|
|
|
|
|
|
|
|
|
|
if (!l2cap_chan_add(conn, &ch->chan, l2cap_chan_destroy)) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->psm = psm;
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2023-04-18 08:18:52 +02:00
|
|
|
|
LOG_DBG("ch %p psm 0x%02x mtu %u mps %u credits 1", ch, ch->psm, ch->rx.mtu, ch->rx.mps);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
struct bt_l2cap_chan **chan, uint16_t psm)
|
2020-02-26 10:29:23 -08:00
|
|
|
|
{
|
|
|
|
|
int i, err;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p chan %p psm 0x%04x", conn, chan, psm);
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
|
|
|
|
if (!conn || !chan) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Init non-null channels */
|
2022-01-26 15:45:08 +01:00
|
|
|
|
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
|
2020-02-26 10:29:23 -08:00
|
|
|
|
if (!chan[i]) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = l2cap_ecred_init(conn, BT_L2CAP_LE_CHAN(chan[i]), psm);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
i--;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l2cap_ecred_conn_req(chan, i);
|
|
|
|
|
fail:
|
|
|
|
|
/* Remove channels added */
|
|
|
|
|
for (; i >= 0; i--) {
|
|
|
|
|
if (!chan[i]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bt_l2cap_chan_remove(conn, chan[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2021-09-17 20:33:53 +02:00
|
|
|
|
|
|
|
|
|
static struct bt_l2cap_le_chan *l2cap_find_pending_reconf(struct bt_conn *conn)
|
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
|
|
|
|
if (BT_L2CAP_LE_CHAN(chan)->pending_rx_mtu) {
|
|
|
|
|
return BT_L2CAP_LE_CHAN(chan);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
|
|
|
|
|
{
|
|
|
|
|
struct bt_l2cap_ecred_reconf_req *req;
|
|
|
|
|
struct bt_conn *conn = NULL;
|
|
|
|
|
struct bt_l2cap_le_chan *ch;
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
uint8_t ident;
|
|
|
|
|
int i;
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chans %p mtu 0x%04x", chans, mtu);
|
2021-09-17 20:33:53 +02:00
|
|
|
|
|
|
|
|
|
if (!chans) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-26 15:45:08 +01:00
|
|
|
|
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
|
2021-09-17 20:33:53 +02:00
|
|
|
|
if (!chans[i]) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* validate that all channels are from same connection */
|
|
|
|
|
if (conn) {
|
|
|
|
|
if (conn != chans[i]->conn) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
conn = chans[i]->conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* validate MTU is not decreased */
|
|
|
|
|
if (mtu < BT_L2CAP_LE_CHAN(chans[i])->rx.mtu) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!conn) {
|
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (conn->type != BT_CONN_TYPE_LE) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* allow only 1 request at time */
|
|
|
|
|
if (l2cap_find_pending_reconf(conn)) {
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ident = get_ident();
|
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_RECONF_REQ,
|
2021-09-17 20:33:53 +02:00
|
|
|
|
ident,
|
|
|
|
|
sizeof(*req) + (i * sizeof(uint16_t)));
|
|
|
|
|
if (!buf) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
|
req->mtu = sys_cpu_to_le16(mtu);
|
|
|
|
|
|
|
|
|
|
/* MPS shall not be bigger than MTU + BT_L2CAP_SDU_HDR_SIZE
|
|
|
|
|
* as the remaining bytes cannot be used.
|
|
|
|
|
*/
|
|
|
|
|
req->mps = sys_cpu_to_le16(MIN(mtu + BT_L2CAP_SDU_HDR_SIZE,
|
|
|
|
|
BT_L2CAP_RX_MTU));
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
|
|
|
ch = BT_L2CAP_LE_CHAN(chans[j]);
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
ch->ident = ident;
|
2021-09-17 20:33:53 +02:00
|
|
|
|
ch->pending_rx_mtu = mtu;
|
|
|
|
|
|
|
|
|
|
net_buf_add_le16(buf, ch->rx.cid);
|
|
|
|
|
};
|
|
|
|
|
|
2022-05-25 15:36:21 +02:00
|
|
|
|
/* We set the RTX timer on one of the supplied channels, but when the
|
|
|
|
|
* request resolves or times out we will act on all the channels in the
|
|
|
|
|
* supplied array, using the ident field to find them.
|
|
|
|
|
*/
|
2021-09-17 20:33:53 +02:00
|
|
|
|
l2cap_chan_send_req(chans[0], buf, L2CAP_CONN_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 13:02:47 +01:00
|
|
|
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
2020-02-26 10:29:23 -08:00
|
|
|
|
|
2015-11-04 14:46:56 +02:00
|
|
|
|
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
2020-05-27 11:26:57 -05:00
|
|
|
|
uint16_t psm)
|
2015-11-04 14:46:56 +02:00
|
|
|
|
{
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("conn %p chan %p psm 0x%04x", conn, chan, psm);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
|
|
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!chan) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2017-01-17 19:10:33 +02:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return bt_l2cap_br_chan_connect(conn, chan, psm);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
if (le_chan->required_sec_level > BT_SECURITY_L4) {
|
2016-11-01 11:05:35 +02:00
|
|
|
|
return -EINVAL;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
} else if (le_chan->required_sec_level == BT_SECURITY_L0) {
|
|
|
|
|
le_chan->required_sec_level = BT_SECURITY_L1;
|
2016-11-01 11:05:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
return l2cap_le_connect(conn, le_chan, psm);
|
2015-11-04 14:46:56 +02:00
|
|
|
|
}
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
|
|
|
|
int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
|
|
|
|
{
|
|
|
|
|
struct bt_conn *conn = chan->conn;
|
2016-06-13 14:56:52 +03:00
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
struct bt_l2cap_disconn_req *req;
|
2022-01-29 09:30:12 +08:00
|
|
|
|
struct bt_l2cap_le_chan *le_chan;
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
|
|
|
|
if (!conn) {
|
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2017-01-17 19:10:33 +02:00
|
|
|
|
conn->type == BT_CONN_TYPE_BR) {
|
2016-05-26 14:03:04 +02:00
|
|
|
|
return bt_l2cap_br_chan_disconnect(chan);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan = BT_L2CAP_LE_CHAN(chan);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
2022-11-02 14:31:13 +01:00
|
|
|
|
LOG_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, le_chan->rx.cid, le_chan->tx.cid);
|
2016-05-26 14:03:04 +02:00
|
|
|
|
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->ident = get_ident();
|
2016-11-08 16:55:09 +02:00
|
|
|
|
|
2023-11-22 16:59:25 +01:00
|
|
|
|
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ,
|
2022-01-29 09:30:12 +08:00
|
|
|
|
le_chan->ident, sizeof(*req));
|
2019-08-27 14:52:37 +03:00
|
|
|
|
if (!buf) {
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2022-01-29 09:30:12 +08:00
|
|
|
|
req->dcid = sys_cpu_to_le16(le_chan->tx.cid);
|
|
|
|
|
req->scid = sys_cpu_to_le16(le_chan->rx.cid);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
2020-02-26 10:29:23 -08:00
|
|
|
|
l2cap_chan_send_req(chan, buf, L2CAP_DISC_TIMEOUT);
|
2022-02-22 15:19:03 +01:00
|
|
|
|
bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECTING);
|
2015-11-06 13:12:44 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-11-12 13:42:10 +02:00
|
|
|
|
|
2024-01-11 17:44:39 +01:00
|
|
|
|
static int bt_l2cap_dyn_chan_send(struct bt_l2cap_le_chan *le_chan, struct net_buf *buf)
|
2015-11-12 13:42:10 +02:00
|
|
|
|
{
|
2024-04-04 17:17:11 +02:00
|
|
|
|
uint16_t sdu_len = buf->len;
|
2016-02-17 16:37:26 +02:00
|
|
|
|
|
2024-01-11 17:51:29 +01:00
|
|
|
|
LOG_DBG("chan %p buf %p", le_chan, buf);
|
2024-01-11 17:37:11 +01:00
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
/* Frags are not supported. */
|
|
|
|
|
__ASSERT_NO_MSG(buf->frags == NULL);
|
|
|
|
|
|
2024-01-11 17:37:11 +01:00
|
|
|
|
if (sdu_len > le_chan->tx.mtu) {
|
2024-01-11 17:51:29 +01:00
|
|
|
|
LOG_ERR("attempt to send %u bytes on %u MTU chan",
|
|
|
|
|
sdu_len, le_chan->tx.mtu);
|
2024-01-11 17:37:11 +01:00
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (net_buf_headroom(buf) < BT_L2CAP_SDU_CHAN_SEND_RESERVE) {
|
|
|
|
|
/* Call `net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE)`
|
|
|
|
|
* when allocating buffers intended for bt_l2cap_chan_send().
|
|
|
|
|
*/
|
|
|
|
|
LOG_DBG("Not enough headroom in buf %p", buf);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-27 10:43:58 +01:00
|
|
|
|
/* Prepend SDU length.
|
2024-01-11 17:37:11 +01:00
|
|
|
|
*
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* L2CAP LE CoC SDUs are segmented and put into K-frames PDUs which have
|
|
|
|
|
* their own L2CAP header (i.e. PDU length, channel id).
|
2024-01-11 17:37:11 +01:00
|
|
|
|
*
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* The SDU length is right before the data that will be segmented and is
|
|
|
|
|
* only present in the first PDU. Here's an example:
|
2024-01-11 17:37:11 +01:00
|
|
|
|
*
|
|
|
|
|
* Sent data payload of 50 bytes over channel 0x4040 with MPS of 30 bytes:
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* First PDU (K-frame):
|
2024-01-11 17:37:11 +01:00
|
|
|
|
* | L2CAP K-frame header | K-frame payload |
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* | PDU length | Channel ID | SDU length | SDU payload |
|
|
|
|
|
* | 0x001e | 0x4040 | 0x0032 | 28 bytes of data |
|
2024-01-11 17:37:11 +01:00
|
|
|
|
*
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* Second and last PDU (K-frame):
|
2024-01-11 17:37:11 +01:00
|
|
|
|
* | L2CAP K-frame header | K-frame payload |
|
|
|
|
|
* | PDU length | Channel ID | rest of SDU payload |
|
2024-03-27 10:43:58 +01:00
|
|
|
|
* | 0x0016 | 0x4040 | 22 bytes of data |
|
2024-01-11 17:37:11 +01:00
|
|
|
|
*/
|
|
|
|
|
net_buf_push_le16(buf, sdu_len);
|
|
|
|
|
|
2024-01-11 17:51:29 +01:00
|
|
|
|
/* Put buffer on TX queue */
|
|
|
|
|
net_buf_put(&le_chan->tx_queue, buf);
|
2022-03-25 14:26:40 +01:00
|
|
|
|
|
2024-01-11 17:51:29 +01:00
|
|
|
|
/* Always process the queue in the same context */
|
|
|
|
|
k_work_reschedule(&le_chan->tx_work, K_NO_WAIT);
|
2022-08-29 15:46:10 +02:00
|
|
|
|
|
2024-01-11 17:51:29 +01:00
|
|
|
|
return 0;
|
2015-11-12 13:42:10 +02:00
|
|
|
|
}
|
2022-03-25 14:26:40 +01:00
|
|
|
|
|
2024-01-11 17:44:39 +01:00
|
|
|
|
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|
|
|
|
{
|
|
|
|
|
if (!buf || !chan) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-04 17:17:11 +02:00
|
|
|
|
LOG_DBG("chan %p buf %p len %zu", chan, buf, buf->len);
|
2024-01-11 17:44:39 +01:00
|
|
|
|
|
|
|
|
|
if (!chan->conn || chan->conn->state != BT_CONN_CONNECTED) {
|
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (atomic_test_bit(chan->status, BT_L2CAP_STATUS_SHUTDOWN)) {
|
|
|
|
|
return -ESHUTDOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 18:45:44 +08:00
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CLASSIC) &&
|
2024-01-11 17:44:39 +01:00
|
|
|
|
chan->conn->type == BT_CONN_TYPE_BR) {
|
|
|
|
|
return bt_l2cap_br_chan_send_cb(chan, buf, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sending over static channels is not supported by this fn. Use
|
|
|
|
|
* `bt_l2cap_send()` if external to this file, or `l2cap_send` if
|
|
|
|
|
* internal.
|
|
|
|
|
*/
|
|
|
|
|
if (IS_ENABLED(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)) {
|
|
|
|
|
struct bt_l2cap_le_chan *le_chan = BT_L2CAP_LE_CHAN(chan);
|
|
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(le_chan);
|
|
|
|
|
__ASSERT_NO_MSG(L2CAP_LE_CID_IS_DYN(le_chan->tx.cid));
|
|
|
|
|
|
|
|
|
|
return bt_l2cap_dyn_chan_send(le_chan, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("Invalid channel type (chan %p)", chan);
|
|
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2017-08-09 09:21:11 +03:00
|
|
|
|
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|