Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/hci_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ def host_to_controller_filter(hci_packet):
response = hci.HCI_Command_Complete_Event(
num_hci_command_packets=1,
command_opcode=hci_packet.op_code,
return_parameters=bytes([hci.HCI_SUCCESS]),
return_parameters=hci.HCI_StatusReturnParameters(
status=hci.HCI_ErrorCode.SUCCESS
),
)
# Return a packet with 'respond to sender' set to True
return (bytes(response), True)
Expand Down
12 changes: 10 additions & 2 deletions bumble/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ def __init__(self, hci_sink, sender_hci_sink, packet_filter, trace):

def on_packet(self, packet):
# Convert the packet bytes to an object
hci_packet = HCI_Packet.from_bytes(packet)
try:
hci_packet = HCI_Packet.from_bytes(packet)
except Exception:
logger.warning('forwarding unparsed packet as-is')
self.hci_sink.on_packet(packet)
return

# Filter the packet
if self.packet_filter is not None:
Expand All @@ -50,7 +55,10 @@ def on_packet(self, packet):
return

# Analyze the packet
self.trace(hci_packet)
try:
self.trace(hci_packet)
except Exception:
logger.exception('Exception while tracing packet')

# Bridge the packet
self.hci_sink.on_packet(packet)
Expand Down
67 changes: 39 additions & 28 deletions bumble/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -1177,7 +1177,7 @@ class ChannelSoundingCapabilities:
rtt_capability: int
rtt_aa_only_n: int
rtt_sounding_n: int
rtt_random_payload_n: int
rtt_random_sequence_n: int
nadm_sounding_capability: int
nadm_random_capability: int
cs_sync_phys_supported: int
Expand Down Expand Up @@ -2763,24 +2763,39 @@ async def send_command(
logger.warning(f'!!! Command {command.name} timed out')
raise CommandTimeoutError() from error

async def send_sync_command(
self, command: hci.HCI_SyncCommand[_RP], check_status: bool = True
) -> _RP:
async def send_sync_command(self, command: hci.HCI_SyncCommand[_RP]) -> _RP:
'''
Send a synchronous command via the host.

If the `status` field of the response's `return_parameters` is not equal to
`SUCCESS` an exception is raised.

Params:
command: the command to send.
check_status: If `True`, check the `status` field of the response's
`return_parameters` and raise and exception if not equal to `SUCCESS`.

Returns:
An instance of the return parameters class associated with the command class.
'''
try:
return await self.host.send_sync_command(
command, check_status, self.command_timeout
)
return await self.host.send_sync_command(command, self.command_timeout)
except asyncio.TimeoutError as error:
logger.warning(f'!!! Command {command.name} timed out')
raise CommandTimeoutError() from error

async def send_sync_command_raw(
self, command: hci.HCI_SyncCommand[_RP]
) -> hci.HCI_Command_Complete_Event[_RP]:
'''
Send a synchronous command via the host without checking the response.

Params:
command: the command to send.

Returns:
An HCI_Command_Complete_Event instance.
'''
try:
return await self.host.send_sync_command_raw(command, self.command_timeout)
except asyncio.TimeoutError as error:
logger.warning(f'!!! Command {command.name} timed out')
raise CommandTimeoutError() from error
Expand All @@ -2797,7 +2812,7 @@ async def send_async_command(
raise and exception if not equal to `PENDING`.

Returns:
An instance of the return parameters class associated with the command class.
A status code.
'''
try:
return await self.host.send_async_command(
Expand All @@ -2812,12 +2827,12 @@ async def power_on(self) -> None:
await self.host.reset()

# Try to get the public address from the controller
response = await self.host.send_sync_command(
hci.HCI_Read_BD_ADDR_Command(), check_status=False
)
if response.status == hci.HCI_SUCCESS:
try:
response = await self.host.send_sync_command(hci.HCI_Read_BD_ADDR_Command())
logger.debug(color(f'BD_ADDR: {response.bd_addr}', 'yellow'))
self.public_address = response.bd_addr
except hci.HCI_Error:
logger.debug('Controller has no public address')

# Instantiate the Key Store (we do this here rather than at __init__ time
# because some Key Store implementations use the public address as a namespace)
Expand Down Expand Up @@ -2926,7 +2941,7 @@ async def power_on(self) -> None:
rtt_capability=result.rtt_capability,
rtt_aa_only_n=result.rtt_aa_only_n,
rtt_sounding_n=result.rtt_sounding_n,
rtt_random_payload_n=result.rtt_random_payload_n,
rtt_random_sequence_n=result.rtt_random_sequence_n,
nadm_sounding_capability=result.nadm_sounding_capability,
nadm_random_capability=result.nadm_random_capability,
cs_sync_phys_supported=result.cs_sync_phys_supported,
Expand Down Expand Up @@ -2954,27 +2969,23 @@ async def power_on(self) -> None:
)

if self.classic_enabled:
await self.send_sync_command(
hci.HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8')),
check_status=False,
await self.send_sync_command_raw(
hci.HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8'))
)
await self.send_sync_command(
await self.send_sync_command_raw(
hci.HCI_Write_Class_Of_Device_Command(
class_of_device=self.class_of_device
),
check_status=False,
)
)
await self.send_sync_command(
await self.send_sync_command_raw(
hci.HCI_Write_Simple_Pairing_Mode_Command(
simple_pairing_mode=int(self.classic_ssp_enabled)
),
check_status=False,
)
)
await self.send_sync_command(
await self.send_sync_command_raw(
hci.HCI_Write_Secure_Connections_Host_Support_Command(
secure_connections_host_support=int(self.classic_sc_enabled)
),
check_status=False,
)
)
await self.set_connectable(self.connectable)
await self.set_discoverable(self.discoverable)
Expand Down Expand Up @@ -6673,7 +6684,7 @@ def on_cs_remote_supported_capabilities(
rtt_capability=event.rtt_capability,
rtt_aa_only_n=event.rtt_aa_only_n,
rtt_sounding_n=event.rtt_sounding_n,
rtt_random_payload_n=event.rtt_random_payload_n,
rtt_random_sequence_n=event.rtt_random_sequence_n,
nadm_sounding_capability=event.nadm_sounding_capability,
nadm_random_capability=event.nadm_random_capability,
cs_sync_phys_supported=event.cs_sync_phys_supported,
Expand Down
24 changes: 16 additions & 8 deletions bumble/drivers/intel.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,13 @@ async def reboot_bootloader(self) -> None:

async def read_device_info(self) -> dict[ValueType, Any]:
self.host.ready = True
response1 = await self.host.send_sync_command(
hci.HCI_Reset_Command(), check_status=False
)
if response1.status not in (hci.HCI_UNKNOWN_HCI_COMMAND_ERROR, hci.HCI_SUCCESS):
response1 = await self.host.send_sync_command_raw(hci.HCI_Reset_Command())
if not isinstance(
response1.return_parameters, hci.HCI_StatusReturnParameters
) or response1.return_parameters.status not in (
hci.HCI_UNKNOWN_HCI_COMMAND_ERROR,
hci.HCI_SUCCESS,
):
# When the controller is in operational mode, the response is a
# successful response.
# When the controller is in bootloader mode,
Expand All @@ -676,13 +679,18 @@ async def read_device_info(self) -> dict[ValueType, Any]:
raise DriverError("unexpected HCI response")

# Read the firmware version.
response2 = await self.host.send_sync_command(
HCI_Intel_Read_Version_Command(param0=0xFF), check_status=False
response2 = await self.host.send_sync_command_raw(
HCI_Intel_Read_Version_Command(param0=0xFF)
)
if response2.status != 0: # type: ignore
if (
not isinstance(
response2.return_parameters, HCI_Intel_Read_Version_ReturnParameters
)
or response2.return_parameters.status != 0
):
raise DriverError("HCI_Intel_Read_Version_Command error")

tlvs = _parse_tlv(response2.tlv) # type: ignore
tlvs = _parse_tlv(response2.return_parameters.tlv) # type: ignore

# Convert the list to a dict. That's Ok here because we only expect each type
# to appear just once.
Expand Down
56 changes: 38 additions & 18 deletions bumble/drivers/rtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,13 @@ def check(host: Host) -> bool:

@staticmethod
async def get_loaded_firmware_version(host: Host) -> int | None:
response1 = await host.send_sync_command(
HCI_RTK_Read_ROM_Version_Command(), check_status=False
)

if response1.status != hci.HCI_SUCCESS:
response1 = await host.send_sync_command_raw(HCI_RTK_Read_ROM_Version_Command())
if (
not isinstance(
response1.return_parameters, HCI_RTK_Read_ROM_Version_ReturnParameters
)
or response1.return_parameters.status != hci.HCI_SUCCESS
):
return None

response2 = await host.send_sync_command(
Expand All @@ -559,13 +561,20 @@ async def driver_info_for_host(cls, host: Host) -> DriverInfo | None:
await host.send_sync_command(hci.HCI_Reset_Command())
host.ready = True

command = hci.HCI_Read_Local_Version_Information_Command()
response = await host.send_sync_command(command, check_status=False)
if response.status != hci.HCI_SUCCESS:
response = await host.send_sync_command_raw(
hci.HCI_Read_Local_Version_Information_Command()
)
if (
not isinstance(
response.return_parameters,
hci.HCI_Read_Local_Version_Information_ReturnParameters,
)
or response.return_parameters.status != hci.HCI_SUCCESS
):
logger.error("failed to probe local version information")
return None

local_version = response
local_version = response.return_parameters

logger.debug(
f"looking for a driver: 0x{local_version.lmp_subversion:04X} "
Expand Down Expand Up @@ -641,15 +650,21 @@ async def download_for_rtl8723a(self):

# TODO: load the firmware

async def download_for_rtl8723b(self):
async def download_for_rtl8723b(self) -> int | None:
if self.driver_info.has_rom_version:
response1 = await self.host.send_sync_command(
HCI_RTK_Read_ROM_Version_Command(), check_status=False
response1 = await self.host.send_sync_command_raw(
HCI_RTK_Read_ROM_Version_Command()
)
if response1.status != hci.HCI_SUCCESS:
if (
not isinstance(
response1.return_parameters,
HCI_RTK_Read_ROM_Version_ReturnParameters,
)
or response1.return_parameters.status != hci.HCI_SUCCESS
):
logger.warning("can't get ROM version")
return None
rom_version = response1.version
rom_version = response1.return_parameters.version
logger.debug(f"ROM version before download: {rom_version:04X}")
else:
rom_version = 0
Expand Down Expand Up @@ -691,13 +706,18 @@ async def download_for_rtl8723b(self):
logger.debug("download complete!")

# Read the version again
response2 = await self.host.send_sync_command(
HCI_RTK_Read_ROM_Version_Command(), check_status=False
response2 = await self.host.send_sync_command_raw(
HCI_RTK_Read_ROM_Version_Command()
)
if response2.status != hci.HCI_SUCCESS:
if (
not isinstance(
response2.return_parameters, HCI_RTK_Read_ROM_Version_ReturnParameters
)
or response2.return_parameters.status != hci.HCI_SUCCESS
):
logger.warning("can't get ROM version")
else:
rom_version = response2.version
rom_version = response2.return_parameters.version
logger.debug(f"ROM version after download: {rom_version:02X}")

return firmware.version
Expand Down
Loading
Loading