Bluetooth: CCP: Introduce new CCP API

The CCP API for the Call Control Profile works on top of the
TBS API, and will eventually replace parts of the TBS API.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-08-15 15:10:58 +02:00 committed by Benjamin Cabé
parent 05c167e0b8
commit e45830893f
48 changed files with 1758 additions and 230 deletions

View file

@ -401,6 +401,7 @@ Bluetooth Host:
- doc/connectivity/bluetooth/shell/audio/ - doc/connectivity/bluetooth/shell/audio/
- samples/bluetooth/bap*/ - samples/bluetooth/bap*/
- samples/bluetooth/cap*/ - samples/bluetooth/cap*/
- samples/bluetooth/ccp*/
- samples/bluetooth/hap*/ - samples/bluetooth/hap*/
- samples/bluetooth/hci_*/ - samples/bluetooth/hci_*/
- samples/bluetooth/pbp*/ - samples/bluetooth/pbp*/
@ -484,6 +485,7 @@ Bluetooth Audio:
- doc/connectivity/bluetooth/shell/audio/ - doc/connectivity/bluetooth/shell/audio/
- samples/bluetooth/bap*/ - samples/bluetooth/bap*/
- samples/bluetooth/cap*/ - samples/bluetooth/cap*/
- samples/bluetooth/ccp*/
- samples/bluetooth/hap*/ - samples/bluetooth/hap*/
- samples/bluetooth/pbp*/ - samples/bluetooth/pbp*/
- samples/bluetooth/tmap*/ - samples/bluetooth/tmap*/

View file

@ -294,6 +294,7 @@ GAF and the top layer profiles gave been implemented in Zephyr with the followin
cluster=true; cluster=true;
label="CCP"; label="CCP";
style=solid; style=solid;
CCP_H [label="ccp.h"];
TBS_H [label="tbs.h"]; TBS_H [label="tbs.h"];
} }
} }
@ -331,6 +332,7 @@ GAF and the top layer profiles gave been implemented in Zephyr with the followin
CAP_H -> MCS_H; CAP_H -> MCS_H;
CAP_H -> MCC_H; CAP_H -> MCC_H;
CAP_H -> MP_H; CAP_H -> MP_H;
CAP_H -> CCP_H;
CAP_H -> TBS_H; CAP_H -> TBS_H;
CAP_H -> BAP_H; CAP_H -> BAP_H;
CAP_H -> BAP_PRESET_H; CAP_H -> BAP_PRESET_H;
@ -341,6 +343,7 @@ GAF and the top layer profiles gave been implemented in Zephyr with the followin
CSIP_H -> MCS_H; CSIP_H -> MCS_H;
CSIP_H -> MCC_H; CSIP_H -> MCC_H;
CSIP_H -> MP_H; CSIP_H -> MP_H;
CSIP_H -> CCP_H;
CSIP_H -> TBS_H; CSIP_H -> TBS_H;
CSIP_H -> BAP_H; CSIP_H -> BAP_H;
CSIP_H -> BAP_PRESET_H; CSIP_H -> BAP_PRESET_H;
@ -719,8 +722,8 @@ Bluetooth Audio Stack.
| | | | | - Shell Module | | | | | | | - Shell Module | |
| | | | | - BSIM test | | | | | | | - BSIM test | |
+--------+-------------------------------+---------+------------------+-----------------------+--------------------------------------------------+ +--------+-------------------------------+---------+------------------+-----------------------+--------------------------------------------------+
| CCP | Call Control Server | 1.0 | 3.0 | - Feature complete | - API refactor | | CCP | Call Control Server | 1.0 | 3.0 | - Feature complete | - API refactor (in progress) |
| | | | | - Shell Module | - Sample Application | | | | | | - Shell Module | - Sample Application (in progress) |
| | | | | - BSIM test | | | | | | | - BSIM test | |
| +-------------------------------+---------+------------------+-----------------------+--------------------------------------------------+ | +-------------------------------+---------+------------------+-----------------------+--------------------------------------------------+
| | Call Control Client | 1.0 | 3.0 | - Feature complete | - API refactor | | | Call Control Client | 1.0 | 3.0 | - Feature complete | - API refactor |

View file

@ -19,6 +19,7 @@ For specific Bluetooth functionality see also the following shell documentation
shell/audio/csip.rst shell/audio/csip.rst
shell/audio/gmap.rst shell/audio/gmap.rst
shell/audio/mcp.rst shell/audio/mcp.rst
shell/audio/tbs.rst
shell/audio/tmap.rst shell/audio/tmap.rst
shell/audio/pbp.rst shell/audio/pbp.rst
shell/classic/a2dp.rst shell/classic/a2dp.rst

View file

@ -1,208 +1,24 @@
Bluetooth: Call Control Profile Shell Bluetooth: Call Control Profile Shell
##################################### #####################################
This document describes how to run the call control functionality, both as Call Control Server
a client and as a (telephone bearer service (TBS)) server. Note that in the *******************
examples below, some lines of debug have been removed to make this shorter The Call Control Server is a role that typically resides on devices that can make calls,
and provide a better overview. including calls from apps such as Skype, e.g. (smart)phones and PCs,
which are typically GAP Central devices.
Telephone Bearer Service Client Using the Call Control Server
******************************* =============================
The Server can be controlled locally, or by a remote device (when in a call). For
The telephone bearer service client will typically exist on a resource example a remote device may initiate a call to the server,
restricted device, such as headphones, but may also exist on e.g. phones or or the Server may initiate a call to remote device, without a client.
laptops. The call control client will also thus typically be the advertiser.
The client can control the states of calls on a server using the call control
point.
It is necessary to have :kconfig:option:`CONFIG_BT_TBS_CLIENT_LOG_LEVEL_DBG`
enabled for using the client interactively.
Using the telephone bearer service client
=========================================
When the Bluetooth stack has been initialized (:code:`bt init`),
and a device has been connected, the telephone bearer service client can
discover TBS on the connected device calling :code:`tbs_client discover`, which
will start a discovery for the TBS UUIDs and store the handles, and optionally
subscribe to all notifications (default is to subscribe to all).
Since a server may have multiple TBS instances, most of the tbs_client commands
will take an index (starting from 0) as input. Joining calls require at least 2
call IDs, and all call indexes shall be on the same TBS instance.
A server will also have a GTBS instance, which is an abstraction layer for all
the telephone bearers on the server. If the server has both GTBS and TBS,
the client may subscribe and use either when sending requests if
:code:`BT_TBS_CLIENT_GTBS` is enabled.
.. code-block:: console .. code-block:: console
tbs_client --help ccp_call_control_server --help
tbs_client - Bluetooth TBS_CLIENT shell commands ccp_call_control_server - Bluetooth CCP Call Control Server shell commands
Subcommands: Subcommands:
discover :Discover TBS [subscribe] init : Initialize CCP Call Control Server
set_signal_reporting_interval :Set the signal reporting interval
[<{instance_index, gtbs}>] <interval>
originate :Originate a call [<{instance_index, gtbs}>]
<uri>
terminate :terminate a call [<{instance_index, gtbs}>]
<id>
accept :Accept a call [<{instance_index, gtbs}>] <id>
hold :Place a call on hold [<{instance_index,
gtbs}>] <id>
retrieve :Retrieve a held call [<{instance_index,
gtbs}>] <id>
read_provider_name :Read the bearer name [<{instance_index,
gtbs}>]
read_bearer_uci :Read the bearer UCI [<{instance_index, gtbs}>]
read_technology :Read the bearer technology [<{instance_index,
gtbs}>]
read_uri_list :Read the bearer's supported URI list
[<{instance_index, gtbs}>]
read_signal_strength :Read the bearer signal strength
[<{instance_index, gtbs}>]
read_signal_interval :Read the bearer signal strength reporting
interval [<{instance_index, gtbs}>]
read_current_calls :Read the current calls [<{instance_index,
gtbs}>]
read_ccid :Read the CCID [<{instance_index, gtbs}>]
read_status_flags :Read the in feature and status value
[<{instance_index, gtbs}>]
read_uri :Read the incoming call target URI
[<{instance_index, gtbs}>]
read_call_state :Read the call state [<{instance_index, gtbs}>]
read_remote_uri :Read the incoming remote URI
[<{instance_index, gtbs}>]
read_friendly_name :Read the friendly name of an incoming call
[<{instance_index, gtbs}>]
read_optional_opcodes :Read the optional opcodes [<{instance_index,
gtbs}>]
In the following examples, notifications from GTBS is ignored, unless otherwise
specified.
Example usage
=============
Setup
-----
.. code-block:: console
uart:~$ bt init
uart:~$ bt advertise on
Advertising started
When connected
--------------
Placing a call:
.. code-block:: console
uart:~$ tbs_client discover
<dbg> bt_tbs_client.primary_discover_func: Discover complete, found 1 instances (GTBS found)
<dbg> bt_tbs_client.discover_func: Setup complete for 1 / 1 TBS
<dbg> bt_tbs_client.discover_func: Setup complete GTBS
uart:~$ tbs_client originate 0 tel:123
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the dialing state with URI tel:123
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the originate opcode for call 0x00
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the alerting state with URI tel:123
<call answered by peer device, and status notified by TBS server>
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the active state with URI tel:123
Placing a call on GTBS:
.. code-block:: console
uart:~$ tbs_client originate 0 tel:123
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the dialing state with URI tel:123
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the originate opcode for call 0x00
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the alerting state with URI tel:123
<call answered by peer device, and status notified by TBS server>
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the active state with URI tel:123
It is necessary to set an outgoing caller ID before placing a call.
Accepting incoming call from peer device:
.. code-block:: console
<dbg> bt_tbs_client.incoming_uri_notify_handler: tel:123
<dbg> bt_tbs_client.in_call_notify_handler: tel:456
<dbg> bt_tbs_client.friendly_name_notify_handler: Peter
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x05 is in the incoming state with URI tel:456
uart:~$ tbs_client accept 0 5
<dbg> bt_tbs_client.call_cp_callback_handler: Status: success for the accept opcode for call 0x05
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x05 is in the active state with URI tel
Terminate call:
.. code-block:: console
uart:~$ tbs_client terminate 0 5
<dbg> bt_tbs_client.termination_reason_notify_handler: ID 0x05, reason 0x06
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the terminate opcode for call 0x05
<dbg> bt_tbs_client.current_calls_notify_handler:
Telephone Bearer Service (TBS)
******************************
The telephone bearer service is a service that typically resides on devices that
can make calls, including calls from apps such as Skype, e.g. (smart)phones and
PCs.
It is necessary to have :kconfig:option:`CONFIG_BT_TBS_LOG_LEVEL_DBG` enabled
for using the TBS server interactively.
Using the telephone bearer service
==================================
TBS can be controlled locally, or by a remote device (when in a call). For
example a remote device may initiate a call to the device with the TBS server,
or the TBS server may initiate a call to remote device, without a TBS_CLIENT client.
The TBS implementation is capable of fully controlling any call.
Omitting an index for commands where a :code:`<instance_index>` can be supplied, defaults to the
GTBS bearer.
.. code-block:: console
tbs --help
tbs - Bluetooth TBS shell commands
Subcommands:
init :Initialize TBS
authorize :Authorize the current connection
accept :Accept call <call_index>
terminate :Terminate call <call_index>
hold :Hold call <call_index>
retrieve :Retrieve call <call_index>
originate :Originate call [<instance_index>] <uri>
join :Join calls <id> <id> [<id> [<id> [...]]]
incoming :Simulate incoming remote call [<{instance_index,
gtbs}>] <local_uri> <remote_uri>
<remote_friendly_name>
remote_answer :Simulate remote answer outgoing call <call_index>
remote_retrieve :Simulate remote retrieve <call_index>
remote_terminate :Simulate remote terminate <call_index>
remote_hold :Simulate remote hold <call_index>
set_bearer_provider_name :Set the bearer provider name [<{instance_index,
gtbs}>] <name>
set_bearer_technology :Set the bearer technology [<{instance_index,
gtbs}>] <technology>
set_bearer_signal_strength :Set the bearer signal strength [<{instance_index,
gtbs}>] <strength>
set_status_flags :Set the bearer feature and status value
[<{instance_index, gtbs}>] <feature_and_status>
set_uri_scheme :Set the URI prefix list <bearer_idx> <uri1 [uri2
[uri3 [...]]]>
print_calls :Output all calls in the debug log
Example Usage Example Usage
============= =============
@ -213,26 +29,7 @@ Setup
.. code-block:: console .. code-block:: console
uart:~$ bt init uart:~$ bt init
uart:~$ ccp_call_control_server init
Registered GTBS bearer
Registered bearer[1]
uart:~$ bt connect xx:xx:xx:xx:xx:xx public uart:~$ bt connect xx:xx:xx:xx:xx:xx public
When connected
--------------
Answering a call for a peer device originated by a client:
.. code-block:: console
<dbg> bt_tbs.write_call_cp: Index 0: Processing the originate opcode
<dbg> bt_tbs.originate_call: New call with call index 1
<dbg> bt_tbs.write_call_cp: Index 0: Processed the originate opcode with status success for call index 1
uart:~$ tbs remote_answer 1
TBS succeeded for call_id: 1
Incoming call from a peer device, accepted by client:
.. code-block:: console
uart:~$ tbs incoming 0 tel:123 tel:456 Peter
TBS succeeded for call_id: 4
<dbg> bt_tbs.bt_tbs_remote_incoming: New call with call index 4
<dbg> bt_tbs.write_call_cp: Index 0: Processed the accept opcode with status success for call index 4

View file

@ -0,0 +1,237 @@
Bluetooth: Telephone Bearer Service Shell
#########################################
This document describes how to run the call control functionality, both as
a client and as a (telephone bearer service (TBS)) server. Note that in the
examples below, some lines of debug have been removed to make this shorter
and provide a better overview.
Telephone Bearer Service Client
*******************************
The telephone bearer service client will typically exist on a resource
restricted device, such as headphones, but may also exist on e.g. phones or
laptops. The call control client will also thus typically be the advertiser.
The client can control the states of calls on a server using the call control
point.
It is necessary to have :kconfig:option:`CONFIG_BT_TBS_CLIENT_LOG_LEVEL_DBG`
enabled for using the client interactively.
Using the telephone bearer service client
=========================================
When the Bluetooth stack has been initialized (:code:`bt init`),
and a device has been connected, the telephone bearer service client can
discover TBS on the connected device calling :code:`tbs_client discover`, which
will start a discovery for the TBS UUIDs and store the handles, and optionally
subscribe to all notifications (default is to subscribe to all).
Since a server may have multiple TBS instances, most of the tbs_client commands
will take an index (starting from 0) as input. Joining calls require at least 2
call IDs, and all call indexes shall be on the same TBS instance.
A server will also have a GTBS instance, which is an abstraction layer for all
the telephone bearers on the server. If the server has both GTBS and TBS,
the client may subscribe and use either when sending requests if
:code:`BT_TBS_CLIENT_GTBS` is enabled.
.. code-block:: console
tbs_client --help
tbs_client - Bluetooth TBS_CLIENT shell commands
Subcommands:
discover :Discover TBS [subscribe]
set_signal_reporting_interval :Set the signal reporting interval
[<{instance_index, gtbs}>] <interval>
originate :Originate a call [<{instance_index, gtbs}>]
<uri>
terminate :terminate a call [<{instance_index, gtbs}>]
<id>
accept :Accept a call [<{instance_index, gtbs}>] <id>
hold :Place a call on hold [<{instance_index,
gtbs}>] <id>
retrieve :Retrieve a held call [<{instance_index,
gtbs}>] <id>
read_provider_name :Read the bearer name [<{instance_index,
gtbs}>]
read_bearer_uci :Read the bearer UCI [<{instance_index, gtbs}>]
read_technology :Read the bearer technology [<{instance_index,
gtbs}>]
read_uri_list :Read the bearer's supported URI list
[<{instance_index, gtbs}>]
read_signal_strength :Read the bearer signal strength
[<{instance_index, gtbs}>]
read_signal_interval :Read the bearer signal strength reporting
interval [<{instance_index, gtbs}>]
read_current_calls :Read the current calls [<{instance_index,
gtbs}>]
read_ccid :Read the CCID [<{instance_index, gtbs}>]
read_status_flags :Read the in feature and status value
[<{instance_index, gtbs}>]
read_uri :Read the incoming call target URI
[<{instance_index, gtbs}>]
read_call_state :Read the call state [<{instance_index, gtbs}>]
read_remote_uri :Read the incoming remote URI
[<{instance_index, gtbs}>]
read_friendly_name :Read the friendly name of an incoming call
[<{instance_index, gtbs}>]
read_optional_opcodes :Read the optional opcodes [<{instance_index,
gtbs}>]
In the following examples, notifications from GTBS are ignored, unless otherwise specified.
Example usage
=============
Setup
-----
.. code-block:: console
uart:~$ bt init
uart:~$ bt advertise on
Advertising started
When connected
--------------
Placing a call:
.. code-block:: console
uart:~$ tbs_client discover
<dbg> bt_tbs_client.primary_discover_func: Discover complete, found 1 instances (GTBS found)
<dbg> bt_tbs_client.discover_func: Setup complete for 1 / 1 TBS
<dbg> bt_tbs_client.discover_func: Setup complete GTBS
uart:~$ tbs_client originate 0 tel:123
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the dialing state with URI tel:123
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the originate opcode for call 0x00
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the alerting state with URI tel:123
<call answered by peer device, and status notified by TBS server>
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the active state with URI tel:123
Placing a call on GTBS:
.. code-block:: console
uart:~$ tbs_client originate 0 tel:123
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the dialing state with URI tel:123
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the originate opcode for call 0x00
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the alerting state with URI tel:123
<call answered by peer device, and status notified by TBS server>
<dbg> bt_tbs_client.notify_handler: Index 0
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x01 is in the active state with URI tel:123
It is necessary to set an outgoing caller ID before placing a call.
Accepting incoming call from peer device:
.. code-block:: console
<dbg> bt_tbs_client.incoming_uri_notify_handler: tel:123
<dbg> bt_tbs_client.in_call_notify_handler: tel:456
<dbg> bt_tbs_client.friendly_name_notify_handler: Peter
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x05 is in the incoming state with URI tel:456
uart:~$ tbs_client accept 0 5
<dbg> bt_tbs_client.call_cp_callback_handler: Status: success for the accept opcode for call 0x05
<dbg> bt_tbs_client.current_calls_notify_handler: Call 0x05 is in the active state with URI tel
Terminate call:
.. code-block:: console
uart:~$ tbs_client terminate 0 5
<dbg> bt_tbs_client.termination_reason_notify_handler: ID 0x05, reason 0x06
<dbg> bt_tbs_client.call_cp_notify_handler: Status: success for the terminate opcode for call 0x05
<dbg> bt_tbs_client.current_calls_notify_handler:
Telephone Bearer Service (TBS)
******************************
The telephone bearer service is a service that typically resides on devices that
can make calls, including calls from apps such as Skype, e.g. (smart)phones and
PCs.
It is necessary to have :kconfig:option:`CONFIG_BT_TBS_LOG_LEVEL_DBG` enabled
for using the TBS server interactively.
Using the telephone bearer service
==================================
TBS can be controlled locally, or by a remote device (when in a call). For
example a remote device may initiate a call to the device with the TBS server,
or the TBS server may initiate a call to remote device, without a TBS_CLIENT client.
The TBS implementation is capable of fully controlling any call.
Omitting an index for commands where a :code:`<instance_index>` can be supplied, defaults to the
GTBS bearer.
.. code-block:: console
tbs --help
tbs - Bluetooth TBS shell commands
Subcommands:
init :Initialize TBS
authorize :Authorize the current connection
accept :Accept call <call_index>
terminate :Terminate call <call_index>
hold :Hold call <call_index>
retrieve :Retrieve call <call_index>
originate :Originate call [<instance_index>] <uri>
join :Join calls <id> <id> [<id> [<id> [...]]]
incoming :Simulate incoming remote call [<{instance_index,
gtbs}>] <local_uri> <remote_uri>
<remote_friendly_name>
remote_answer :Simulate remote answer outgoing call <call_index>
remote_retrieve :Simulate remote retrieve <call_index>
remote_terminate :Simulate remote terminate <call_index>
remote_hold :Simulate remote hold <call_index>
set_bearer_provider_name :Set the bearer provider name [<{instance_index,
gtbs}>] <name>
set_bearer_technology :Set the bearer technology [<{instance_index,
gtbs}>] <technology>
set_bearer_signal_strength :Set the bearer signal strength [<{instance_index,
gtbs}>] <strength>
set_status_flags :Set the bearer feature and status value
[<{instance_index, gtbs}>] <feature_and_status>
set_uri_scheme :Set the URI prefix list <bearer_idx> <uri1 [uri2
[uri3 [...]]]>
print_calls :Output all calls in the debug log
Example Usage
=============
Setup
-----
.. code-block:: console
uart:~$ bt init
uart:~$ bt connect xx:xx:xx:xx:xx:xx public
When connected
--------------
Answering a call for a peer device originated by a client:
.. code-block:: console
<dbg> bt_tbs.write_call_cp: Index 0: Processing the originate opcode
<dbg> bt_tbs.originate_call: New call with call index 1
<dbg> bt_tbs.write_call_cp: Index 0: Processed the originate opcode with status success for call index 1
uart:~$ tbs remote_answer 1
TBS succeeded for call_id: 1
Incoming call from a peer device, accepted by client:
.. code-block:: console
uart:~$ tbs incoming 0 tel:123 tel:456 Peter
TBS succeeded for call_id: 4
<dbg> bt_tbs.bt_tbs_remote_incoming: New call with call index 4
<dbg> bt_tbs.write_call_cp: Index 0: Processed the accept opcode with status success for call index 4

View file

@ -0,0 +1,101 @@
/**
* @file
* @brief Bluetooth Call Control Profile (CCP) APIs.
*/
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_CCP_H_
#define ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_CCP_H_
/**
* @brief Call Control Profile (CCP)
*
* @defgroup bt_ccp Call Control Profile (CCP)
*
* @since 3.7
* @version 0.1.0
*
* @ingroup bluetooth
* @{
*
* Call Control Profile (CCP) provides procedures to initiate and control calls.
* It provides the Call Control Client and the Call Control Server roles,
* where the former is usually placed on resource constrained devices like headphones,
* and the latter placed on more powerful devices like phones and PCs.
*
* The profile is not limited to carrier phone calls and can be used with common applications like
* Discord and Teams.
*/
#include <zephyr/bluetooth/audio/tbs.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup bt_ccp_call_control_server CCP Call Control Server APIs
* @ingroup bt_ccp
* @{
*/
/** @brief Abstract Call Control Server Telephone Bearer structure. */
struct bt_ccp_call_control_server_bearer;
/**
* @brief Register a Telephone Bearer
*
* This will register a Telephone Bearer Service (TBS) (or a Generic Telephone Bearer service
* (GTBS)) with the provided parameters.
*
* As per the TBS specification, the GTBS shall be instantiated for the feature,
* and as such a GTBS shall always be registered before any TBS can be registered.
* Similarly, all TBS shall be unregistered before the GTBS can be unregistered with
* bt_ccp_call_control_server_unregister_bearer().
*
* @param[in] param The parameters to initialize the bearer.
* @param[out] bearer Pointer to the initialized bearer.
*
* @retval 0 Success
* @retval -EINVAL @p param contains invalid data
* @retval -EALREADY @p param.gtbs is true and GTBS has already been registered
* @retval -EAGAIN @p param.gtbs is false and GTBS has not been registered
* @retval -ENOMEM @p param.gtbs is false and no more TBS can be registered (see
* @kconfig{CONFIG_BT_TBS_BEARER_COUNT})
* @retval -ENOEXEC The service failed to be registered
*/
int bt_ccp_call_control_server_register_bearer(const struct bt_tbs_register_param *param,
struct bt_ccp_call_control_server_bearer **bearer);
/**
* @brief Unregister a Telephone Bearer
*
* This will unregister a Telephone Bearer Service (TBS) (or a Generic Telephone Bearer service
* (GTBS)) with the provided parameters. The bearer shall be registered first by
* bt_ccp_call_control_server_register_bearer() before it can be unregistered.
*
* All TBS shall be unregistered before the GTBS can be unregistered with.
*
* @param bearer The bearer to unregister.
*
* @retval 0 Success
* @retval -EINVAL @p bearer is NULL
* @retval -EALREADY The bearer is not registered
* @retval -ENOEXEC The service failed to be unregistered
*/
int bt_ccp_call_control_server_unregister_bearer(struct bt_ccp_call_control_server_bearer *bearer);
/** @} */ /* End of group bt_ccp_call_control_server */
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_CCP_H_ */

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ccp_call_control_server)
target_sources(app PRIVATE
src/main.c
)
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)

View file

@ -0,0 +1,15 @@
# Copyright 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
source "share/sysbuild/Kconfig"
config NET_CORE_BOARD
string
default "nrf5340dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340dk"
default "nrf5340_audio_dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340_audio_dk"
default "nrf5340bsim/nrf5340/cpunet" if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"
config NET_CORE_IMAGE_HCI_IPC
bool "HCI IPC image on network core"
default y
depends on NET_CORE_BOARD != ""

View file

@ -0,0 +1,77 @@
.. zephyr:code-sample:: bluetooth_ccp_call_control_server
:name: Call Control Profile (CCP) Call Control Server
:relevant-api: bluetooth bt_ccp bt_tbs
CCP Call Control Server sample that registers one or more TBS bearers and advertises the
TBS UUID(s).
Overview
********
Application demonstrating the CCP Call Control Server functionality.
Starts by advertising for CCP Call Control Clients to connect and set up calls.
The profile works for both GAP Central and GAP Peripheral devices, but this sample only assumes the
GAP Peripheral role.
This sample can be found under :zephyr_file:`samples/bluetooth/ccp_call_control_server` in the Zephyr tree.
Check the :zephyr:code-sample-category:`bluetooth` samples for general information.
Requirements
************
* BlueZ running on the host, or
* A board with Bluetooth Low Energy 5.2 support
Building and Running
********************
When building targeting an nrf52 series board with the Zephyr Bluetooth Controller,
use ``-DOVERLAY_CONFIG=overlay-bt_ll_sw_split.conf`` to enable the required feature support.
Building for an nrf5340dk
-------------------------
You can build both the application core image and an appropriate controller image for the network
core with:
.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/ccp_call_control_server/
:board: nrf5340dk/nrf5340/cpuapp
:goals: build
:west-args: --sysbuild
If you prefer to only build the application core image, you can do so by doing instead:
.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/ccp_call_control_server/
:board: nrf5340dk/nrf5340/cpuapp
:goals: build
In that case you can pair this application core image with the
:zephyr:code-sample:`bluetooth_hci_ipc` sample
:zephyr_file:`samples/bluetooth/hci_ipc/nrf5340_cpunet_iso-bt_ll_sw_split.conf` configuration.
Building for a simulated nrf5340bsim
------------------------------------
Similarly to how you would for real HW, you can do:
.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/ccp_call_control_server/
:board: nrf5340bsim/nrf5340/cpuapp
:goals: build
:west-args: --sysbuild
Note this will produce a Linux executable in :file:`./build/zephyr/zephyr.exe`.
For more information, check :ref:`this board documentation <nrf5340bsim>`.
Building for a simulated nrf52_bsim
-----------------------------------
.. zephyr-app-commands::
:zephyr-app: samples/bluetooth/ccp_call_control_server/
:board: nrf52_bsim
:goals: build
:gen-args: -DOVERLAY_CONFIG=overlay-bt_ll_sw_split.conf

View file

@ -0,0 +1,6 @@
CONFIG_BT_BUF_EVT_RX_SIZE=255
CONFIG_BT_BUF_ACL_RX_SIZE=255
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_CMD_TX_SIZE=255
CONFIG_BT_SEND_ECC_EMULATION=y

View file

@ -0,0 +1,6 @@
CONFIG_BT_BUF_EVT_RX_SIZE=255
CONFIG_BT_BUF_ACL_RX_SIZE=255
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_CMD_TX_SIZE=255
CONFIG_BT_SEND_ECC_EMULATION=y

View file

@ -0,0 +1,5 @@
# Zephyr Bluetooth Controller
CONFIG_BT_LL_SW_SPLIT=y
# Zephyr Controller tested maximum advertising data that can be set in a single HCI command
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191

View file

@ -0,0 +1,18 @@
CONFIG_BT=y
CONFIG_LOG=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_AUDIO=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_DEVICE_NAME="CCP Call Control Server"
CONFIG_BT_SMP=y
CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
# CCP support
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT=2
CONFIG_BT_TBS=y
CONFIG_BT_TBS_BEARER_COUNT=1
CONFIG_UTF8=y

View file

@ -0,0 +1,30 @@
sample:
description: Bluetooth Low Energy Call Control Profile Call Control Server sample
name: Bluetooth Low Energy Call Control Profile Call Control Server sample
tests:
sample.bluetooth.ccp_call_control_server:
harness: bluetooth
platform_allow:
- qemu_cortex_m3
- qemu_x86
- nrf5340dk/nrf5340/cpuapp
- nrf5340bsim/nrf5340/cpuapp
integration_platforms:
- qemu_x86
- nrf5340dk/nrf5340/cpuapp
tags: bluetooth
sysbuild: true
sample.bluetooth.ccp_call_control_server.bt_ll_sw_split:
harness: bluetooth
platform_allow:
- nrf52_bsim
- nrf52833dk/nrf52833
- nrf52840dk/nrf52840
- nrf52840dongle/nrf52840
integration_platforms:
- nrf52_bsim
- nrf52833dk/nrf52833
- nrf52840dk/nrf52840
- nrf52840dongle/nrf52840
extra_args: OVERLAY_CONFIG=overlay-bt_ll_sw_split.conf
tags: bluetooth

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/bluetooth/audio/ccp.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gap.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
LOG_MODULE_REGISTER(ccp_call_control_server, CONFIG_LOG_DEFAULT_LEVEL);
#define SEM_TIMEOUT K_SECONDS(5)
static const struct bt_data ad[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_GTBS_VAL)),
BT_DATA_BYTES(BT_DATA_SVC_DATA16, BT_UUID_16_ENCODE(BT_UUID_GTBS_VAL)),
IF_ENABLED(CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT > 1,
(BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_TBS_VAL)),
BT_DATA_BYTES(BT_DATA_SVC_DATA16, BT_UUID_16_ENCODE(BT_UUID_TBS_VAL))))};
static struct bt_le_ext_adv *adv;
static struct bt_conn *peer_conn;
static struct bt_ccp_call_control_server_bearer
*bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT];
static K_SEM_DEFINE(sem_state_change, 0, 1);
static void connected_cb(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Connected: %s", addr);
peer_conn = bt_conn_ref(conn);
k_sem_give(&sem_state_change);
}
static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
if (conn != peer_conn) {
return;
}
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Disconnected: %s (reason 0x%02x)", addr, reason);
bt_conn_unref(peer_conn);
peer_conn = NULL;
k_sem_give(&sem_state_change);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected_cb,
.disconnected = disconnected_cb,
};
static int advertise(void)
{
int err;
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN, NULL, &adv);
if (err) {
LOG_ERR("Failed to create advertising set: %d", err);
return err;
}
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
LOG_ERR("Failed to set advertising data: %d", err);
return err;
}
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
LOG_ERR("Failed to start advertising set: %d", err);
return err;
}
LOG_INF("Advertising successfully started");
/* Wait for connection*/
err = k_sem_take(&sem_state_change, K_FOREVER);
if (err != 0) {
LOG_ERR("Failed to take sem_state_change: err %d", err);
return err;
}
return 0;
}
static int reset_ccp_call_control_server(void)
{
int err;
LOG_INF("Resetting");
if (peer_conn != NULL) {
err = bt_conn_disconnect(peer_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (err != 0) {
return err;
}
err = k_sem_take(&sem_state_change, K_FOREVER);
if (err != 0) {
LOG_ERR("Failed to take sem_state_change: %d", err);
return err;
}
}
if (adv != NULL) {
err = bt_le_ext_adv_stop(adv);
if (err != 0) {
LOG_ERR("Failed to stop advertiser: %d", err);
return err;
}
err = bt_le_ext_adv_delete(adv);
if (err != 0) {
LOG_ERR("Failed to delete advertiser: %d", err);
return err;
}
adv = NULL;
}
k_sem_reset(&sem_state_change);
return 0;
}
static int init_ccp_call_control_server(void)
{
const struct bt_tbs_register_param gtbs_param = {
.provider_name = "Generic TBS",
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
int err;
err = bt_enable(NULL);
if (err != 0) {
LOG_ERR("Bluetooth enable failed (err %d)", err);
return err;
}
LOG_DBG("Bluetooth initialized");
err = bt_ccp_call_control_server_register_bearer(&gtbs_param, &bearers[0]);
if (err < 0) {
LOG_ERR("Failed to register GTBS (err %d)", err);
return err;
}
LOG_INF("Registered GTBS bearer");
for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
char prov_name[22]; /* Enough to store "Telephone Bearer #255" */
const struct bt_tbs_register_param tbs_param = {
.provider_name = prov_name,
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = false,
.authorization_required = false,
/* Set different technologies per bearer */
.technology = (i % BT_TBS_TECHNOLOGY_WCDMA) + 1,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
snprintf(prov_name, sizeof(prov_name), "Telephone Bearer #%d", i);
err = bt_ccp_call_control_server_register_bearer(&tbs_param, &bearers[i]);
if (err < 0) {
LOG_ERR("Failed to register bearer[%d]: %d", i, err);
return err;
}
LOG_INF("Registered bearer[%d]", i);
}
return 0;
}
int main(void)
{
int err;
err = init_ccp_call_control_server();
if (err != 0) {
return 0;
}
LOG_INF("CCP Call Control Server initialized");
while (true) {
err = reset_ccp_call_control_server();
if (err != 0) {
LOG_ERR("Failed to reset");
break;
}
/* Start advertising as a CCP Call Control Server, which includes setting the
* required advertising data based on the roles we support.
*/
err = advertise();
if (err != 0) {
continue;
}
/* After advertising we expect CCP Call Control Clients to connect to us and
* eventually disconnect again. As a CCP Call Control Server we just react to their
* requests and not do anything else.
*/
/* Reset if disconnected */
err = k_sem_take(&sem_state_change, K_FOREVER);
if (err != 0) {
LOG_ERR("Failed to take sem_state_change: err %d", err);
break;
}
}
return 0;
}

View file

@ -0,0 +1,24 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC)
# For builds in the nrf5340, we build the netcore image with the controller
set(NET_APP hci_ipc)
set(NET_APP_SRC_DIR ${ZEPHYR_BASE}/samples/bluetooth/${NET_APP})
ExternalZephyrProject_Add(
APPLICATION ${NET_APP}
SOURCE_DIR ${NET_APP_SRC_DIR}
BOARD ${SB_CONFIG_NET_CORE_BOARD}
)
set(${NET_APP}_CONF_FILE
${NET_APP_SRC_DIR}/nrf5340_cpunet_iso-bt_ll_sw_split.conf
CACHE INTERNAL ""
)
native_simulator_set_child_images(${DEFAULT_IMAGE} ${NET_APP})
endif()
native_simulator_set_final_executable(${DEFAULT_IMAGE})

View file

@ -8,7 +8,7 @@ target_sources(app PRIVATE
src/main.c src/main.c
src/mcp_server.c src/mcp_server.c
src/vcp_vol_ctlr.c src/vcp_vol_ctlr.c
src/ccp_server.c src/ccp_call_control_server.c
src/cap_initiator.c src/cap_initiator.c
) )

View file

@ -35,6 +35,7 @@ CONFIG_MCTL_LOCAL_PLAYER_CONTROL=y
CONFIG_MCTL=y CONFIG_MCTL=y
# CCP support # CCP support
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_TBS=y CONFIG_BT_TBS=y
CONFIG_BT_TBS_SUPPORTED_FEATURES=3 CONFIG_BT_TBS_SUPPORTED_FEATURES=3

View file

@ -44,7 +44,7 @@ static struct bt_tbs_cb tbs_cbs = {
.authorize = NULL, .authorize = NULL,
}; };
int ccp_server_init(void) int ccp_call_control_server_init(void)
{ {
int err; int err;

View file

@ -306,8 +306,8 @@ int main(void)
} }
printk("MCP initialized\n"); printk("MCP initialized\n");
/* Initialize CCP Server */ /* Initialize CCP Call Control Server */
err = ccp_server_init(); err = ccp_call_control_server_init();
if (err != 0) { if (err != 0) {
return err; return err;
} }

View file

@ -18,11 +18,11 @@
int mcp_server_init(void); int mcp_server_init(void);
/** /**
* @brief Initialize the CCP Server role * @brief Initialize the CCP Call Control Server role
* *
* @return 0 if success, errno on failure. * @return 0 if success, errno on failure.
*/ */
int ccp_server_init(void); int ccp_call_control_server_init(void);
/** /**
* @brief Initialize the VCP Volume Controller role * @brief Initialize the VCP Volume Controller role

View file

@ -43,7 +43,7 @@ CONFIG_BT_PAC_SNK_LOC=y
# Source PAC Location Support # Source PAC Location Support
CONFIG_BT_PAC_SRC_LOC=y CONFIG_BT_PAC_SRC_LOC=y
# CCP Client Support # CCP Call Control Client Support
CONFIG_BT_TBS_CLIENT_GTBS=y CONFIG_BT_TBS_CLIENT_GTBS=y
CONFIG_BT_TBS_CLIENT_ORIGINATE_CALL=y CONFIG_BT_TBS_CLIENT_ORIGINATE_CALL=y
CONFIG_BT_TBS_CLIENT_TERMINATE_CALL=y CONFIG_BT_TBS_CLIENT_TERMINATE_CALL=y

View file

@ -58,6 +58,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_SOURCE bap_broadcast_source
zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_SINK bap_broadcast_sink.c) zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_SINK bap_broadcast_sink.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_SCAN_DELEGATOR bap_scan_delegator.c) zephyr_library_sources_ifdef(CONFIG_BT_BAP_SCAN_DELEGATOR bap_scan_delegator.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_ASSISTANT bap_broadcast_assistant.c) zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_ASSISTANT bap_broadcast_assistant.c)
zephyr_library_sources_ifdef(CONFIG_BT_CCP_CALL_CONTROL_SERVER ccp_call_control_server.c)
zephyr_library_sources_ifdef(CONFIG_BT_HAS has.c) zephyr_library_sources_ifdef(CONFIG_BT_HAS has.c)
zephyr_library_sources_ifdef(CONFIG_BT_HAS_CLIENT has_client.c) zephyr_library_sources_ifdef(CONFIG_BT_HAS_CLIENT has_client.c)
zephyr_library_sources_ifdef(CONFIG_BT_CAP cap_stream.c) zephyr_library_sources_ifdef(CONFIG_BT_CAP cap_stream.c)

View file

@ -34,6 +34,7 @@ config BT_AUDIO_NOTIFY_RETRY_DELAY
available. available.
rsource "Kconfig.bap" rsource "Kconfig.bap"
rsource "Kconfig.ccp"
rsource "Kconfig.vocs" rsource "Kconfig.vocs"
rsource "Kconfig.aics" rsource "Kconfig.aics"
rsource "Kconfig.vcp" rsource "Kconfig.vcp"

View file

@ -0,0 +1,34 @@
# Bluetooth Audio - Call Control Profile (CCP) configuration options
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
if BT_AUDIO
config BT_CCP_CALL_CONTROL_SERVER
bool "Call Control Profile Call Control Server Support"
depends on BT_EXT_ADV
depends on BT_TBS
depends on BT_BONDABLE
help
This option enables support for the Call Control Profile Call Control Server which uses
the Telephone Bearer Service (TBS) to hold and control calls on a device.
if BT_CCP_CALL_CONTROL_SERVER
config BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT
int "Telephone bearer count"
default 1
range 1 $(UINT8_MAX)
help
The number of supported telephone bearers on the CCP Call Control Server
module = BT_CCP_CALL_CONTROL_SERVER
module-str = "Call Control Profile Call Control Server"
source "subsys/logging/Kconfig.template.log_config"
endif # BT_CCP_CALL_CONTROL_SERVER
endif # BT_AUDIO

View file

@ -0,0 +1,106 @@
/* Bluetooth CCP - Call Control Profile Call Control Server
*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/check.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(bt_ccp_call_control_server, CONFIG_BT_CCP_CALL_CONTROL_SERVER_LOG_LEVEL);
/* A service instance can either be a GTBS or a TBS instance */
struct bt_ccp_call_control_server_bearer {
uint8_t tbs_index;
bool registered;
};
static struct bt_ccp_call_control_server_bearer
bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT];
static struct bt_ccp_call_control_server_bearer *get_free_bearer(void)
{
for (size_t i = 0; i < ARRAY_SIZE(bearers); i++) {
if (!bearers[i].registered) {
return &bearers[i];
}
}
return NULL;
}
int bt_ccp_call_control_server_register_bearer(const struct bt_tbs_register_param *param,
struct bt_ccp_call_control_server_bearer **bearer)
{
struct bt_ccp_call_control_server_bearer *free_bearer;
int ret;
CHECKIF(bearer == NULL) {
LOG_DBG("bearer is NULL");
return -EINVAL;
}
free_bearer = get_free_bearer();
if (free_bearer == NULL) {
return -ENOMEM;
}
ret = bt_tbs_register_bearer(param);
if (ret < 0) {
LOG_DBG("Failed to register TBS bearer: %d", ret);
/* Return known errors */
if (ret == -EINVAL || ret == -EALREADY || ret == -EAGAIN || ret == -ENOMEM) {
return ret;
}
return -ENOEXEC;
}
free_bearer->registered = true;
free_bearer->tbs_index = (uint8_t)ret;
*bearer = free_bearer;
return 0;
}
int bt_ccp_call_control_server_unregister_bearer(struct bt_ccp_call_control_server_bearer *bearer)
{
int err;
CHECKIF(bearer == NULL) {
LOG_DBG("bearer is NULL");
return -EINVAL;
}
if (!bearer->registered) {
LOG_DBG("Bearer %p already unregistered", bearer);
return -EALREADY;
}
err = bt_tbs_unregister_bearer(bearer->tbs_index);
if (err != 0) {
/* Return known errors */
if (err == -EINVAL || err == -EALREADY) {
return err;
}
return -ENOEXEC;
}
bearer->registered = false;
return 0;
}

View file

@ -3,6 +3,10 @@
zephyr_library() zephyr_library()
zephyr_library_link_libraries(subsys__bluetooth) zephyr_library_link_libraries(subsys__bluetooth)
zephyr_library_sources_ifdef(
CONFIG_BT_CCP_CALL_CONTROL_SERVER
ccp_call_control_server.c
)
zephyr_library_sources_ifdef( zephyr_library_sources_ifdef(
CONFIG_BT_VCP_VOL_REND CONFIG_BT_VCP_VOL_REND
vcp_vol_rend.c vcp_vol_rend.c

View file

@ -0,0 +1,100 @@
/** @file
* @brief Bluetooth Call Control Profile Call Control Server shell
*/
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/bluetooth/audio/ccp.h>
#include <zephyr/shell/shell.h>
static struct bt_ccp_call_control_server_bearer
*bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT];
static int cmd_ccp_call_control_server_init(const struct shell *sh, size_t argc, char *argv[])
{
static bool registered;
if (registered) {
shell_info(sh, "Already initialized");
return -ENOEXEC;
}
const struct bt_tbs_register_param gtbs_param = {
.provider_name = "Generic TBS",
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
int err;
err = bt_ccp_call_control_server_register_bearer(&gtbs_param, &bearers[0]);
if (err != 0) {
shell_error(sh, "Failed to register GTBS bearer: %d", err);
return -ENOEXEC;
}
shell_info(sh, "Registered GTBS bearer");
for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
char prov_name[22]; /* Enough to store "Telephone Bearer #255" */
const struct bt_tbs_register_param tbs_param = {
.provider_name = prov_name,
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = false,
.authorization_required = false,
/* Set different technologies per bearer */
.technology = (i % BT_TBS_TECHNOLOGY_WCDMA) + 1,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
snprintf(prov_name, sizeof(prov_name), "Telephone Bearer #%d", i);
err = bt_ccp_call_control_server_register_bearer(&tbs_param, &bearers[i]);
if (err != 0) {
shell_error(sh, "Failed to register bearer[%d]: %d", i, err);
return -ENOEXEC;
}
shell_info(sh, "Registered bearer[%d]", i);
}
registered = true;
return 0;
}
static int cmd_ccp_call_control_server(const struct shell *sh, size_t argc, char **argv)
{
if (argc > 1) {
shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
} else {
shell_error(sh, "%s Missing subcommand", argv[0]);
}
return -ENOEXEC;
}
SHELL_STATIC_SUBCMD_SET_CREATE(ccp_call_control_server_cmds,
SHELL_CMD_ARG(init, NULL, "Initialize CCP Call Control Server",
cmd_ccp_call_control_server_init, 1, 0),
SHELL_SUBCMD_SET_END);
SHELL_CMD_ARG_REGISTER(ccp_call_control_server, &ccp_call_control_server_cmds,
"Bluetooth CCP Call Control Server shell commands",
cmd_ccp_call_control_server, 1, 1);

View file

@ -0,0 +1,17 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
project(bluetooth_ccp)
find_package(Zephyr COMPONENTS unittest HINTS $ENV{ZEPHYR_BASE})
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/ccp_call_control_server/uut uut)
target_link_libraries(testbinary PRIVATE uut)
target_include_directories(testbinary PRIVATE include)
target_sources(testbinary
PRIVATE
src/main.c
)

View file

@ -0,0 +1,21 @@
CONFIG_ZTEST=y
CONFIG_BT=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_AUDIO=y
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT=2
CONFIG_BT_TBS=y
CONFIG_BT_TBS_BEARER_COUNT=1
CONFIG_UTF8=y
CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2
CONFIG_ASSERT_VERBOSE=y
CONFIG_LOG=y
CONFIG_BT_CCP_CALL_CONTROL_SERVER_LOG_LEVEL_DBG=y
CONFIG_BT_TBS_LOG_LEVEL_DBG=y

View file

@ -0,0 +1,264 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/audio/ccp.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/fff.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <ztest_test.h>
#include <ztest_assert.h>
DEFINE_FFF_GLOBALS;
struct ccp_call_control_server_test_suite_fixture {
/** Need 1 additional bearer than the max to trigger some corner cases */
struct bt_ccp_call_control_server_bearer
*bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT + 1];
};
static void *ccp_call_control_server_test_suite_setup(void)
{
struct ccp_call_control_server_test_suite_fixture *fixture;
fixture = malloc(sizeof(*fixture));
zassert_not_null(fixture);
return fixture;
}
static void ccp_call_control_server_test_suite_before(void *f)
{
memset(f, 0, sizeof(struct ccp_call_control_server_test_suite_fixture));
}
static void ccp_call_control_server_test_suite_after(void *f)
{
struct ccp_call_control_server_test_suite_fixture *fixture = f;
/* We unregister from largest to lowest index, as GTBS shall be unregistered last and is
* always at index 0
*/
for (size_t i = ARRAY_SIZE(fixture->bearers); i > 0; i--) {
/* Since size_t cannot be negative, we cannot use the regular i >= 0 when counting
* downwards as that will always be true, so we use an additional index variable
*/
const size_t index_to_unreg = i - 1;
if (fixture->bearers[index_to_unreg] != NULL) {
bt_ccp_call_control_server_unregister_bearer(
fixture->bearers[index_to_unreg]);
}
fixture->bearers[index_to_unreg] = NULL;
}
}
static void ccp_call_control_server_test_suite_teardown(void *f)
{
free(f);
}
ZTEST_SUITE(ccp_call_control_server_test_suite, NULL, ccp_call_control_server_test_suite_setup,
ccp_call_control_server_test_suite_before, ccp_call_control_server_test_suite_after,
ccp_call_control_server_test_suite_teardown);
static void register_default_bearer(struct ccp_call_control_server_test_suite_fixture *fixture)
{
const struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
err = bt_ccp_call_control_server_register_bearer(&register_param, &fixture->bearers[0]);
zassert_equal(err, 0, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite, test_ccp_call_control_server_register_bearer)
{
register_default_bearer(fixture);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_multiple_bearers)
{
if (CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT == 1) {
ztest_test_skip();
}
register_default_bearer(fixture);
for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = false,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
err = bt_ccp_call_control_server_register_bearer(&register_param,
&fixture->bearers[i]);
zassert_equal(err, 0, "Unexpected return value %d", err);
}
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_bearer_inval_null_param)
{
int err;
err = bt_ccp_call_control_server_register_bearer(NULL, &fixture->bearers[0]);
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_bearer_inval_null_bearer)
{
const struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
err = bt_ccp_call_control_server_register_bearer(&register_param, NULL);
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_bearer_inval_no_gtbs)
{
const struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = false,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
err = bt_ccp_call_control_server_register_bearer(&register_param, &fixture->bearers[0]);
zassert_equal(err, -EAGAIN, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_bearer_inval_double_gtbs)
{
const struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
if (CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT == 1) {
ztest_test_skip();
}
register_default_bearer(fixture);
err = bt_ccp_call_control_server_register_bearer(&register_param, &fixture->bearers[1]);
zassert_equal(err, -EALREADY, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_register_bearer_inval_cnt)
{
const struct bt_tbs_register_param register_param = {
.provider_name = "test",
.uci = "un999",
.uri_schemes_supported = "tel",
.gtbs = false,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = 0,
};
int err;
if (CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT == 1) {
ztest_test_skip();
}
register_default_bearer(fixture);
for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
err = bt_ccp_call_control_server_register_bearer(&register_param,
&fixture->bearers[i]);
zassert_equal(err, 0, "Unexpected return value %d", err);
}
err = bt_ccp_call_control_server_register_bearer(
&register_param, &fixture->bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT]);
zassert_equal(err, -ENOMEM, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite, test_ccp_call_control_server_unregister_bearer)
{
int err;
register_default_bearer(fixture);
err = bt_ccp_call_control_server_unregister_bearer(fixture->bearers[0]);
zassert_equal(err, 0, "Unexpected return value %d", err);
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_unregister_bearer_inval_double_unregister)
{
int err;
register_default_bearer(fixture);
err = bt_ccp_call_control_server_unregister_bearer(fixture->bearers[0]);
zassert_equal(err, 0, "Unexpected return value %d", err);
err = bt_ccp_call_control_server_unregister_bearer(fixture->bearers[0]);
zassert_equal(err, -EALREADY, "Unexpected return value %d", err);
fixture->bearers[0] = NULL;
}
static ZTEST_F(ccp_call_control_server_test_suite,
test_ccp_call_control_server_unregister_bearer_inval_null_bearer)
{
int err;
err = bt_ccp_call_control_server_unregister_bearer(NULL);
zassert_equal(err, -EINVAL, "Unexpected return value %d", err);
}

View file

@ -0,0 +1,7 @@
common:
tags:
- bluetooth
- bluetooth_audio
tests:
bluetooth.audio.ccp_call_control_server.test:
type: unit

View file

@ -0,0 +1,22 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
# CMakeLists.txt file for creating of uut library.
#
add_library(uut STATIC
${ZEPHYR_BASE}/subsys/bluetooth/audio/audio.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/ccid.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/ccp_call_control_server.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/tbs.c
${ZEPHYR_BASE}/lib/net_buf/buf_simple.c
${ZEPHYR_BASE}/lib/utils/utf8.c
)
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks)
target_link_libraries(uut PUBLIC test_interface mocks)
target_compile_options(uut PRIVATE -std=c11 -include ztest.h)

View file

@ -130,7 +130,9 @@ CONFIG_BT_MPL_MAX_OBJ_SIZE=600
CONFIG_BT_MPL_ICON_BITMAP_SIZE=321 CONFIG_BT_MPL_ICON_BITMAP_SIZE=321
CONFIG_BT_MPL_TRACK_MAX_SIZE=50 CONFIG_BT_MPL_TRACK_MAX_SIZE=50
# Telephone bearer service # Call Control
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT=2
CONFIG_BT_TBS=y CONFIG_BT_TBS=y
CONFIG_BT_TBS_BEARER_COUNT=1 CONFIG_BT_TBS_BEARER_COUNT=1
CONFIG_BT_TBS_SUPPORTED_FEATURES=3 CONFIG_BT_TBS_SUPPORTED_FEATURES=3

View file

@ -324,6 +324,7 @@ tests:
extra_args: CONF_FILE="audio.conf" extra_args: CONF_FILE="audio.conf"
build_only: true build_only: true
extra_configs: extra_configs:
- CONFIG_BT_CCP_CALL_CONTROL_SERVER=n
- CONFIG_BT_TBS=n - CONFIG_BT_TBS=n
tags: bluetooth tags: bluetooth
bluetooth.shell.audio.only_gtbs: bluetooth.shell.audio.only_gtbs:
@ -431,3 +432,9 @@ tests:
- nrf52_bsim - nrf52_bsim
integration_platforms: integration_platforms:
- nrf52_bsim - nrf52_bsim
bluetooth.shell.audio.no_ccp_call_control_server:
extra_args: CONF_FILE="audio.conf"
build_only: true
extra_configs:
- CONFIG_BT_CCP_CALL_CONTROL_SERVER=n
tags: bluetooth

View file

@ -127,6 +127,7 @@ CONFIG_BT_CSIP_SET_COORDINATOR=y
CONFIG_BT_CSIP_SET_COORDINATOR_MAX_CSIS_INSTANCES=3 CONFIG_BT_CSIP_SET_COORDINATOR_MAX_CSIS_INSTANCES=3
# CCP # CCP
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_ATT_TX_COUNT=12 CONFIG_BT_ATT_TX_COUNT=12
CONFIG_BT_TBS_CLIENT_GTBS=y CONFIG_BT_TBS_CLIENT_GTBS=y
CONFIG_BT_TBS_CLIENT_TBS=y CONFIG_BT_TBS_CLIENT_TBS=y

View file

@ -89,7 +89,8 @@ CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE=y
CONFIG_BT_CSIP_SET_COORDINATOR=y CONFIG_BT_CSIP_SET_COORDINATOR=y
CONFIG_BT_CSIP_SET_COORDINATOR_TEST_SAMPLE_DATA=y CONFIG_BT_CSIP_SET_COORDINATOR_TEST_SAMPLE_DATA=y
# Telephone bearer service # Call control
CONFIG_BT_CCP_CALL_CONTROL_SERVER=y
CONFIG_BT_TBS=y CONFIG_BT_TBS=y
CONFIG_BT_TBS_BEARER_COUNT=1 CONFIG_BT_TBS_BEARER_COUNT=1
CONFIG_BT_TBS_CLIENT_TBS=y CONFIG_BT_TBS_CLIENT_TBS=y

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/bluetooth/audio/ccp.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/logging/log.h>
#include "bstests.h"
#include "common.h"
#ifdef CONFIG_BT_CCP_CALL_CONTROL_SERVER
LOG_MODULE_REGISTER(ccp_call_control_server, CONFIG_LOG_DEFAULT_LEVEL);
extern enum bst_result_t bst_result;
static struct bt_ccp_call_control_server_bearer
*bearers[CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT];
CREATE_FLAG(is_connected);
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != 0) {
FAIL("Failed to connect to %s (%u)\n", addr, err);
return;
}
LOG_DBG("Connected to %s", addr);
default_conn = bt_conn_ref(conn);
SET_FLAG(is_connected);
}
static void init(void)
{
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
const struct bt_tbs_register_param gtbs_param = {
.provider_name = "Generic TBS",
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = true,
.authorization_required = false,
.technology = BT_TBS_TECHNOLOGY_3G,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
LOG_DBG("Bluetooth initialized");
err = bt_conn_cb_register(&conn_callbacks);
if (err != 0) {
FAIL("Failed to register conn CBs (err %d)\n", err);
return;
}
err = bt_le_scan_cb_register(&common_scan_cb);
if (err != 0) {
FAIL("Failed to register scan CBs (err %d)\n", err);
return;
}
err = bt_ccp_call_control_server_register_bearer(&gtbs_param, &bearers[0]);
if (err < 0) {
FAIL("Failed to register GTBS (err %d)\n", err);
return;
}
LOG_INF("Registered GTBS bearer");
for (int i = 1; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
char prov_name[22]; /* Enough to store "Telephone Bearer #255" */
const struct bt_tbs_register_param tbs_param = {
.provider_name = prov_name,
.uci = "un000",
.uri_schemes_supported = "tel,skype",
.gtbs = false,
.authorization_required = false,
/* Set different technologies per bearer */
.technology = (i % BT_TBS_TECHNOLOGY_WCDMA) + 1,
.supported_features = CONFIG_BT_TBS_SUPPORTED_FEATURES,
};
snprintf(prov_name, sizeof(prov_name), "Telephone Bearer #%d", i);
err = bt_ccp_call_control_server_register_bearer(&tbs_param, &bearers[i]);
if (err < 0) {
FAIL("Failed to register bearer[%d]: %d\n", i, err);
return;
}
LOG_INF("Registered bearer[%d]", i);
}
}
static void unregister_bearers(void)
{
for (int i = 0; i < CONFIG_BT_CCP_CALL_CONTROL_SERVER_BEARER_COUNT; i++) {
if (bearers[i] != NULL) {
const int err = bt_ccp_call_control_server_unregister_bearer(bearers[i]);
if (err < 0) {
FAIL("Failed to unregister bearer[%d]: %d\n", i, err);
return;
}
LOG_DBG("Unregistered bearer[%d]", i);
bearers[i] = NULL;
}
}
}
static void test_main(void)
{
init();
unregister_bearers();
PASS("CCP Call Control Server Passed\n");
}
static const struct bst_test_instance test_ccp_call_control_server[] = {
{
.test_id = "ccp_call_control_server",
.test_pre_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_ccp_call_control_server_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_ccp_call_control_server);
}
#else
struct bst_test_list *test_ccp_call_control_server_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_TBS */

View file

@ -44,6 +44,7 @@ extern struct bst_test_list *test_csip_notify_client_install(struct bst_test_lis
extern struct bst_test_list *test_csip_notify_server_install(struct bst_test_list *tests); extern struct bst_test_list *test_csip_notify_server_install(struct bst_test_list *tests);
extern struct bst_test_list *test_gmap_ugg_install(struct bst_test_list *tests); extern struct bst_test_list *test_gmap_ugg_install(struct bst_test_list *tests);
extern struct bst_test_list *test_gmap_ugt_install(struct bst_test_list *tests); extern struct bst_test_list *test_gmap_ugt_install(struct bst_test_list *tests);
extern struct bst_test_list *test_ccp_call_control_server_install(struct bst_test_list *tests);
bst_test_install_t test_installers[] = { bst_test_install_t test_installers[] = {
test_vcp_install, test_vcp_install,
@ -82,6 +83,7 @@ bst_test_install_t test_installers[] = {
test_csip_notify_server_install, test_csip_notify_server_install,
test_gmap_ugg_install, test_gmap_ugg_install,
test_gmap_ugt_install, test_gmap_ugt_install,
test_ccp_call_control_server_install,
NULL, NULL,
}; };

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
VERBOSITY_LEVEL=2
cd ${BSIM_OUT_PATH}/bin
SIMULATION_ID="ccp"
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=ccp_call_control_server -rs=1 -D=1
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=1 -sim_length=60e6 $@
wait_for_background_jobs

View file

@ -0,0 +1,23 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ccp_call_control_server_self_tets)
set(ccp_call_control_server_path ${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server)
target_sources(app PRIVATE
${ccp_call_control_server_path}/src/main.c
)
target_sources(app PRIVATE
src/test_main.c
)
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

View file

@ -0,0 +1,10 @@
# Copyright (c) 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
source "${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server/Kconfig.sysbuild"
config NATIVE_SIMULATOR_PRIMARY_MCU_INDEX
int
# Let's pass the test arguments to the application MCU test
# otherwise by default they would have gone to the net core.
default 0 if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"

View file

@ -0,0 +1,2 @@
# Please build using the sample configuration file:
# ${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server/prj.conf

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "bs_utils.h"
#include "bstests.h"
#define WAIT_TIME 10 /* Seconds */
#define PASS_THRESHOLD 100 /* Audio packets */
extern enum bst_result_t bst_result;
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, __VA_ARGS__); \
} while (0)
static void test_ccp_call_control_server_sample_init(void)
{
bst_ticker_set_next_tick_absolute(WAIT_TIME * 1e6);
bst_result = In_progress;
}
static void test_ccp_call_control_server_sample_tick(bs_time_t HW_device_time)
{
/* TODO: Once the sample is more complete we can expand the pass criteria */
PASS("CCP Call Control Server sample PASSED\n");
}
static const struct bst_test_instance test_sample[] = {
{
.test_id = "ccp_call_control_server",
.test_descr = "Test based on the CCP Call Control Server sample. "
"It expects to be connected to a compatible CCP Call Control Client, "
"waits for " STR(
WAIT_TIME) " seconds, and checks how "
"many audio packets have been received correctly",
.test_post_init_f = test_ccp_call_control_server_sample_init,
.test_tick_f = test_ccp_call_control_server_sample_tick,
},
BSTEST_END_MARKER,
};
static struct bst_test_list *
test_ccp_call_control_server_sample_install(struct bst_test_list *tests)
{
tests = bst_add_tests(tests, test_sample);
return tests;
}
bst_test_install_t test_installers[] = {test_ccp_call_control_server_sample_install, NULL};

View file

@ -0,0 +1,6 @@
# Copyright (c) 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
include(${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server/sysbuild.cmake)
native_simulator_set_primary_mcu_index(${DEFAULT_IMAGE} ${NET_APP})

View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Copyright 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
#set -x #uncomment this line for debugging
set -ue
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root directory}"
source ${ZEPHYR_BASE}/tests/bsim/compile.source
if [ "${BOARD_TS}" == "nrf5340bsim_nrf5340_cpuapp" ]; then
app=tests/bsim/bluetooth/audio_samples/ccp/call_control_server \
sample=${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server \
conf_file=${sample}/prj.conf \
conf_overlay=${sample}/boards/nrf5340_audio_dk_nrf5340_cpuapp.conf \
exe_name=bs_${BOARD_TS}_${app}_prj_conf sysbuild=1 compile
else
app=tests/bsim/bluetooth/audio_samples/ccp/call_control_server \
sample=${ZEPHYR_BASE}/samples/bluetooth/ccp_call_control_server \
conf_file=${sample}/prj.conf \
conf_overlay=${sample}/overlay-bt_ll_sw_split.conf \
exe_name=bs_${BOARD_TS}_${app}_prj_conf sysbuild=1 compile
fi
wait_for_background_jobs

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Copyright 2023-2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Simple selfchecking test for the CCP samples.
simulation_id="ccp_samples_test"
verbosity_level=2
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_samples_ccp_call_control_server_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=1 -testid=ccp_call_control_server
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=1 -sim_length=20e6 $@ -argschannel -at=40
wait_for_background_jobs #Wait for all programs in background and return != 0 if any fails

View file

@ -14,5 +14,6 @@ source ${ZEPHYR_BASE}/tests/bsim/compile.source
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/bap_broadcast_sink/compile.sh run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/bap_broadcast_sink/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/bap_unicast_client/compile.sh run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/bap_unicast_client/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/cap/compile.sh run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/cap/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio_samples/ccp/compile.sh
wait_for_background_jobs wait_for_background_jobs