Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 50 additions & 23 deletions doc/connectivity/bluetooth/api/l2cap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,53 @@ configuration option: :kconfig:option:`CONFIG_BT_L2CAP_DYNAMIC_CHANNEL`. These c
support segmentation and reassembly transparently, they also support credit
based flow control making it suitable for data streams.

Channels instances are represented by the :c:struct:`bt_l2cap_chan` struct which
contains the callbacks in the :c:struct:`bt_l2cap_chan_ops` struct to inform
when the channel has been connected, disconnected or when the encryption has
changed.
In addition to that it also contains the ``recv`` callback which is called
whenever an incoming data has been received. Data received this way can be
marked as processed by returning 0 or using
:c:func:`bt_l2cap_chan_recv_complete` API if processing is asynchronous.

.. note::
The ``recv`` callback is called directly from RX Thread thus it is not
recommended to block for long periods of time.

For sending data the :c:func:`bt_l2cap_chan_send` API can be used noting that
it may block if no credits are available, and resuming as soon as more credits
are available.

Servers can be registered using :c:func:`bt_l2cap_server_register` API passing
the :c:struct:`bt_l2cap_server` struct which informs what ``psm`` it should
listen to, the required security level ``sec_level``, and the callback
``accept`` which is called to authorize incoming connection requests and
allocate channel instances.

Creating a simple L2CAP server
-------------------------------

A complete working example demonstrating L2CAP dynamic channels is available
in the samples directory:

- Acceptor (server) sample: :zephyr:code-sample:`bluetooth_l2cap_coc_acceptor`
- Initiator (client) sample: :zephyr:code-sample:`bluetooth_l2cap_coc_initiator`

The acceptor sample shows how to register an L2CAP server and handle incoming
connections:

.. literalinclude:: ../../../../samples/bluetooth/l2cap_coc_acceptor/src/main.c
:language: c
:linenos:

Comment on lines +46 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should point to exact lines not to the entire file. What should be demonstrated (based on your previous change) are line 17-48 from l2cap_coc_acceptor/src/main.c.

I also suggest to move the literalinclude up to line 34 (right after paragraph that starts at line 28) and remove the existing lines 34-35 (Creating a simple L2CAP server...).

Then paragraph at line 37 that refers to samples passes perfectly to the entire section. But! I suggest to keep only reference to acceptor (server) role there and move the initiator (client) reference to the end of Client Channels section that you have added (after line 97 at the current change).

.. note::
The sample demonstrates allocating one channel per connection using
``CONFIG_BT_MAX_CONN`` and ``bt_conn_index(conn)``. See the initiator sample
for how to open a channel and send data to the acceptor.
Comment on lines +50 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this note doesn't belong to l2cap.rst but to README of the particular sample.


Fixed Channels
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good change, thanks!

--------------

The user can also define fixed channels using the :c:macro:`BT_L2CAP_FIXED_CHANNEL_DEFINE`
macro. Fixed channels are initialized upon connection, and do not support segmentation. An example
of how to define a fixed channel is shown below.
Expand Down Expand Up @@ -42,32 +89,12 @@ of how to define a fixed channel is shown below.
.accept = l2cap_fixed_accept,
};

Channels instances are represented by the :c:struct:`bt_l2cap_chan` struct which
contains the callbacks in the :c:struct:`bt_l2cap_chan_ops` struct to inform
when the channel has been connected, disconnected or when the encryption has
changed.
In addition to that it also contains the ``recv`` callback which is called
whenever an incoming data has been received. Data received this way can be
marked as processed by returning 0 or using
:c:func:`bt_l2cap_chan_recv_complete` API if processing is asynchronous.

.. note::
The ``recv`` callback is called directly from RX Thread thus it is not
recommended to block for long periods of time.

For sending data the :c:func:`bt_l2cap_chan_send` API can be used noting that
it may block if no credits are available, and resuming as soon as more credits
are available.

Servers can be registered using :c:func:`bt_l2cap_server_register` API passing
the :c:struct:`bt_l2cap_server` struct which informs what ``psm`` it should
listen to, the required security level ``sec_level``, and the callback
``accept`` which is called to authorize incoming connection requests and
allocate channel instances.
Client Channels
---------------

Client channels can be initiated with use of :c:func:`bt_l2cap_chan_connect`
API and can be disconnected with the :c:func:`bt_l2cap_chan_disconnect` API.
Note that the later can also disconnect channel instances created by servers.
Note that the latter can also disconnect channel instances created by servers.

API Reference
*************
Expand Down
7 changes: 7 additions & 0 deletions samples/bluetooth/l2cap_coc_acceptor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(l2cap_server_simple)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated


target_sources(app PRIVATE src/main.c)
89 changes: 89 additions & 0 deletions samples/bluetooth/l2cap_coc_acceptor/README.rst
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not addressed - please use the template

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.. _bluetooth_l2cap_coc_acceptor:

L2CAP Connection Oriented Channels (Acceptor)
##############################################

Overview
********

This sample demonstrates how to create an L2CAP Connection Oriented Channel
(CoC) server that listens for incoming L2CAP connections on a fixed PSM
(Protocol/Service Multiplexer). When a client connects, the server accepts
the connection and receives data sent over the L2CAP channel.

The sample uses a fixed allocation strategy where one L2CAP channel instance is
pre-allocated for each possible Bluetooth connection (based on
``CONFIG_BT_MAX_CONN``). This approach avoids dynamic memory allocation and
simplifies resource management.

Requirements
************

* A board with Bluetooth Low Energy (BLE) support
* Bluetooth controller with L2CAP Connection Oriented Channels support

Building and Running
********************

This sample can be built and run on any board with Bluetooth LE support.

To build the sample for the nRF52840 DK:

.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/l2cap_coc_acceptor
:board: nrf52840dk/nrf52840
:goals: build flash
:compact:

For testing with native_posix (simulation):

.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/l2cap_coc_acceptor
:board: native_posix
:goals: build
:compact:

After flashing, the device will register an L2CAP server on PSM 0x29 and wait
for incoming connections. Pair this sample with the
:zephyr:code-sample:`bluetooth_l2cap_coc_initiator` sample to establish a
connection and exchange data.

Sample Output
=============

Upon successful initialization:

.. code-block:: console

L2CAP server registered, PSM 41

When a client connects:

.. code-block:: console

L2CAP channel accepted, assigned chan[0]
L2CAP channel connected

When data is received:

.. code-block:: console

L2CAP channel received 17 bytes

Testing
*******

This sample is designed to work with the
:zephyr:code-sample:`bluetooth_l2cap_coc_initiator` sample. Run the acceptor
sample on one device and the initiator sample on another device within BLE range.

The initiator will scan, connect, and establish an L2CAP channel to send periodic
messages to the acceptor.

References
**********

* :ref:`bluetooth_api`
* :ref:`bluetooth-samples`
* `Bluetooth Core Specification v5.4, Vol 3, Part A (L2CAP)
<https://www.bluetooth.com/specifications/specs/core-specification-5-4/>`_
6 changes: 6 additions & 0 deletions samples/bluetooth/l2cap_coc_acceptor/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
# Optional, adjust for sample:
CONFIG_BT_MAX_CONN=2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as here: #96896 (comment)

11 changes: 11 additions & 0 deletions samples/bluetooth/l2cap_coc_acceptor/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
sample:
name: Bluetooth L2CAP dynamic channels sample (acceptor role)
description: Sample demonstrating L2CAP dynamic channels (acceptor role).
tests:
sample.bluetooth.l2cap_coc_acceptor:
harness: bluetooth
platform_allow:
- qemu_x86
integration_platforms:
- qemu_x86
tags: bluetooth

Check warning on line 11 in samples/bluetooth/l2cap_coc_acceptor/sample.yaml

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

YAMLLint (new-line-at-end-of-file)

samples/bluetooth/l2cap_coc_acceptor/sample.yaml:11 no new line character at the end of file
83 changes: 83 additions & 0 deletions samples/bluetooth/l2cap_coc_acceptor/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright The Zephyr Project Contributors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.zephyrproject.org/latest/contribute/guidelines.html#copyright-and-license-notices
Please fix everywhere

Suggested change
* Copyright The Zephyr Project Contributors
* SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors

*/

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/sys/printk.h>

#define PSM 0x29 /* example PSM */

static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf);
static void l2cap_connected(struct bt_l2cap_chan *chan);
static void l2cap_disconnected(struct bt_l2cap_chan *chan);

/* ops used for every channel instance */
static struct bt_l2cap_chan_ops l2cap_ops = {
.recv = l2cap_recv,
.connected = l2cap_connected,
.disconnected = l2cap_disconnected,
};

/* Fixed array of channel instances, one per possible connection */
static struct bt_l2cap_chan fixed_chan[CONFIG_BT_MAX_CONN];

static int accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
struct bt_l2cap_chan **chan)
{
uint8_t conn_index = bt_conn_index(conn);

/* initialize the chosen entry */
fixed_chan[conn_index] = (struct bt_l2cap_chan){
.ops = &l2cap_ops,
};

*chan = &fixed_chan[conn_index];

printk("L2CAP channel accepted, assigned chan[%d]\n", conn_index);

return 0; /* accept */
}

static struct bt_l2cap_server server = {
.psm = PSM,
.sec_level = BT_SECURITY_L1,
.accept = accept_cb,
};

static void l2cap_connected(struct bt_l2cap_chan *chan)
{
printk("L2CAP channel connected\n");
}

static void l2cap_disconnected(struct bt_l2cap_chan *chan)
{
printk("L2CAP channel disconnected\n");
}

static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
printk("L2CAP channel received %u bytes\n", buf->len);
/* For synchronous processing, return 0 and the stack frees buf. */
return 0;
}

int main(void)
{
int err = bt_enable(NULL);
if (err) {

Check warning on line 70 in samples/bluetooth/l2cap_coc_acceptor/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LINE_SPACING

samples/bluetooth/l2cap_coc_acceptor/src/main.c:70 Missing a blank line after declarations
printk("Bluetooth init failed: %d\n", err);
return err;
}

err = bt_l2cap_server_register(&server);
if (err) {
printk("L2CAP server registration failed: %d\n", err);
return err;
}

Check failure on line 80 in samples/bluetooth/l2cap_coc_acceptor/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TRAILING_WHITESPACE

samples/bluetooth/l2cap_coc_acceptor/src/main.c:80 trailing whitespace
printk("L2CAP server registered, PSM %u\n", PSM);
return 0;
}

Check warning on line 83 in samples/bluetooth/l2cap_coc_acceptor/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

MISSING_EOF_NEWLINE

samples/bluetooth/l2cap_coc_acceptor/src/main.c:83 adding a line without newline at end of file
7 changes: 7 additions & 0 deletions samples/bluetooth/l2cap_coc_initiator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(l2cap_client_simple)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated


target_sources(app PRIVATE src/main.c)
98 changes: 98 additions & 0 deletions samples/bluetooth/l2cap_coc_initiator/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
.. _bluetooth_l2cap_coc_initiator:

L2CAP Connection Oriented Channels (Initiator)
###############################################

Overview
********

This sample demonstrates how to create an L2CAP Connection Oriented Channel
(CoC) client that scans for Bluetooth devices, connects to one, and establishes
an L2CAP channel to communicate with a server.

The sample periodically sends short text messages over the L2CAP channel to
demonstrate basic data transmission. It uses a fixed allocation strategy for
the L2CAP channel to avoid dynamic memory allocation.

Requirements
************

* A board with Bluetooth Low Energy (BLE) support
* Bluetooth controller with L2CAP Connection Oriented Channels support
* A peer device running an L2CAP server (such as the
:zephyr:code-sample:`bluetooth_l2cap_coc_acceptor` sample)

Building and Running
********************

This sample can be built and run on any board with Bluetooth LE support.

To build the sample for the nRF52840 DK:

.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/l2cap_coc_initiator
:board: nrf52840dk/nrf52840
:goals: build flash
:compact:

For testing with native_posix (simulation):

.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/l2cap_coc_initiator
:board: native_posix
:goals: build
:compact:

After flashing, the device will scan for nearby Bluetooth devices, connect to
the first one it finds, and attempt to establish an L2CAP channel on PSM 0x29.
Once connected, it will send messages periodically.

Sample Output
=============

Upon successful connection and channel establishment:

.. code-block:: console

Scanning...
Found device: XX:XX:XX:XX:XX:XX (RSSI -45)
Connecting to XX:XX:XX:XX:XX:XX ...
Connected
L2CAP channel connection initiated
L2CAP channel connected
Sent: Hello from client

The message "Hello from client" will be sent periodically to demonstrate
ongoing data transmission.

Testing
*******

This sample is designed to work with the
:zephyr:code-sample:`bluetooth_l2cap_coc_acceptor` sample:

1. Flash the acceptor sample on one device
2. Flash the initiator sample on another device
3. Place both devices within BLE range
4. The initiator will automatically scan, connect, and establish the L2CAP channel
5. Monitor the serial output on both devices to observe the connection and data
exchange

Notes
*****

* This sample is intentionally simple and is designed for testing and
demonstration purposes
* The initiator connects to the first advertising device it finds - in a
production environment, you would want to filter devices by name, address, or
advertised services
* You may need to adjust Bluetooth settings in the ``prj.conf`` file to match
your platform's capabilities

References
**********

* :ref:`bluetooth_api`
* :ref:`bluetooth-samples`
* `Bluetooth Core Specification v5.4, Vol 3, Part A (L2CAP)
<https://www.bluetooth.com/specifications/specs/core-specification-5-4/>`_
Loading
Loading