mirror of
https://github.com/google/bumble.git
synced 2026-05-08 03:58:01 +00:00
Support LE Subrating
This commit is contained in:
@@ -1259,6 +1259,56 @@ class Controller:
|
|||||||
)
|
)
|
||||||
return bytes([HCI_SUCCESS]) + bd_addr
|
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):
|
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
|
See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
|
||||||
|
|||||||
@@ -1752,6 +1752,8 @@ class Connection(utils.CompositeEventEmitter):
|
|||||||
EVENT_CIS_REQUEST = "cis_request"
|
EVENT_CIS_REQUEST = "cis_request"
|
||||||
EVENT_CIS_ESTABLISHMENT = "cis_establishment"
|
EVENT_CIS_ESTABLISHMENT = "cis_establishment"
|
||||||
EVENT_CIS_ESTABLISHMENT_FAILURE = "cis_establishment_failure"
|
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
|
@utils.composite_listener
|
||||||
class Listener:
|
class Listener:
|
||||||
@@ -1787,6 +1789,8 @@ class Connection(utils.CompositeEventEmitter):
|
|||||||
connection_interval: float # Connection interval, in milliseconds. [LE only]
|
connection_interval: float # Connection interval, in milliseconds. [LE only]
|
||||||
peripheral_latency: int # Peripheral latency, in number of intervals. [LE only]
|
peripheral_latency: int # Peripheral latency, in number of intervals. [LE only]
|
||||||
supervision_timeout: float # Supervision timeout, in milliseconds.
|
supervision_timeout: float # Supervision timeout, in milliseconds.
|
||||||
|
subrate_factor: int = 1
|
||||||
|
continuation_number: int = 0
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -2058,6 +2062,7 @@ class DeviceConfiguration:
|
|||||||
le_simultaneous_enabled: bool = False
|
le_simultaneous_enabled: bool = False
|
||||||
le_privacy_enabled: bool = False
|
le_privacy_enabled: bool = False
|
||||||
le_rpa_timeout: int = DEVICE_DEFAULT_LE_RPA_TIMEOUT
|
le_rpa_timeout: int = DEVICE_DEFAULT_LE_RPA_TIMEOUT
|
||||||
|
le_subrate_enabled: bool = True
|
||||||
classic_enabled: bool = False
|
classic_enabled: bool = False
|
||||||
classic_sc_enabled: bool = True
|
classic_sc_enabled: bool = True
|
||||||
classic_ssp_enabled: bool = True
|
classic_ssp_enabled: bool = True
|
||||||
@@ -2410,6 +2415,7 @@ class Device(utils.CompositeEventEmitter):
|
|||||||
self.le_privacy_enabled = config.le_privacy_enabled
|
self.le_privacy_enabled = config.le_privacy_enabled
|
||||||
self.le_rpa_timeout = config.le_rpa_timeout
|
self.le_rpa_timeout = config.le_rpa_timeout
|
||||||
self.le_rpa_periodic_update_task: Optional[asyncio.Task] = None
|
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.classic_enabled = config.classic_enabled
|
||||||
self.cis_enabled = config.cis_enabled
|
self.cis_enabled = config.cis_enabled
|
||||||
self.classic_sc_enabled = config.classic_sc_enabled
|
self.classic_sc_enabled = config.classic_sc_enabled
|
||||||
@@ -6226,6 +6232,22 @@ class Device(utils.CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE_FAILURE, error)
|
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.subrate_factor = subrate_factor
|
||||||
|
connection.parameters.peripheral_latency = peripheral_latency
|
||||||
|
connection.parameters.continuation_number = continuation_number
|
||||||
|
connection.parameters.supervision_timeout = supervision_timeout * 10
|
||||||
|
connection.emit(connection.EVENT_LE_SUBRATE_CHANGE)
|
||||||
|
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
@with_connection_from_handle
|
@with_connection_from_handle
|
||||||
def on_connection_att_mtu_update(self, connection, att_mtu):
|
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))
|
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
|
@HCI_Command.command
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
@@ -6460,6 +6491,22 @@ class HCI_LE_BIGInfo_Advertising_Report_Event(HCI_LE_Meta_Event):
|
|||||||
encryption: int = field(metadata=metadata(1))
|
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(1))
|
||||||
|
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
|
@HCI_LE_Meta_Event.event
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
|
|||||||
@@ -1645,5 +1645,15 @@ class Host(utils.EventEmitter):
|
|||||||
def on_hci_le_cs_subevent_result_continue_event(self, event):
|
def on_hci_le_cs_subevent_result_continue_event(self, event):
|
||||||
self.emit('cs_subevent_result_continue', 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):
|
def on_hci_vendor_event(self, event):
|
||||||
self.emit('vendor_event', 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
|
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
|
@pytest.mark.asyncio
|
||||||
async def test_power_on_default_static_address_should_not_be_any():
|
async def test_power_on_default_static_address_should_not_be_any():
|
||||||
|
|||||||
Reference in New Issue
Block a user