mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
323 lines
8.6 KiB
C
323 lines
8.6 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/bluetooth/l2cap.h>
|
|
|
|
#include <../host/conn_internal.h>
|
|
|
|
#include "host/ble_hs.h"
|
|
#include "host/ble_l2cap.h"
|
|
#include "host/ble_hs_mbuf.h"
|
|
#include "../../../nimble/host/src/ble_l2cap_priv.h"
|
|
|
|
#include "common/host.h"
|
|
|
|
_Static_assert(CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM && "At least one L2CAP coc shall be supported");
|
|
|
|
#define OTS_L2CAP_BUF_COUNT (3 * CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM)
|
|
#define OTS_L2CAP_MEM_SIZE OS_MEMPOOL_SIZE(OTS_L2CAP_BUF_COUNT, L2CAP_LE_OTS_MTU * 2)
|
|
|
|
static os_membuf_t ots_mem[OTS_L2CAP_MEM_SIZE];
|
|
static struct os_mempool ots_mbuf_mempool;
|
|
static struct os_mbuf_pool ots_mbuf_pool;
|
|
|
|
static struct ble_l2cap_chan *ots_chan;
|
|
|
|
static int ots_l2cap_recv_ready(struct ble_l2cap_chan *chan)
|
|
{
|
|
struct os_mbuf *sdu_rx;
|
|
int rc;
|
|
|
|
LOG_DBG("[N]OtsRecvReady");
|
|
|
|
sdu_rx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
|
|
if (sdu_rx == NULL) {
|
|
LOG_ERR("[N]NoBufForL2capRecv");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rc = ble_l2cap_recv_ready(chan, sdu_rx);
|
|
if (rc) {
|
|
LOG_ERR("[N]L2capRecvFail[%d]", rc);
|
|
|
|
os_mbuf_free_chain(sdu_rx);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
|
|
{
|
|
struct ble_l2cap_chan_info chan_info;
|
|
uint16_t result = 0;
|
|
size_t sdu_len;
|
|
uint8_t *sdu;
|
|
int err;
|
|
|
|
LOG_DBG("[N]OtsEvtCb[%u]", event->type);
|
|
|
|
switch (event->type) {
|
|
case BLE_L2CAP_EVENT_COC_CONNECTED:
|
|
if (event->connect.status) {
|
|
LOG_ERR("[N]CocConnectFail[%d]", event->connect.status);
|
|
return 0;
|
|
}
|
|
|
|
if (ots_chan) {
|
|
LOG_ERR("[N]CocChanExist");
|
|
return 0;
|
|
}
|
|
|
|
ots_chan = event->connect.chan;
|
|
|
|
if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) {
|
|
assert(0);
|
|
}
|
|
|
|
LOG_DBG("[N]CocConnect[%u][%04x][%04x][%04x][%u][%u][%u][%u]",
|
|
event->connect.conn_handle, chan_info.scid, chan_info.dcid,
|
|
chan_info.psm, chan_info.our_l2cap_mtu, chan_info.peer_l2cap_mtu,
|
|
chan_info.our_coc_mtu, chan_info.peer_coc_mtu);
|
|
|
|
bt_le_l2cap_connected(event->connect.conn_handle, chan_info.psm,
|
|
chan_info.dcid, chan_info.peer_coc_mtu,
|
|
chan_info.scid, chan_info.our_coc_mtu);
|
|
return 0;
|
|
|
|
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
|
|
if (ots_chan != event->disconnect.chan) {
|
|
LOG_ERR("[N]DisconnectInvCocChan");
|
|
return 0;
|
|
}
|
|
|
|
LOG_DBG("[N]CocDisconnect[%u][%04x]",
|
|
event->disconnect.conn_handle, event->disconnect.chan->psm);
|
|
|
|
bt_le_l2cap_disconnected(event->disconnect.conn_handle, event->disconnect.chan->psm);
|
|
|
|
ots_chan = NULL;
|
|
return 0;
|
|
|
|
case BLE_L2CAP_EVENT_COC_ACCEPT:
|
|
if (event->accept.peer_sdu_size > L2CAP_LE_OTS_MTU) {
|
|
LOG_ERR("[N]InvAcceptMtu[%u][%u]",
|
|
event->accept.peer_sdu_size, L2CAP_LE_OTS_MTU);
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("[N]CocAccept[%u][%04x][%04x][%u][%u][%u][%u]",
|
|
event->accept.conn_handle, event->accept.chan->psm,
|
|
event->accept.chan->dcid, event->accept.chan->coc_tx.mtu,
|
|
event->accept.chan->peer_coc_mps, event->accept.peer_sdu_size,
|
|
event->accept.chan->coc_tx.credits);
|
|
|
|
err = bt_le_l2cap_accept(event->accept.conn_handle,
|
|
event->accept.chan->psm,
|
|
event->accept.chan->dcid,
|
|
event->accept.chan->coc_tx.mtu,
|
|
event->accept.chan->peer_coc_mps,
|
|
event->accept.chan->coc_tx.credits,
|
|
&result);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
ARG_UNUSED(result);
|
|
|
|
return ots_l2cap_recv_ready(event->accept.chan);
|
|
|
|
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
|
|
if (ots_chan != event->receive.chan) {
|
|
LOG_ERR("[N]RecvOnInvCocChan");
|
|
return 0;
|
|
}
|
|
|
|
assert(event->receive.sdu_rx);
|
|
|
|
LOG_DBG("[N]CocReceive[%u][%04x][%u]",
|
|
event->receive.conn_handle, event->receive.chan->psm,
|
|
event->receive.sdu_rx->om_len);
|
|
|
|
sdu_len = OS_MBUF_PKTLEN(event->receive.sdu_rx);
|
|
|
|
sdu = calloc(1, sdu_len);
|
|
assert(sdu);
|
|
|
|
err = os_mbuf_copydata(event->receive.sdu_rx, 0, sdu_len, sdu);
|
|
assert(err == 0);
|
|
|
|
bt_le_l2cap_received(event->receive.conn_handle,
|
|
event->receive.chan->psm,
|
|
sdu, sdu_len);
|
|
|
|
os_mbuf_free_chain(event->receive.sdu_rx);
|
|
free(sdu);
|
|
|
|
return ots_l2cap_recv_ready(event->receive.chan);
|
|
|
|
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
|
|
if (ots_chan != event->tx_unstalled.chan) {
|
|
LOG_ERR("[N]TxUnstalledOnInvCocChan");
|
|
return 0;
|
|
}
|
|
|
|
LOG_DBG("[N]CocTxUnstalled[%u][%d]",
|
|
event->tx_unstalled.conn_handle, event->tx_unstalled.status);
|
|
|
|
/* TODO: transmit the remaining data */
|
|
return 0;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int bt_le_nimble_l2cap_chan_connect(uint16_t conn_handle)
|
|
{
|
|
struct os_mbuf *sdu_rx;
|
|
int rc;
|
|
|
|
if (ots_chan) {
|
|
LOG_WRN("[N]OtsChanExist");
|
|
return -EALREADY;
|
|
}
|
|
|
|
sdu_rx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
|
|
if (sdu_rx == NULL) {
|
|
LOG_ERR("[N]NoBufForL2capConnect");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rc = ble_l2cap_connect(conn_handle, L2CAP_LE_OTS_PSM, L2CAP_LE_OTS_MTU,
|
|
sdu_rx, ots_l2cap_event_cb, NULL);
|
|
if (rc) {
|
|
LOG_ERR("[N]L2capConnectFail[%d]", rc);
|
|
|
|
os_mbuf_free_chain(sdu_rx);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_nimble_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
|
{
|
|
int rc;
|
|
|
|
if (ots_chan == NULL) {
|
|
LOG_WRN("[N]NoOtsChan");
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (ble_l2cap_get_conn_handle(ots_chan) != chan->conn->handle) {
|
|
LOG_ERR("[N]UnexpOtsChan[%u][%u]",
|
|
ble_l2cap_get_conn_handle(ots_chan), chan->conn->handle);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = ble_l2cap_disconnect(ots_chan);
|
|
if (rc) {
|
|
LOG_ERR("[N]L2capDisconnectFail[%d]", rc);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_nimble_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|
{
|
|
struct os_mbuf *sdu_tx;
|
|
int rc;
|
|
|
|
if (ots_chan == NULL) {
|
|
LOG_WRN("[N]NoOtsChan");
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
if (ble_l2cap_get_conn_handle(ots_chan) != chan->conn->handle) {
|
|
LOG_ERR("[N]UnexpOtsChan[%u][%u]",
|
|
ble_l2cap_get_conn_handle(ots_chan), chan->conn->handle);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sdu_tx = os_mbuf_get_pkthdr(&ots_mbuf_pool, 0);
|
|
if (sdu_tx == NULL) {
|
|
LOG_ERR("[N]NoBufForL2capSend");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rc = os_mbuf_append(sdu_tx, buf->data, buf->len);
|
|
if (rc) {
|
|
LOG_ERR("[N]AppendBufFail[%d]", rc);
|
|
os_mbuf_free_chain(sdu_tx);
|
|
return -EIO;
|
|
}
|
|
|
|
rc = ble_l2cap_send(ots_chan, sdu_tx);
|
|
if (rc) {
|
|
if (rc == BLE_HS_ESTALLED) {
|
|
/* TODO:
|
|
* Wait for the BLE_L2CAP_EVENT_COC_TX_UNSTALLED event and continue.
|
|
*/
|
|
LOG_WRN("[N]MoreCreditsForL2capSend");
|
|
} else {
|
|
LOG_ERR("[N]L2capSendFail[%d]", rc);
|
|
}
|
|
|
|
os_mbuf_free_chain(sdu_tx);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_le_nimble_l2cap_init(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = os_mempool_init(&ots_mbuf_mempool, OTS_L2CAP_BUF_COUNT, L2CAP_LE_OTS_MTU * 2, ots_mem, "ots_pool");
|
|
if (rc) {
|
|
LOG_ERR("[N]InitOtsMempoolFail[%d]", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = os_mbuf_pool_init(&ots_mbuf_pool, &ots_mbuf_mempool, L2CAP_LE_OTS_MTU, OTS_L2CAP_BUF_COUNT);
|
|
if (rc) {
|
|
LOG_ERR("[N]InitOtsMbufPoolFail[%d]", rc);
|
|
return rc;
|
|
}
|
|
|
|
#if CONFIG_BT_OTS
|
|
rc = ble_l2cap_create_server(L2CAP_LE_OTS_PSM, L2CAP_LE_OTS_MTU, ots_l2cap_event_cb, NULL);
|
|
if (rc) {
|
|
LOG_ERR("[N]CreateL2capSrvFail[%d]", rc);
|
|
return rc;
|
|
}
|
|
#endif /* CONFIG_BT_OTS */
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_le_nimble_l2cap_deinit(void)
|
|
{
|
|
LOG_DBG("NimbleL2capDeinit");
|
|
|
|
/* TODO: free the ots_mbuf_pool and ots_mbuf_mempool */
|
|
|
|
#if CONFIG_BT_OTS
|
|
/* TODO: destroy the server */
|
|
#endif /* CONFIG_BT_OTS */
|
|
}
|