Add connection parameter update from peripheral

This commit is contained in:
William Escande
2023-09-09 00:13:30 -07:00
committed by David Duarte
parent 80824f3fc1
commit 783b2d70a5
3 changed files with 66 additions and 4 deletions

View File

@@ -142,6 +142,10 @@ class ConnectionError(BaseError): # pylint: disable=redefined-builtin
self.peer_address = peer_address self.peer_address = peer_address
class ConnectionParameterUpdateError(BaseError):
"""Connection Parameter Update Error"""
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# UUID # UUID
# #

View File

@@ -141,6 +141,7 @@ from .core import (
BT_LE_TRANSPORT, BT_LE_TRANSPORT,
BT_PERIPHERAL_ROLE, BT_PERIPHERAL_ROLE,
AdvertisingData, AdvertisingData,
ConnectionParameterUpdateError,
CommandTimeoutError, CommandTimeoutError,
ConnectionPHY, ConnectionPHY,
InvalidStateError, InvalidStateError,
@@ -723,6 +724,7 @@ class Connection(CompositeEventEmitter):
connection_interval_max, connection_interval_max,
max_latency, max_latency,
supervision_timeout, supervision_timeout,
use_l2cap=False,
): ):
return await self.device.update_connection_parameters( return await self.device.update_connection_parameters(
self, self,
@@ -730,6 +732,7 @@ class Connection(CompositeEventEmitter):
connection_interval_max, connection_interval_max,
max_latency, max_latency,
supervision_timeout, supervision_timeout,
use_l2cap=use_l2cap,
) )
async def set_phy(self, tx_phys=None, rx_phys=None, phy_options=None): async def set_phy(self, tx_phys=None, rx_phys=None, phy_options=None):
@@ -2110,11 +2113,30 @@ class Device(CompositeEventEmitter):
supervision_timeout, supervision_timeout,
min_ce_length=0, min_ce_length=0,
max_ce_length=0, max_ce_length=0,
): use_l2cap=False,
) -> None:
''' '''
NOTE: the name of the parameters may look odd, but it just follows the names NOTE: the name of the parameters may look odd, but it just follows the names
used in the Bluetooth spec. used in the Bluetooth spec.
''' '''
if use_l2cap:
if connection.role != BT_PERIPHERAL_ROLE:
raise InvalidStateError(
'only peripheral can update connection parameters with l2cap'
)
l2cap_result = (
await self.l2cap_channel_manager.update_connection_parameters(
connection,
connection_interval_min,
connection_interval_max,
max_latency,
supervision_timeout,
)
)
if l2cap_result != l2cap.L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT:
raise ConnectionParameterUpdateError(l2cap_result)
result = await self.send_command( result = await self.send_command(
HCI_LE_Connection_Update_Command( HCI_LE_Connection_Update_Command(
connection_handle=connection.handle, connection_handle=connection.handle,
@@ -2124,7 +2146,7 @@ class Device(CompositeEventEmitter):
supervision_timeout=supervision_timeout, supervision_timeout=supervision_timeout,
min_ce_length=min_ce_length, min_ce_length=min_ce_length,
max_ce_length=max_ce_length, max_ce_length=max_ce_length,
) ) # type: ignore[call-arg]
) )
if result.status != HCI_Command_Status_Event.PENDING: if result.status != HCI_Command_Status_Event.PENDING:
raise HCI_StatusError(result) raise HCI_StatusError(result)

View File

@@ -1387,6 +1387,7 @@ class ChannelManager:
le_coc_requests: Dict[int, L2CAP_LE_Credit_Based_Connection_Request] le_coc_requests: Dict[int, L2CAP_LE_Credit_Based_Connection_Request]
fixed_channels: Dict[int, Optional[Callable[[int, bytes], Any]]] fixed_channels: Dict[int, Optional[Callable[[int, bytes], Any]]]
_host: Optional[Host] _host: Optional[Host]
connection_parameters_update_response: Optional[asyncio.Future[int]]
def __init__( def __init__(
self, self,
@@ -1408,6 +1409,7 @@ class ChannelManager:
self.le_coc_requests = {} # LE CoC connection requests, by identifier self.le_coc_requests = {} # LE CoC connection requests, by identifier
self.extended_features = extended_features self.extended_features = extended_features
self.connectionless_mtu = connectionless_mtu self.connectionless_mtu = connectionless_mtu
self.connection_parameters_update_response = None
@property @property
def host(self) -> Host: def host(self) -> Host:
@@ -1865,11 +1867,45 @@ class ChannelManager:
), ),
) )
async def update_connection_parameters(
self,
connection: Connection,
interval_min: int,
interval_max: int,
latency: int,
timeout: int,
) -> int:
# Check that there isn't already a request pending
if self.connection_parameters_update_response:
raise InvalidStateError('request already pending')
self.connection_parameters_update_response = (
asyncio.get_running_loop().create_future()
)
self.send_control_frame(
connection,
L2CAP_LE_SIGNALING_CID,
L2CAP_Connection_Parameter_Update_Request(
interval_min=interval_min,
interval_max=interval_max,
latency=latency,
timeout=timeout,
),
)
return await self.connection_parameters_update_response
def on_l2cap_connection_parameter_update_response( def on_l2cap_connection_parameter_update_response(
self, connection: Connection, cid: int, response self, connection: Connection, cid: int, response
) -> None: ) -> None:
# TODO: check response if self.connection_parameters_update_response:
pass self.connection_parameters_update_response.set_result(response.result)
self.connection_parameters_update_response = None
else:
logger.warning(
color(
'received l2cap_connection_parameter_update_response without a pending request',
'red',
)
)
def on_l2cap_le_credit_based_connection_request( def on_l2cap_le_credit_based_connection_request(
self, connection: Connection, cid: int, request self, connection: Connection, cid: int, request