mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
Merge pull request #734 from khsiao-google/le_subrating
Support LE Subrating
This commit is contained in:
@@ -1269,6 +1269,56 @@ class Controller:
|
||||
)
|
||||
return bytes([HCI_SUCCESS]) + bd_addr
|
||||
|
||||
def on_hci_le_set_default_subrate_command(
|
||||
self, command: hci.HCI_LE_Set_Default_Subrate_Command
|
||||
):
|
||||
'''
|
||||
See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
|
||||
'''
|
||||
|
||||
if (
|
||||
command.subrate_max * (command.max_latency) > 500
|
||||
or command.subrate_max < command.subrate_min
|
||||
or command.continuation_number >= command.subrate_max
|
||||
):
|
||||
return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
|
||||
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_subrate_request_command(
|
||||
self, command: hci.HCI_LE_Subrate_Request_Command
|
||||
):
|
||||
'''
|
||||
See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
|
||||
'''
|
||||
if (
|
||||
command.subrate_max * (command.max_latency) > 500
|
||||
or command.continuation_number < command.continuation_number
|
||||
or command.subrate_max < command.subrate_min
|
||||
or command.continuation_number >= command.subrate_max
|
||||
):
|
||||
return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
|
||||
|
||||
self.send_hci_packet(
|
||||
hci.HCI_Command_Status_Event(
|
||||
status=hci.HCI_SUCCESS,
|
||||
num_hci_command_packets=1,
|
||||
command_opcode=command.op_code,
|
||||
)
|
||||
)
|
||||
|
||||
self.send_hci_packet(
|
||||
hci.HCI_LE_Subrate_Change_Event(
|
||||
status=hci.HCI_SUCCESS,
|
||||
connection_handle=command.connection_handle,
|
||||
subrate_factor=2,
|
||||
peripheral_latency=2,
|
||||
continuation_number=command.continuation_number,
|
||||
supervision_timeout=command.supervision_timeout,
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
def on_hci_le_set_event_mask_command(self, command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
|
||||
@@ -1815,3 +1865,11 @@ class Controller:
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
|
||||
'''
|
||||
return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
|
||||
|
||||
def on_hci_le_set_host_feature_command(
|
||||
self, _command: hci.HCI_LE_Set_Host_Feature_Command
|
||||
):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
@@ -1752,6 +1752,8 @@ class Connection(utils.CompositeEventEmitter):
|
||||
EVENT_CIS_REQUEST = "cis_request"
|
||||
EVENT_CIS_ESTABLISHMENT = "cis_establishment"
|
||||
EVENT_CIS_ESTABLISHMENT_FAILURE = "cis_establishment_failure"
|
||||
EVENT_LE_SUBRATE_CHANGE = "le_subrate_change"
|
||||
EVENT_LE_SUBRATE_CHANGE_FAILURE = "le_subrate_change_failure"
|
||||
|
||||
@utils.composite_listener
|
||||
class Listener:
|
||||
@@ -1787,6 +1789,12 @@ class Connection(utils.CompositeEventEmitter):
|
||||
connection_interval: float # Connection interval, in milliseconds. [LE only]
|
||||
peripheral_latency: int # Peripheral latency, in number of intervals. [LE only]
|
||||
supervision_timeout: float # Supervision timeout, in milliseconds.
|
||||
subrate_factor: int = (
|
||||
1 # See Bluetooth spec Vol 6, Part B - 4.5.1 Connection events
|
||||
)
|
||||
continuation_number: int = (
|
||||
0 # See Bluetooth spec Vol 6, Part B - 4.5.1 Connection events
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -2058,6 +2066,7 @@ class DeviceConfiguration:
|
||||
le_simultaneous_enabled: bool = False
|
||||
le_privacy_enabled: bool = False
|
||||
le_rpa_timeout: int = DEVICE_DEFAULT_LE_RPA_TIMEOUT
|
||||
le_subrate_enabled: bool = False
|
||||
classic_enabled: bool = False
|
||||
classic_sc_enabled: bool = True
|
||||
classic_ssp_enabled: bool = True
|
||||
@@ -2410,6 +2419,7 @@ class Device(utils.CompositeEventEmitter):
|
||||
self.le_privacy_enabled = config.le_privacy_enabled
|
||||
self.le_rpa_timeout = config.le_rpa_timeout
|
||||
self.le_rpa_periodic_update_task: Optional[asyncio.Task] = None
|
||||
self.le_subrate_enabled = config.le_subrate_enabled
|
||||
self.classic_enabled = config.classic_enabled
|
||||
self.cis_enabled = config.cis_enabled
|
||||
self.classic_sc_enabled = config.classic_sc_enabled
|
||||
@@ -2789,6 +2799,15 @@ class Device(utils.CompositeEventEmitter):
|
||||
check_result=True,
|
||||
)
|
||||
|
||||
if self.le_subrate_enabled:
|
||||
await self.send_command(
|
||||
hci.HCI_LE_Set_Host_Feature_Command(
|
||||
bit_number=hci.LeFeature.CONNECTION_SUBRATING_HOST_SUPPORT,
|
||||
bit_value=1,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
|
||||
if self.config.channel_sounding_enabled:
|
||||
await self.send_command(
|
||||
hci.HCI_LE_Set_Host_Feature_Command(
|
||||
@@ -6189,11 +6208,23 @@ class Device(utils.CompositeEventEmitter):
|
||||
f'{connection.peer_address} as {connection.role_name}, '
|
||||
f'{connection_parameters}'
|
||||
)
|
||||
connection.parameters = Connection.Parameters(
|
||||
connection_parameters.connection_interval * 1.25,
|
||||
connection_parameters.peripheral_latency,
|
||||
connection_parameters.supervision_timeout * 10.0,
|
||||
)
|
||||
if (
|
||||
connection.parameters.connection_interval
|
||||
!= connection_parameters.connection_interval * 1.25
|
||||
):
|
||||
connection.parameters = Connection.Parameters(
|
||||
connection_parameters.connection_interval * 1.25,
|
||||
connection_parameters.peripheral_latency,
|
||||
connection_parameters.supervision_timeout * 10.0,
|
||||
)
|
||||
else:
|
||||
connection.parameters = Connection.Parameters(
|
||||
connection_parameters.connection_interval * 1.25,
|
||||
connection_parameters.peripheral_latency,
|
||||
connection_parameters.supervision_timeout * 10.0,
|
||||
connection.parameters.subrate_factor,
|
||||
connection.parameters.continuation_number,
|
||||
)
|
||||
connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE)
|
||||
|
||||
@host_event_handler
|
||||
@@ -6226,6 +6257,25 @@ class Device(utils.CompositeEventEmitter):
|
||||
)
|
||||
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE_FAILURE, error)
|
||||
|
||||
@host_event_handler
|
||||
@with_connection_from_handle
|
||||
def on_le_subrate_change(
|
||||
self,
|
||||
connection: Connection,
|
||||
subrate_factor: int,
|
||||
peripheral_latency: int,
|
||||
continuation_number: int,
|
||||
supervision_timeout: int,
|
||||
):
|
||||
connection.parameters = Connection.Parameters(
|
||||
connection.parameters.connection_interval,
|
||||
peripheral_latency,
|
||||
supervision_timeout * 10.0,
|
||||
subrate_factor,
|
||||
continuation_number,
|
||||
)
|
||||
connection.emit(connection.EVENT_LE_SUBRATE_CHANGE)
|
||||
|
||||
@host_event_handler
|
||||
@with_connection_from_handle
|
||||
def on_connection_att_mtu_update(self, connection, att_mtu):
|
||||
|
||||
@@ -5315,6 +5315,37 @@ class HCI_LE_Set_Host_Feature_Command(HCI_Command):
|
||||
bit_value: int = field(metadata=metadata(1))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command
|
||||
@dataclasses.dataclass
|
||||
class HCI_LE_Set_Default_Subrate_Command(HCI_Command):
|
||||
'''
|
||||
See Bluetooth spec @ 7.8.123 LE Set Default Subrate command
|
||||
'''
|
||||
|
||||
subrate_min: int = field(metadata=metadata(2))
|
||||
subrate_max: int = field(metadata=metadata(2))
|
||||
max_latency: int = field(metadata=metadata(2))
|
||||
continuation_number: int = field(metadata=metadata(2))
|
||||
supervision_timeout: int = field(metadata=metadata(2))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command
|
||||
@dataclasses.dataclass
|
||||
class HCI_LE_Subrate_Request_Command(HCI_Command):
|
||||
'''
|
||||
See Bluetooth spec @ 7.8.124 LE Subrate Request command
|
||||
'''
|
||||
|
||||
connection_handle: int = field(metadata=metadata(2))
|
||||
subrate_min: int = field(metadata=metadata(2))
|
||||
subrate_max: int = field(metadata=metadata(2))
|
||||
max_latency: int = field(metadata=metadata(2))
|
||||
continuation_number: int = field(metadata=metadata(2))
|
||||
supervision_timeout: int = field(metadata=metadata(2))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command
|
||||
@dataclasses.dataclass
|
||||
@@ -6460,6 +6491,22 @@ class HCI_LE_BIGInfo_Advertising_Report_Event(HCI_LE_Meta_Event):
|
||||
encryption: int = field(metadata=metadata(1))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_LE_Meta_Event.event
|
||||
@dataclasses.dataclass
|
||||
class HCI_LE_Subrate_Change_Event(HCI_LE_Meta_Event):
|
||||
'''
|
||||
See Bluetooth spec @ 7.7.65.35 LE Subrate Change event
|
||||
'''
|
||||
|
||||
status: int = field(metadata=metadata(STATUS_SPEC))
|
||||
connection_handle: int = field(metadata=metadata(2))
|
||||
subrate_factor: int = field(metadata=metadata(2))
|
||||
peripheral_latency: int = field(metadata=metadata(2))
|
||||
continuation_number: int = field(metadata=metadata(2))
|
||||
supervision_timeout: int = field(metadata=metadata(2))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_LE_Meta_Event.event
|
||||
@dataclasses.dataclass
|
||||
|
||||
@@ -1645,5 +1645,15 @@ class Host(utils.EventEmitter):
|
||||
def on_hci_le_cs_subevent_result_continue_event(self, event):
|
||||
self.emit('cs_subevent_result_continue', event)
|
||||
|
||||
def on_hci_le_subrate_change_event(self, event: hci.HCI_LE_Subrate_Change_Event):
|
||||
self.emit(
|
||||
'le_subrate_change',
|
||||
event.connection_handle,
|
||||
event.subrate_factor,
|
||||
event.peripheral_latency,
|
||||
event.continuation_number,
|
||||
event.supervision_timeout,
|
||||
)
|
||||
|
||||
def on_hci_vendor_event(self, event):
|
||||
self.emit('vendor_event', event)
|
||||
|
||||
@@ -611,6 +611,37 @@ async def test_enter_and_exit_sniff_mode():
|
||||
assert devices.connections[0].classic_interval == 2
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_le_request_subrate():
|
||||
devices = TwoDevices()
|
||||
await devices.setup_connection()
|
||||
|
||||
q = asyncio.Queue()
|
||||
|
||||
def on_le_subrate_change():
|
||||
q.put_nowait(lambda: None)
|
||||
|
||||
devices.connections[0].on(Connection.EVENT_LE_SUBRATE_CHANGE, on_le_subrate_change)
|
||||
|
||||
await devices[0].send_command(
|
||||
hci.HCI_LE_Subrate_Request_Command(
|
||||
connection_handle=devices.connections[0].handle,
|
||||
subrate_min=2,
|
||||
subrate_max=2,
|
||||
max_latency=2,
|
||||
continuation_number=1,
|
||||
supervision_timeout=2,
|
||||
)
|
||||
)
|
||||
|
||||
await asyncio.wait_for(q.get(), _TIMEOUT)
|
||||
assert devices.connections[0].parameters.subrate_factor == 2
|
||||
assert devices.connections[0].parameters.peripheral_latency == 2
|
||||
assert devices.connections[0].parameters.continuation_number == 1
|
||||
assert devices.connections[0].parameters.supervision_timeout == 20
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@pytest.mark.asyncio
|
||||
async def test_power_on_default_static_address_should_not_be_any():
|
||||
|
||||
Reference in New Issue
Block a user