Bluetooth: Classic: SDP: Improve SDP discover

Extend the function `bt_sdp_discover` to support service search
transaction and service attribute transaction.

Improve the `session->rec_buf`. If the net buffer cannot be allocated
from the channel, disconnect the SDP session.

Set the `MaximumAttributeByteCount` of the request
`SDP_SERVICE_SEARCH_ATTR_REQ` with the tail room of `session->rec_buf`.

Set the `MaximumAttributeByteCount` of the request `SDP_SERVICE_ATTR_REQ`
with the tail room of `session->rec_buf`.

Set the `MaximumServiceRecordCount` of the request
`SDP_SERVICE_SEARCH_REQ` according to the tail room of
`session->rec_buf`.

Handle the response code `SDP_SERVICE_SEARCH_RSP`, and
`SDP_SERVICE_ATTR_RSP`.

Handle the error `SDP_ERROR_RSP`. Start the next SDP discovery if the
error received.

If there no more request, disconnect the session.

If the request cannot be sent, start the next SDP discovery.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
This commit is contained in:
Lyle Zhu 2024-09-05 20:32:15 +08:00 committed by Benjamin Cabé
parent ff5a4581b2
commit a047113556
2 changed files with 440 additions and 123 deletions

View File

@ -481,8 +481,6 @@ struct bt_sdp_client_result {
struct net_buf *resp_buf;
/** flag pointing that there are more result chunks for given UUID */
bool next_record_hint;
/** Reference to UUID object on behalf one discovery was started */
const struct bt_uuid *uuid;
};
/** @brief Helper enum to be used as return value of bt_sdp_discover_func_t.
@ -493,6 +491,8 @@ enum {
BT_SDP_DISCOVER_UUID_CONTINUE,
};
struct bt_sdp_discover_params;
/** @typedef bt_sdp_discover_func_t
*
* @brief Callback type reporting to user that there is a resolved result
@ -514,24 +514,41 @@ enum {
*
* @param conn Connection object identifying connection to queried remote.
* @param result Object pointing to logical unparsed SDP record collected on
* base of response driven by given UUID.
* base of response driven by given discover params.
* @param params Discover parameters.
*
* @return BT_SDP_DISCOVER_UUID_STOP in case of no more need to read next
* record data and continue discovery for given UUID. By returning
* BT_SDP_DISCOVER_UUID_CONTINUE user allows this discovery continuation.
* @return BT_SDP_DISCOVER_UUID_CONTINUE user allows this discovery continuation.
*/
typedef uint8_t (*bt_sdp_discover_func_t)
(struct bt_conn *conn, struct bt_sdp_client_result *result);
typedef uint8_t (*bt_sdp_discover_func_t)(struct bt_conn *conn, struct bt_sdp_client_result *result,
const struct bt_sdp_discover_params *params);
/** SDP Discover types */
enum {
/** Discover Service Search. */
BT_SDP_DISCOVER_SERVICE_SEARCH,
/** Discover Service Attribute. */
BT_SDP_DISCOVER_SERVICE_ATTR,
/** Discover Service Search Attribute. */
BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR,
};
/** @brief Main user structure used in SDP discovery of remote. */
struct bt_sdp_discover_params {
sys_snode_t _node;
/** UUID (service) to be discovered on remote SDP entity */
const struct bt_uuid *uuid;
sys_snode_t _node;
union {
/** UUID (service) to be discovered on remote SDP entity */
const struct bt_uuid *uuid;
/** Service record handle */
uint32_t handle;
};
/** Discover callback to be called on resolved SDP record */
bt_sdp_discover_func_t func;
bt_sdp_discover_func_t func;
/** Memory buffer enabled by user for SDP query results */
struct net_buf_pool *pool;
struct net_buf_pool *pool;
/** Discover type */
uint8_t type;
};
/** @brief Allows user to start SDP discovery session.
@ -543,6 +560,21 @@ struct bt_sdp_discover_params {
* On the service discovery completion the callback function will be
* called to get feedback to user about findings.
*
* Service Search: The SDP Client generates an
* SDP_SERVICE_SEARCH_REQ to locate service
* records that match the service search
* pattern (`params->uuid`) given as the first
* parameter of the PDU.
* Service Attribute: The SDP Client generates an
* SDP_SERVICE_ATTR_REQ to retrieve specified
* attribute values from a specific service
* record (`params->handle`).
* Service Search Attribute: The SDP Client generates an
* SDP_SERVICE_SEARCH_ATTR_REQ to retrieve
* specified attribute values that match the
* service search pattern (`params->uuid`)
* given as the first parameter of the PDU.
*
* @param conn Object identifying connection to remote.
* @param params SDP discovery parameters.
*

View File

@ -58,6 +58,9 @@ LOG_MODULE_REGISTER(bt_sdp);
#define SDP_INVALID 0xff
/* SDP record handle size */
#define SDP_RECORD_HANDLE_SIZE 4
struct bt_sdp {
struct bt_l2cap_br_chan chan;
struct k_fifo partial_resp_queue;
@ -1476,29 +1479,115 @@ int bt_sdp_register_service(struct bt_sdp_record *service)
#define GET_PARAM(__node) \
CONTAINER_OF(__node, struct bt_sdp_discover_params, _node)
/* ServiceSearchAttribute PDU, ref to BT Core 4.2, Vol 3, part B, 4.7.1 */
static int sdp_client_ssa_search(struct bt_sdp_client *session)
/* ServiceSearch PDU, ref to BT Core 5.4, Vol 3, part B, 4.5.1 */
static int sdp_client_ss_search(struct bt_sdp_client *session,
const struct bt_sdp_discover_params *param)
{
const struct bt_sdp_discover_params *param;
struct net_buf *buf;
/*
* Select proper user params, if session->param is invalid it means
* getting new UUID from top of to be resolved params list. Otherwise
* the context is in a middle of partial SDP PDU responses and cached
* value from context can be used.
*/
if (!session->param) {
param = GET_PARAM(sys_slist_peek_head(&session->reqs));
} else {
param = session->param;
}
buf = bt_sdp_create_pdu();
if (!param) {
LOG_WRN("No UUIDs to be resolved on remote");
/* BT_SDP_SEQ8 means length of sequence is on additional next byte */
net_buf_add_u8(buf, BT_SDP_SEQ8);
switch (param->uuid->type) {
case BT_UUID_TYPE_16:
/* Seq length */
net_buf_add_u8(buf, 0x03);
/* Seq type */
net_buf_add_u8(buf, BT_SDP_UUID16);
/* Seq value */
net_buf_add_be16(buf, BT_UUID_16(param->uuid)->val);
break;
case BT_UUID_TYPE_32:
net_buf_add_u8(buf, 0x05);
net_buf_add_u8(buf, BT_SDP_UUID32);
net_buf_add_be32(buf, BT_UUID_32(param->uuid)->val);
break;
case BT_UUID_TYPE_128:
net_buf_add_u8(buf, 0x11);
net_buf_add_u8(buf, BT_SDP_UUID128);
net_buf_add_mem(buf, BT_UUID_128(param->uuid)->val,
ARRAY_SIZE(BT_UUID_128(param->uuid)->val));
break;
default:
LOG_ERR("Unknown UUID type %u", param->uuid->type);
return -EINVAL;
}
/* Set maximum number of service record handles */
net_buf_add_be16(buf, net_buf_tailroom(session->rec_buf) / SDP_RECORD_HANDLE_SIZE);
/*
* Update and validate PDU ContinuationState. Initial SSA Request has
* zero length continuation state since no interaction has place with
* server so far, otherwise use the original state taken from remote's
* last response PDU that is cached by SDP client context.
*/
if (session->cstate.length == 0U) {
net_buf_add_u8(buf, 0x00);
} else {
net_buf_add_u8(buf, session->cstate.length);
net_buf_add_mem(buf, session->cstate.data, session->cstate.length);
}
/* Update context param to the one being resolving now */
session->param = param;
session->tid++;
return bt_sdp_send(&session->chan.chan, buf, BT_SDP_SVC_SEARCH_REQ, session->tid);
}
/* ServiceAttribute PDU, ref to BT Core 5.4, Vol 3, part B, 4.6.1 */
static int sdp_client_sa_search(struct bt_sdp_client *session,
const struct bt_sdp_discover_params *param)
{
struct net_buf *buf;
buf = bt_sdp_create_pdu();
/* Add service record handle */
net_buf_add_be32(buf, param->handle);
/* Set attribute max bytes count to be returned from server */
net_buf_add_be16(buf, net_buf_tailroom(session->rec_buf));
/*
* Sequence definition where data is sequence of elements and where
* additional next byte points the size of elements within
*/
net_buf_add_u8(buf, BT_SDP_SEQ8);
net_buf_add_u8(buf, 0x05);
/* Data element definition for two following 16bits range elements */
net_buf_add_u8(buf, BT_SDP_UINT32);
/* Get all attributes. It enables filter out wanted only attributes */
net_buf_add_be16(buf, 0x0000);
net_buf_add_be16(buf, 0xffff);
/*
* Update and validate PDU ContinuationState. Initial SSA Request has
* zero length continuation state since no interaction has place with
* server so far, otherwise use the original state taken from remote's
* last response PDU that is cached by SDP client context.
*/
if (session->cstate.length == 0U) {
net_buf_add_u8(buf, 0x00);
} else {
net_buf_add_u8(buf, session->cstate.length);
net_buf_add_mem(buf, session->cstate.data, session->cstate.length);
}
/* Update context param to the one being resolving now */
session->param = param;
session->tid++;
return bt_sdp_send(&session->chan.chan, buf, BT_SDP_SVC_ATTR_REQ, session->tid);
}
/* ServiceSearchAttribute PDU, ref to BT Core 4.2, Vol 3, part B, 4.7.1 */
static int sdp_client_ssa_search(struct bt_sdp_client *session,
const struct bt_sdp_discover_params *param)
{
struct net_buf *buf;
buf = bt_sdp_create_pdu();
/* BT_SDP_SEQ8 means length of sequence is on additional next byte */
@ -1530,7 +1619,7 @@ static int sdp_client_ssa_search(struct bt_sdp_client *session)
}
/* Set attribute max bytes count to be returned from server */
net_buf_add_be16(buf, BT_SDP_MAX_ATTR_LEN);
net_buf_add_be16(buf, net_buf_tailroom(session->rec_buf));
/*
* Sequence definition where data is sequence of elements and where
* additional next byte points the size of elements within
@ -1565,6 +1654,56 @@ static int sdp_client_ssa_search(struct bt_sdp_client *session)
session->tid);
}
static void sdp_client_params_iterator(struct bt_sdp_client *session);
static int sdp_client_discover(struct bt_sdp_client *session)
{
const struct bt_sdp_discover_params *param;
int err;
/*
* Select proper user params, if session->param is invalid it means
* getting new UUID from top of to be resolved params list. Otherwise
* the context is in a middle of partial SDP PDU responses and cached
* value from context can be used.
*/
if (!session->param) {
param = GET_PARAM(sys_slist_peek_head(&session->reqs));
} else {
param = session->param;
}
if (!param) {
struct bt_l2cap_chan *chan = &session->chan.chan;
LOG_WRN("No more request, disconnect channel");
/* No UUID items, disconnect channel */
return bt_l2cap_chan_disconnect(chan);
}
switch (param->type) {
case BT_SDP_DISCOVER_SERVICE_SEARCH:
err = sdp_client_ss_search(session, param);
break;
case BT_SDP_DISCOVER_SERVICE_ATTR:
err = sdp_client_sa_search(session, param);
break;
case BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR:
err = sdp_client_ssa_search(session, param);
break;
default:
err = -EINVAL;
break;
}
if (err) {
/* Get next UUID and start resolving it */
sdp_client_params_iterator(session);
}
return 0;
}
static void sdp_client_params_iterator(struct bt_sdp_client *session)
{
struct bt_l2cap_chan *chan = &session->chan.chan;
@ -1586,7 +1725,7 @@ static void sdp_client_params_iterator(struct bt_sdp_client *session)
/* Check if there's valid next UUID */
if (!sys_slist_is_empty(&session->reqs)) {
sdp_client_ssa_search(session);
sdp_client_discover(session);
return;
}
@ -1640,7 +1779,17 @@ static uint16_t sdp_client_get_total(struct bt_sdp_client *session,
return pulled;
}
static uint16_t get_record_len(struct net_buf *buf)
static uint16_t get_ss_record_len(struct net_buf *buf)
{
if (buf->len >= SDP_RECORD_HANDLE_SIZE) {
return SDP_RECORD_HANDLE_SIZE;
}
LOG_WRN("Invalid service record handle length");
return 0;
}
static uint16_t get_ssa_sa_record_len(struct net_buf *buf)
{
uint16_t len;
uint8_t seq;
@ -1663,6 +1812,33 @@ static uint16_t get_record_len(struct net_buf *buf)
break;
}
return len;
}
static uint16_t get_record_len(struct bt_sdp_client *session)
{
struct net_buf *buf;
uint16_t len;
buf = session->rec_buf;
if (!session->param) {
return buf->len;
}
switch (session->param->type) {
case BT_SDP_DISCOVER_SERVICE_SEARCH:
len = get_ss_record_len(buf);
break;
case BT_SDP_DISCOVER_SERVICE_ATTR:
__fallthrough;
case BT_SDP_DISCOVER_SERVICE_SEARCH_ATTR:
len = get_ssa_sa_record_len(buf);
break;
default:
len = buf->len;
}
LOG_DBG("Record len %u", len);
return len;
@ -1681,19 +1857,17 @@ static void sdp_client_notify_result(struct bt_sdp_client *session,
uint16_t rec_len;
uint8_t user_ret;
result.uuid = session->param->uuid;
if (state == UUID_NOT_RESOLVED) {
result.resp_buf = NULL;
result.next_record_hint = false;
session->param->func(conn, &result);
session->param->func(conn, &result, session->param);
return;
}
while (session->rec_buf->len) {
struct net_buf_simple_state buf_state;
rec_len = get_record_len(session->rec_buf);
rec_len = get_record_len(session);
/* tell the user about multi record resolution */
if (session->rec_buf->len > rec_len) {
result.next_record_hint = true;
@ -1712,7 +1886,7 @@ static void sdp_client_notify_result(struct bt_sdp_client *session,
*/
result.resp_buf->len = rec_len;
user_ret = session->param->func(conn, &result);
user_ret = session->param->func(conn, &result, session->param);
/* restore original session buffer */
net_buf_simple_restore(&session->rec_buf->b, &buf_state);
@ -1727,13 +1901,194 @@ static void sdp_client_notify_result(struct bt_sdp_client *session,
}
}
static int sdp_client_receive_ss(struct bt_sdp_client *session, struct net_buf *buf)
{
struct bt_sdp_pdu_cstate *cstate;
uint16_t current_count;
uint16_t total_count;
uint32_t record_len;
uint32_t received_count;
/* Check the buffer len for the total_count field */
if (buf->len < sizeof(total_count)) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Get total service record count. */
total_count = net_buf_pull_be16(buf);
/* Check the buffer len for the current_count field */
if (buf->len < sizeof(current_count)) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Get current service record count. */
current_count = net_buf_pull_be16(buf);
/* Check valid of current service record count */
if (current_count > total_count) {
LOG_ERR("Invalid current service record count");
return 0;
}
received_count = session->rec_buf->len / SDP_RECORD_HANDLE_SIZE;
if ((received_count + current_count) > total_count) {
LOG_ERR("Excess data received");
return 0;
}
record_len = current_count * SDP_RECORD_HANDLE_SIZE;
if (record_len >= buf->len) {
LOG_ERR("Invalid packet");
return 0;
}
/* Get PDU continuation state */
cstate = (struct bt_sdp_pdu_cstate *)(buf->data + record_len);
if (cstate->length > BT_SDP_MAX_PDU_CSTATE_LEN) {
LOG_ERR("Invalid SDP PDU Continuation State length %u", cstate->length);
return 0;
}
if ((record_len + SDP_CONT_STATE_LEN_SIZE + cstate->length) > buf->len) {
LOG_ERR("Invalid payload length");
return 0;
}
/*
* No record found for given UUID. The check catches case when
* current response frame has Continuation State shortest and
* valid and this is the first response frame as well.
*/
if (!current_count && cstate->length == 0U && session->cstate.length == 0U) {
LOG_DBG("Service record handle 0x%x not found", session->param->handle);
/* Call user UUID handler */
sdp_client_notify_result(session, UUID_NOT_RESOLVED);
net_buf_pull(buf, sizeof(cstate->length));
goto iterate;
}
if (record_len > net_buf_tailroom(session->rec_buf)) {
LOG_WRN("Not enough room for getting records data");
goto iterate;
}
net_buf_add_mem(session->rec_buf, buf->data, record_len);
net_buf_pull(buf, record_len);
/* check if current response says there's next portion to be fetched */
if (cstate->length) {
/* Cache original Continuation State in context */
memcpy(&session->cstate, cstate, sizeof(struct bt_sdp_pdu_cstate));
net_buf_pull(buf, cstate->length + sizeof(cstate->length));
/* Request for next portion of attributes data */
return sdp_client_discover(session);
}
net_buf_pull(buf, sizeof(cstate->length));
LOG_DBG("UUID 0x%s resolved", bt_uuid_str(session->param->uuid));
sdp_client_notify_result(session, UUID_RESOLVED);
iterate:
/* Get next UUID and start resolving it */
sdp_client_params_iterator(session);
return 0;
}
static int sdp_client_receive_ssa_sa(struct bt_sdp_client *session, struct net_buf *buf)
{
struct bt_sdp_pdu_cstate *cstate;
uint16_t frame_len;
uint16_t total;
/* Check the buffer len for the frame_len field */
if (buf->len < sizeof(frame_len)) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Get number of attributes in this frame. */
frame_len = net_buf_pull_be16(buf);
/* Check valid buf len for attribute list and cont state */
if (buf->len < frame_len + SDP_CONT_STATE_LEN_SIZE) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Check valid range of attributes length */
if (frame_len < 2) {
LOG_ERR("Invalid attributes data length");
return 0;
}
/* Get PDU continuation state */
cstate = (struct bt_sdp_pdu_cstate *)(buf->data + frame_len);
if (cstate->length > BT_SDP_MAX_PDU_CSTATE_LEN) {
LOG_ERR("Invalid SDP PDU Continuation State length %u", cstate->length);
return 0;
}
if ((frame_len + SDP_CONT_STATE_LEN_SIZE + cstate->length) > buf->len) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/*
* No record found for given UUID. The check catches case when
* current response frame has Continuation State shortest and
* valid and this is the first response frame as well.
*/
if (frame_len == 2U && cstate->length == 0U && session->cstate.length == 0U) {
LOG_DBG("Record for UUID 0x%s not found", bt_uuid_str(session->param->uuid));
/* Call user UUID handler */
sdp_client_notify_result(session, UUID_NOT_RESOLVED);
net_buf_pull(buf, frame_len + sizeof(cstate->length));
goto iterate;
}
/* Get total value of all attributes to be collected */
frame_len -= sdp_client_get_total(session, buf, &total);
if (total > net_buf_tailroom(session->rec_buf)) {
LOG_WRN("Not enough room for getting records data");
goto iterate;
}
net_buf_add_mem(session->rec_buf, buf->data, frame_len);
net_buf_pull(buf, frame_len);
/* check if current response says there's next portion to be fetched */
if (cstate->length) {
/* Cache original Continuation State in context */
memcpy(&session->cstate, cstate, sizeof(struct bt_sdp_pdu_cstate));
net_buf_pull(buf, cstate->length + sizeof(cstate->length));
/* Request for next portion of attributes data */
return sdp_client_discover(session);
}
net_buf_pull(buf, sizeof(cstate->length));
LOG_DBG("UUID 0x%s resolved", bt_uuid_str(session->param->uuid));
sdp_client_notify_result(session, UUID_RESOLVED);
iterate:
/* Get next UUID and start resolving it */
sdp_client_params_iterator(session);
return 0;
}
static int sdp_client_receive(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan);
struct bt_sdp_hdr *hdr;
struct bt_sdp_pdu_cstate *cstate;
uint16_t len, tid, frame_len;
uint16_t total;
uint16_t len, tid;
LOG_DBG("session %p buf %p", session, buf);
@ -1764,88 +2119,13 @@ static int sdp_client_receive(struct bt_l2cap_chan *chan, struct net_buf *buf)
}
switch (hdr->op_code) {
case BT_SDP_SVC_SEARCH_RSP:
return sdp_client_receive_ss(session, buf);
case BT_SDP_SVC_ATTR_RSP:
__fallthrough;
case BT_SDP_SVC_SEARCH_ATTR_RSP:
/* Check the buffer len for the length field */
if (buf->len < sizeof(uint16_t)) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Get number of attributes in this frame. */
frame_len = net_buf_pull_be16(buf);
/* Check valid buf len for attribute list and cont state */
if (buf->len < frame_len + SDP_CONT_STATE_LEN_SIZE) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/* Check valid range of attributes length */
if (frame_len < 2) {
LOG_ERR("Invalid attributes data length");
return 0;
}
/* Get PDU continuation state */
cstate = (struct bt_sdp_pdu_cstate *)(buf->data + frame_len);
if (cstate->length > BT_SDP_MAX_PDU_CSTATE_LEN) {
LOG_ERR("Invalid SDP PDU Continuation State length %u", cstate->length);
return 0;
}
if ((frame_len + SDP_CONT_STATE_LEN_SIZE + cstate->length) >
buf->len) {
LOG_ERR("Invalid frame payload length");
return 0;
}
/*
* No record found for given UUID. The check catches case when
* current response frame has Continuation State shortest and
* valid and this is the first response frame as well.
*/
if (frame_len == 2U && cstate->length == 0U &&
session->cstate.length == 0U) {
LOG_DBG("record for UUID 0x%s not found",
bt_uuid_str(session->param->uuid));
/* Call user UUID handler */
sdp_client_notify_result(session, UUID_NOT_RESOLVED);
net_buf_pull(buf, frame_len + sizeof(cstate->length));
goto iterate;
}
/* Get total value of all attributes to be collected */
frame_len -= sdp_client_get_total(session, buf, &total);
if (total > net_buf_tailroom(session->rec_buf)) {
LOG_WRN("Not enough room for getting records data");
goto iterate;
}
net_buf_add_mem(session->rec_buf, buf->data, frame_len);
net_buf_pull(buf, frame_len);
/*
* check if current response says there's next portion to be
* fetched
*/
if (cstate->length) {
/* Cache original Continuation State in context */
memcpy(&session->cstate, cstate,
sizeof(struct bt_sdp_pdu_cstate));
net_buf_pull(buf, cstate->length +
sizeof(cstate->length));
/* Request for next portion of attributes data */
sdp_client_ssa_search(session);
break;
}
net_buf_pull(buf, sizeof(cstate->length));
LOG_DBG("UUID 0x%s resolved", bt_uuid_str(session->param->uuid));
sdp_client_notify_result(session, UUID_RESOLVED);
iterate:
/* Get next UUID and start resolving it */
return sdp_client_receive_ssa_sa(session, buf);
case BT_SDP_ERROR_RSP:
sdp_client_params_iterator(session);
break;
default:
@ -1858,8 +2138,7 @@ iterate:
static int sdp_client_chan_connect(struct bt_sdp_client *session)
{
return bt_l2cap_br_chan_connect(session->chan.chan.conn,
&session->chan.chan, SDP_PSM);
return bt_l2cap_br_chan_connect(session->chan.chan.conn, &session->chan.chan, SDP_PSM);
}
static struct net_buf *sdp_client_alloc_buf(struct bt_l2cap_chan *chan)
@ -1884,8 +2163,12 @@ static void sdp_client_connected(struct bt_l2cap_chan *chan)
LOG_DBG("session %p chan %p connected", session, chan);
session->rec_buf = chan->ops->alloc_buf(chan);
if (!session->rec_buf) {
bt_l2cap_chan_disconnect(chan);
return;
}
sdp_client_ssa_search(session);
sdp_client_discover(session);
}
static void sdp_client_disconnected(struct bt_l2cap_chan *chan)
@ -1894,7 +2177,9 @@ static void sdp_client_disconnected(struct bt_l2cap_chan *chan)
LOG_DBG("session %p chan %p disconnected", session, chan);
net_buf_unref(session->rec_buf);
if (session->rec_buf) {
net_buf_unref(session->rec_buf);
}
/*
* Reset session excluding L2CAP channel member. Let's the channel