407 lines
13 KiB
Python
407 lines
13 KiB
Python
# Copyright 2021-2022 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Imports
|
|
# -----------------------------------------------------------------------------
|
|
from bumble.hci import *
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def basic_check(x):
|
|
packet = x.to_bytes()
|
|
print(packet.hex())
|
|
parsed = HCI_Packet.from_bytes(packet)
|
|
x_str = str(x)
|
|
parsed_str = str(parsed)
|
|
print(x_str)
|
|
parsed_bytes = parsed.to_bytes()
|
|
assert(x_str == parsed_str)
|
|
assert(packet == parsed_bytes)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Event():
|
|
event = HCI_Event(0xF9)
|
|
basic_check(event)
|
|
|
|
event = HCI_Event(0xF8, bytes.fromhex('AABBCC'))
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Connection_Complete_Event():
|
|
address = Address('00:11:22:33:44:55')
|
|
event = HCI_LE_Connection_Complete_Event(
|
|
status = HCI_SUCCESS,
|
|
connection_handle = 1,
|
|
role = 1,
|
|
peer_address_type = 1,
|
|
peer_address = address,
|
|
conn_interval = 3,
|
|
conn_latency = 4,
|
|
supervision_timeout = 5,
|
|
master_clock_accuracy = 6
|
|
)
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Advertising_Report_Event():
|
|
address = Address('00:11:22:33:44:55')
|
|
report = HCI_Object(
|
|
HCI_LE_Advertising_Report_Event.REPORT_FIELDS,
|
|
event_type = HCI_LE_Advertising_Report_Event.ADV_IND,
|
|
address_type = Address.PUBLIC_DEVICE_ADDRESS,
|
|
address = address,
|
|
data = bytes.fromhex('0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03'),
|
|
rssi = 100
|
|
)
|
|
event = HCI_LE_Advertising_Report_Event([report])
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Read_Remote_Features_Complete_Event():
|
|
event = HCI_LE_Read_Remote_Features_Complete_Event(
|
|
status = HCI_SUCCESS,
|
|
connection_handle = 0x007,
|
|
le_features = bytes.fromhex('0011223344556677')
|
|
)
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Connection_Update_Complete_Event():
|
|
event = HCI_LE_Connection_Update_Complete_Event(
|
|
status = HCI_SUCCESS,
|
|
connection_handle = 0x007,
|
|
conn_interval = 10,
|
|
conn_latency = 3,
|
|
supervision_timeout = 5
|
|
)
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Channel_Selection_Algorithm_Event():
|
|
event = HCI_LE_Channel_Selection_Algorithm_Event(
|
|
connection_handle = 7,
|
|
channel_selection_algorithm = 1
|
|
)
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Command_Complete_Event():
|
|
# With a serializable object
|
|
event = HCI_Command_Complete_Event(
|
|
num_hci_command_packets = 34,
|
|
command_opcode = HCI_LE_READ_BUFFER_SIZE_COMMAND,
|
|
return_parameters = HCI_LE_Read_Buffer_Size_Command.create_return_parameters(
|
|
status = 0,
|
|
hc_le_acl_data_packet_length = 1234,
|
|
hc_total_num_le_acl_data_packets = 56
|
|
)
|
|
)
|
|
basic_check(event)
|
|
|
|
# With an arbitrary byte array
|
|
event = HCI_Command_Complete_Event(
|
|
num_hci_command_packets = 1,
|
|
command_opcode = HCI_RESET_COMMAND,
|
|
return_parameters = bytes([1, 2, 3, 4])
|
|
)
|
|
basic_check(event)
|
|
|
|
# With a simple status as a 1-byte array
|
|
event = HCI_Command_Complete_Event(
|
|
num_hci_command_packets = 1,
|
|
command_opcode = HCI_RESET_COMMAND,
|
|
return_parameters = bytes([7])
|
|
)
|
|
basic_check(event)
|
|
event = HCI_Packet.from_bytes(event.to_bytes())
|
|
assert(event.return_parameters == 7)
|
|
|
|
# With a simple status as an integer status
|
|
event = HCI_Command_Complete_Event(
|
|
num_hci_command_packets = 1,
|
|
command_opcode = HCI_RESET_COMMAND,
|
|
return_parameters = 9
|
|
)
|
|
basic_check(event)
|
|
assert(event.return_parameters == 9)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Command_Status_Event():
|
|
event = HCI_Command_Status_Event(
|
|
status = 0,
|
|
num_hci_command_packets = 37,
|
|
command_opcode = HCI_DISCONNECT_COMMAND
|
|
)
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Number_Of_Completed_Packets_Event():
|
|
event = HCI_Number_Of_Completed_Packets_Event([
|
|
(1, 2),
|
|
(3, 4)
|
|
])
|
|
basic_check(event)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Command():
|
|
command = HCI_Command(0x5566)
|
|
basic_check(command)
|
|
|
|
command = HCI_Command(0x5566, bytes.fromhex('AABBCC'))
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Reset_Command():
|
|
command = HCI_Reset_Command()
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Read_Local_Version_Information_Command():
|
|
command = HCI_Read_Local_Version_Information_Command()
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Read_Local_Supported_Commands_Command():
|
|
command = HCI_Read_Local_Supported_Commands_Command()
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Read_Local_Supported_Features_Command():
|
|
command = HCI_Read_Local_Supported_Features_Command()
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Disconnect_Command():
|
|
command = HCI_Disconnect_Command(
|
|
connection_handle = 123,
|
|
reason = 0x11
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_Set_Event_Mask_Command():
|
|
command = HCI_Set_Event_Mask_Command(
|
|
event_mask = bytes.fromhex('0011223344556677')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Event_Mask_Command():
|
|
command = HCI_LE_Set_Event_Mask_Command(
|
|
le_event_mask = bytes.fromhex('0011223344556677')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Random_Address_Command():
|
|
command = HCI_LE_Set_Random_Address_Command(
|
|
random_address = Address('00:11:22:33:44:55')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Advertising_Parameters_Command():
|
|
command = HCI_LE_Set_Advertising_Parameters_Command(
|
|
advertising_interval_min = 20,
|
|
advertising_interval_max = 30,
|
|
advertising_type = HCI_LE_Set_Advertising_Parameters_Command.ADV_NONCONN_IND,
|
|
own_address_type = Address.PUBLIC_DEVICE_ADDRESS,
|
|
peer_address_type = Address.RANDOM_DEVICE_ADDRESS,
|
|
peer_address = Address('00:11:22:33:44:55'),
|
|
advertising_channel_map = 0x03,
|
|
advertising_filter_policy = 1
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Advertising_Data_Command():
|
|
command = HCI_LE_Set_Advertising_Data_Command(
|
|
advertising_data = bytes.fromhex('AABBCC')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Scan_Parameters_Command():
|
|
command = HCI_LE_Set_Scan_Parameters_Command(
|
|
le_scan_type = 1,
|
|
le_scan_interval = 20,
|
|
le_scan_window = 10,
|
|
own_address_type = 1,
|
|
scanning_filter_policy = 0
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Scan_Enable_Command():
|
|
command = HCI_LE_Set_Scan_Enable_Command(
|
|
le_scan_enable = 1,
|
|
filter_duplicates = 0
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Create_Connection_Command():
|
|
command = HCI_LE_Create_Connection_Command(
|
|
le_scan_interval = 4,
|
|
le_scan_window = 5,
|
|
initiator_filter_policy = 1,
|
|
peer_address_type = 1,
|
|
peer_address = Address('00:11:22:33:44:55'),
|
|
own_address_type = 2,
|
|
conn_interval_min = 7,
|
|
conn_interval_max = 8,
|
|
conn_latency = 9,
|
|
supervision_timeout = 10,
|
|
minimum_ce_length = 11,
|
|
maximum_ce_length = 12
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Add_Device_To_White_List_Command():
|
|
command = HCI_LE_Add_Device_To_White_List_Command(
|
|
address_type = 1,
|
|
address = Address('00:11:22:33:44:55')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Remove_Device_From_White_List_Command():
|
|
command = HCI_LE_Remove_Device_From_White_List_Command(
|
|
address_type = 1,
|
|
address = Address('00:11:22:33:44:55')
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Connection_Update_Command():
|
|
command = HCI_LE_Connection_Update_Command(
|
|
connection_handle = 0x0002,
|
|
conn_interval_min = 10,
|
|
conn_interval_max = 20,
|
|
conn_latency = 7,
|
|
supervision_timeout = 3,
|
|
minimum_ce_length = 100,
|
|
maximum_ce_length = 200
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Read_Remote_Features_Command():
|
|
command = HCI_LE_Read_Remote_Features_Command(
|
|
connection_handle = 0x0002
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_HCI_LE_Set_Default_PHY_Command():
|
|
command = HCI_LE_Set_Default_PHY_Command(
|
|
all_phys = 0,
|
|
tx_phys = 1,
|
|
rx_phys = 1
|
|
)
|
|
basic_check(command)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_address():
|
|
a = Address('C4:F2:17:1A:1D:BB')
|
|
assert(not a.is_public)
|
|
assert(a.is_random)
|
|
assert(a.address_type == Address.RANDOM_DEVICE_ADDRESS)
|
|
assert(not a.is_resolvable)
|
|
assert(not a.is_resolved)
|
|
assert(a.is_static)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_custom():
|
|
data = bytes([0x77, 0x02, 0x01, 0x03])
|
|
packet = HCI_CustomPacket(data)
|
|
assert(packet.hci_packet_type == 0x77)
|
|
assert(packet.payload == data)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def run_test_events():
|
|
test_HCI_Event()
|
|
test_HCI_LE_Connection_Complete_Event()
|
|
test_HCI_LE_Advertising_Report_Event()
|
|
test_HCI_LE_Connection_Update_Complete_Event()
|
|
test_HCI_LE_Read_Remote_Features_Complete_Event()
|
|
test_HCI_LE_Channel_Selection_Algorithm_Event()
|
|
test_HCI_Command_Complete_Event()
|
|
test_HCI_Command_Status_Event()
|
|
test_HCI_Number_Of_Completed_Packets_Event()
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def run_test_commands():
|
|
test_HCI_Command()
|
|
test_HCI_Reset_Command()
|
|
test_HCI_Read_Local_Version_Information_Command()
|
|
test_HCI_Read_Local_Supported_Commands_Command()
|
|
test_HCI_Read_Local_Supported_Features_Command()
|
|
test_HCI_Disconnect_Command()
|
|
test_HCI_Set_Event_Mask_Command()
|
|
test_HCI_LE_Set_Event_Mask_Command()
|
|
test_HCI_LE_Set_Random_Address_Command()
|
|
test_HCI_LE_Set_Advertising_Parameters_Command()
|
|
test_HCI_LE_Set_Advertising_Data_Command()
|
|
test_HCI_LE_Set_Scan_Parameters_Command()
|
|
test_HCI_LE_Set_Scan_Enable_Command()
|
|
test_HCI_LE_Create_Connection_Command()
|
|
test_HCI_LE_Add_Device_To_White_List_Command()
|
|
test_HCI_LE_Remove_Device_From_White_List_Command()
|
|
test_HCI_LE_Connection_Update_Command()
|
|
test_HCI_LE_Read_Remote_Features_Command()
|
|
test_HCI_LE_Set_Default_PHY_Command()
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
if __name__ == '__main__':
|
|
run_test_events()
|
|
run_test_commands()
|
|
test_address()
|
|
test_custom()
|