pw_bluetooth_proxy#
Lightweight proxy for augmenting Bluetooth functionality
Unstable C++17
The pw_bluetooth_proxy
module provides a lightweight proxy host that
can be placed between a Bluetooth host and Bluetooth controller to add
additional functionality or inspection. All without modifying the host or the
controller.
An example use case could be offloading some functionality from a main host located on the application processor to instead be handled on the MCU (to reduce power usage).
The proxy acts as a proxy of all host controller interface (HCI) packets between the host and the controller.
pw::bluetooth::proxy::ProxyHost
acts as the main coordinator for
proxy functionality.
#include "pw_bluetooth_proxy/proxy_host.h"
// Container creates ProxyHost .
ProxyHost proxy = ProxyHost(std::move(container_send_to_host_fn),
std::move(container_send_to_controller_fn),
2);
// Container passes H4 packets from host through proxy. Proxy will in turn
// call the container-provided `container_send_to_controller_fn` to pass them
// on to the controller. Some packets may be modified, added, or removed.
proxy.HandleH4HciFromHost(std::move(h4_packet_from_host));
// Container passes H4 packets from controller through proxy. Proxy will in
// turn call the container-provided `container_send_to_host_fn` to pass them
// on to the controller. Some packets may be modified, added, or removed.
proxy.HandleH4HciFromController(std::move(h4_packet_from_controller));
Get started#
Add Emboss to your project as described in Emboss.
Bazel isn’t supported yet.
2. Then add $dir_pw_bluetooth_proxy
to
the deps
list in your pw_executable()
build target:
pw_executable("...") {
# ...
deps = [
# ...
"$dir_pw_bluetooth_proxy",
# ...
]
}
2. Then add pw_bluetooth_proxy
to
the DEPS
list in your cmake target:
API reference#
pw::bluetooth::proxy::ProxyHost#
-
class ProxyHost#
ProxyHost
acts as the main coordinator for proxy functionality. After creation, the container then passes packets through the proxy.Public Functions
- ProxyHost(
- pw::Function<void(H4PacketWithHci &&packet)> &&send_to_host_fn,
- pw::Function<void(H4PacketWithH4 &&packet)> &&send_to_controller_fn,
- uint16_t le_acl_credits_to_reserve,
- uint16_t br_edr_acl_credits_to_reserve = 0,
Creates an
ProxyHost
that will process HCI packets.- Parameters:
send_to_host_fn – [in] Callback that will be called when proxy wants to send HCI packet towards the host.
send_to_controller_fn – [in] - Callback that will be called when proxy wants to send HCI packet towards the controller.
-
void HandleH4HciFromHost(H4PacketWithH4 &&h4_packet)#
Called by container to ask proxy to handle a H4 HCI packet sent from the host side towards the controller side. Proxy will in turn call the
send_to_controller_fn
provided during construction to pass the packet on to the controller. Some packets may be modified, added, or removed.The proxy host currently does not require any from-host packets to support its current functionality. It will pass on all packets, so containers can choose to just pass all from-host packets through it.
Container is required to call this function synchronously (one packet at a time).
-
void HandleH4HciFromController(H4PacketWithHci &&h4_packet)#
Called by container to ask proxy to handle a H4 packet sent from the controller side towards the host side. Proxy will in turn call the
send_to_host_fn
provided during construction to pass the packet on to the host. Some packets may be modified, added, or removed.To support all of its current functionality, the proxy host needs at least the following from-controller packets passed through it. It will pass on all other packets, so containers can choose to just pass all from-controller packets through the proxy host.
All packets of this type:
L2CAP over ACL packets (specifically those addressed to channels managed by the proxy host, including signaling packets)
HCI_Command_Complete events (7.7.14) containing return parameters for these commands:
HCI_LE_Read_Buffer_Size [v1] command (7.8.2)
HCI_LE_Read_Buffer_Size [v2] command (7.8.2)
These HCI event packets:
HCI_Number_Of_Completed_Packets event (7.7.19)
HCI_Disconnection_Complete event (7.7.5)
Container is required to call this function synchronously (one packet at a time).
-
void Reset()#
Called by container to notify proxy that the Bluetooth system is being reset, so the proxy can reset its internal state. Warning: Outstanding H4 packets are not invalidated upon reset. If they are destructed post-reset, packets generated post-reset are liable to be overwritten prematurely.
- pw::Result<L2capCoc> AcquireL2capCoc(
- uint16_t connection_handle,
- L2capCoc::CocConfig rx_config,
- L2capCoc::CocConfig tx_config,
- pw::Function<void(pw::span<uint8_t> payload)> &&receive_fn,
- pw::Function<void(L2capCoc::Event event)> &&event_fn,
Returns an L2CAP connection-oriented channel that supports writing to and reading from a remote peer.
- Parameters:
connection_handle – [in] The connection handle of the remote peer.
rx_config – [in] Parameters applying to reading packets. See
l2cap_coc.h
for details.tx_config – [in] Parameters applying to writing packets. See
l2cap_coc.h
for details.receive_fn – [in] Read callback to be invoked on Rx SDUs.
event_fn – [in] Handle asynchronous events such as errors encountered by the channel.
- Returns:
Code
Description
If arguments are invalid (check logs). UNAVAILABLE: If channel could not be created because no memory was available to accommodate an additional ACL connection.
- pw::Result<BasicL2capChannel> AcquireBasicL2capChannel(
- uint16_t connection_handle,
- uint16_t local_cid,
- AclTransportType transport,
- pw::Function<void(pw::span<uint8_t> payload)> &&payload_from_controller_fn,
Returns an L2CAP channel operating in basic mode that supports writing to and reading from a remote peer.
- Parameters:
connection_handle – [in] The connection handle of the remote peer.
local_cid – [in] L2CAP channel ID of the local endpoint.
transport – [in] Logical link transport type.
payload_from_controller_fn – [in] Read callback to be invoked on Rx SDUs.
- Returns:
Code
Description
If arguments are invalid (check logs). UNAVAILABLE: If channel could not be created because no memory was available to accommodate an additional ACL connection.
-
pw::Status SendGattNotify(uint16_t connection_handle, uint16_t attribute_handle, pw::span<const uint8_t> attribute_value)#
Send a GATT Notify to the indicated connection.
- Parameters:
connection_handle – [in] The connection handle of the peer to notify. Maximum valid connection handle is 0x0EFF.
attribute_handle – [in] The attribute handle the notify should be sent on. Cannot be 0.
attribute_value – [in] The data to be sent. Data will be copied before function completes.
- Returns:
Code
Description
If notify was successfully queued for send. UNAVAILABLE: If CHRE doesn’t have resources to queue the send at this time (transient error). INVALID_ARGUMENT: If arguments are invalid (check logs).
- pw::Result<RfcommChannel> AcquireRfcommChannel(
- uint16_t connection_handle,
- RfcommChannel::Config rx_config,
- RfcommChannel::Config tx_config,
- uint8_t channel_number,
- pw::Function<void(pw::span<uint8_t> payload)> &&receive_fn,
Returns an RFCOMM channel that supports writing to and reading from a remote peer.
- Parameters:
connection_handle – [in] The connection handle of the remote peer.
rx_config – [in] Parameters applying to reading packets. See
rfcomm_channel.h
for details.tx_config – [in] Parameters applying to writing packets. See
rfcomm_channel.h
for details.channel_number – [in] RFCOMM channel number to use.
receive_fn – [in] Read callback to be invoked on Rx frames.
- Returns:
Code
Description
If arguments are invalid (check logs). UNAVAILABLE: If channel could not be created.
-
bool HasSendLeAclCapability() const#
Indicates whether the proxy has the capability of sending LE ACL packets. Note that this indicates intention, so it can be true even if the proxy has not yet or has been unable to reserve credits from the host.
-
inline bool HasSendAclCapability() const#
- Deprecated:
Use HasSendLeAclCapability
-
bool HasSendBrEdrAclCapability() const#
Indicates whether the proxy has the capability of sending BR/EDR ACL packets. Note that this indicates intention, so it can be true even if the proxy has not yet or has been unable to reserve credits from the host.
-
uint16_t GetNumFreeLeAclPackets() const#
Returns the number of available LE ACL send credits for the proxy. Can be zero if the controller has not yet been initialized by the host.
-
uint16_t GetNumFreeBrEdrAclPackets() const#
Returns the number of available BR/EDR ACL send credits for the proxy. Can be zero if the controller has not yet been initialized by the host.
Public Static Functions
-
static inline constexpr size_t GetNumSimultaneousAclSendsSupported()#
Returns the max number of LE ACL sends that can be in-flight at one time. That is, ACL packets that have been sent and not yet released.
-
static inline constexpr size_t GetMaxAclSendSize()#
Returns the max LE ACL packet size supported to be sent.
-
static inline constexpr size_t GetMaxNumLeAclConnections()#
Returns the max number of simultaneous LE ACL connections supported.
Code size analysis#
Delta when constructing a proxy and just sending packets through.
Label |
Segment |
Delta |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Create and use proxy as a simple passthrough |
FLASH
|
+14,192 |
Roadmap#
ACL flow control
Sending GATT notifications
CMake support
Receiving GATT notifications
Taking ownership of a L2CAP channel
Bazel support
And more…