diff --git a/bumble/hci.py b/bumble/hci.py index af39976c..1d0cd8ef 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -267,6 +267,19 @@ HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_V2_EVENT = 0X26 HCI_LE_PERIODIC_ADVERTISING_SUBEVENT_DATA_REQUEST_EVENT = 0X27 HCI_LE_PERIODIC_ADVERTISING_RESPONSE_REPORT_EVENT = 0X28 HCI_LE_ENHANCED_CONNECTION_COMPLETE_V2_EVENT = 0X29 +HCI_LE_READ_ALL_REMOTE_FEATURES_COMPLETE_EVENT = 0x2A +HCI_LE_CIS_ESTABLISHED_V2_EVENT = 0x2B +HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE_EVENT = 0x2C +HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE_EVENT = 0x2D +HCI_LE_CS_SECURITY_ENABLE_COMPLETE_EVENT = 0x2E +HCI_LE_CS_CONFIG_COMPLETE_EVENT = 0x2F +HCI_LE_CS_PROCEDURE_ENABLE_EVENT = 0x30 +HCI_LE_CS_SUBEVENT_RESULT_EVENT = 0x31 +HCI_LE_CS_SUBEVENT_RESULT_CONTINUE_EVENT = 0x32 +HCI_LE_CS_TEST_END_COMPLETE_EVENT = 0x33 +HCI_LE_MONITORED_ADVERTISERS_REPORT_EVENT = 0x34 +HCI_LE_FRAME_SPACE_UPDATE_EVENT = 0x35 + # HCI Command @@ -573,11 +586,36 @@ HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND = hci_c HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D) HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E) HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x007F) +HCI_LE_SET_DECISION_DATA_COMMAND = hci_command_op_code(0x08, 0x0080) +HCI_LE_SET_DECISION_INSTRUCTIONS_COMMAND = hci_command_op_code(0x08, 0x0081) HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND = hci_command_op_code(0x08, 0x0082) HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0083) HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND = hci_command_op_code(0x08, 0x0084) HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND = hci_command_op_code(0x08, 0x0085) HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x0086) +HCI_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0087) +HCI_LE_READ_ALL_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0088) +HCI_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x0089) +HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x008A) +HCI_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES = hci_command_op_code(0x08, 0x008B) +HCI_LE_CS_SECURITY_ENABLE_COMMAND = hci_command_op_code(0x08, 0x008C) +HCI_LE_CS_SET_DEFAULT_SETTINGS_COMMAND = hci_command_op_code(0x08, 0x008D) +HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008E) +HCI_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008F) +HCI_LE_CS_CREATE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0090) +HCI_LE_CS_REMOVE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0091) +HCI_LE_CS_SET_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0092) +HCI_LE_CS_SET_PROCEDURE_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0093) +HCI_LE_CS_PROCEDURE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0094) +HCI_LE_CS_TEST_COMMAND = hci_command_op_code(0x08, 0x0095) +HCI_LE_CS_TEST_END_COMMAND = hci_command_op_code(0x08, 0x0096) +HCI_LE_SET_HOST_FEATURE_V2_COMMAND = hci_command_op_code(0x08, 0x0097) +HCI_LE_ADD_DEVICE_TO_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0098) +HCI_LE_REMOVE_DEVICE_FROM_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0099) +HCI_LE_CLEAR_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x009A) +HCI_LE_READ_MONITORED_ADVERTISERS_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x009B) +HCI_LE_ENABLE_MONITORING_ADVERTISERS_COMMAND = hci_command_op_code(0x08, 0x009C) +HCI_LE_FRAME_SPACE_UPDATE_COMMAND = hci_command_op_code(0x08, 0x009D) # HCI Error Codes @@ -1150,8 +1188,16 @@ class LeFeature(OpenIntEnum): CHANNEL_CLASSIFICATION = 39 ADVERTISING_CODING_SELECTION = 40 ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 41 + DECISION_BASED_ADVERTISING_FILTERING = 42 PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 43 PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 44 + UNSEGMENTED_FRAMED_MODE = 45 + CHANNEL_SOUNDING = 46 + CHANNEL_SOUNDING_HOST_SUPPORT = 47 + CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 48 + LL_EXTENDED_FEATURE_SET = 63 + MONITORING_ADVERTISERS = 64 + FRAME_SPACE_UPDATE = 65 class LeFeatureMask(enum.IntFlag): LE_ENCRYPTION = 1 << LeFeature.LE_ENCRYPTION @@ -1196,8 +1242,16 @@ class LeFeatureMask(enum.IntFlag): CHANNEL_CLASSIFICATION = 1 << LeFeature.CHANNEL_CLASSIFICATION ADVERTISING_CODING_SELECTION = 1 << LeFeature.ADVERTISING_CODING_SELECTION ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 1 << LeFeature.ADVERTISING_CODING_SELECTION_HOST_SUPPORT + DECISION_BASED_ADVERTISING_FILTERING = 1 << LeFeature.DECISION_BASED_ADVERTISING_FILTERING PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER + UNSEGMENTED_FRAMED_MODE = 1 << LeFeature.UNSEGMENTED_FRAMED_MODE + CHANNEL_SOUNDING = 1 << LeFeature.CHANNEL_SOUNDING + CHANNEL_SOUNDING_HOST_SUPPORT = 1 << LeFeature.CHANNEL_SOUNDING_HOST_SUPPORT + CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 1 << LeFeature.CHANNEL_SOUNDING_TONE_QUALITY_INDICATION + LL_EXTENDED_FEATURE_SET = 1 << LeFeature.LL_EXTENDED_FEATURE_SET + MONITORING_ADVERTISERS = 1 << LeFeature.MONITORING_ADVERTISERS + FRAME_SPACE_UPDATE = 1 << LeFeature.FRAME_SPACE_UPDATE class LmpFeature(enum.IntEnum): # Page 0 (Legacy LMP features) @@ -1565,12 +1619,16 @@ class HCI_Object: # This is an array field, starting with a 1-byte item count. item_count = data[offset] offset += 1 + # Set fields first, because item_count might be 0. + for sub_field_name, _ in field: + result[sub_field_name] = [] + for _ in range(item_count): for sub_field_name, sub_field_type in field: value, size = HCI_Object.parse_field( data, offset, sub_field_type ) - result.setdefault(sub_field_name, []).append(value) + result[sub_field_name].append(value) offset += size continue @@ -2982,6 +3040,27 @@ class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command): ''' +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('authentication_enable', 1), + ] +) +class HCI_Read_Authentication_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.3.23 Read Authentication Enable Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('authentication_enable', 1)]) +class HCI_Write_Authentication_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.3.24 Write Authentication Enable Command + ''' + + # ----------------------------------------------------------------------------- @HCI_Command.command( return_parameters_fields=[ @@ -3022,7 +3101,12 @@ class HCI_Write_Voice_Setting_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('synchronous_flow_control_enable', 1), + ] +) class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.36 Read Synchronous Flow Control Enable Command @@ -3191,7 +3275,13 @@ class HCI_Set_Event_Mask_Page_2_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('le_supported_host', 1), + ('unused', 1), + ] +) class HCI_Read_LE_Host_Support_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.78 Read LE Host Support Command @@ -3324,13 +3414,39 @@ class HCI_Read_BD_ADDR_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ("status", STATUS_SPEC), + [("standard_codec_ids", 1)], + [("vendor_specific_codec_ids", 4)], + ] +) class HCI_Read_Local_Supported_Codecs_Command(HCI_Command): ''' See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command ''' +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ("status", STATUS_SPEC), + [("standard_codec_ids", 1), ("standard_codec_transports", 1)], + [("vendor_specific_codec_ids", 4), ("vendor_specific_codec_transports", 1)], + ] +) +class HCI_Read_Local_Supported_Codecs_V2_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command + ''' + + class Transport(OpenIntEnum): + BR_EDR_ACL = 0x00 + BR_EDR_SCO = 0x01 + LE_CIS = 0x02 + LE_BIS = 0x03 + + # ----------------------------------------------------------------------------- @HCI_Command.command( fields=[('handle', 2)], @@ -3488,7 +3604,12 @@ class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('tx_power_level', 1), + ] +) class HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.6 LE Read Advertising Physical Channel Tx Power Command @@ -3612,7 +3733,12 @@ class HCI_LE_Create_Connection_Cancel_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('filter_accept_list_size', 1), + ] +) class HCI_LE_Read_Filter_Accept_List_Size_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.14 LE Read Filter Accept List Size Command @@ -3723,7 +3849,12 @@ class HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command() +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('le_states', 8), + ] +) class HCI_LE_Read_Supported_States_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.27 LE Read Supported States Command @@ -4698,6 +4829,102 @@ class HCI_LE_Reject_CIS_Request_Command(HCI_Command): reason: int +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[ + ('big_handle', 1), + ('advertising_handle', 1), + ('num_bis', 1), + ('sdu_interval', 3), + ('max_sdu', 2), + ('max_transport_latency', 2), + ('rtn', 1), + ('phy', 1), + ('packing', 1), + ('framing', 1), + ('encryption', 1), + ('broadcast_code', 16), + ], +) +class HCI_LE_Create_BIG_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.103 LE Create BIG command + ''' + + big_handle: int + advertising_handle: int + num_bis: int + sdu_interval: int + max_sdu: int + max_transport_latency: int + rtn: int + phy: int + packing: int + framing: int + encryption: int + broadcast_code: int + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[ + ('big_handle', 1), + ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), + ], +) +class HCI_LE_Terminate_BIG_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.105 LE Terminate BIG command + ''' + + big_handle: int + reason: int + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[ + ('big_handle', 1), + ('sync_handle', 2), + ('encryption', 1), + ('broadcast_code', 16), + ('mse', 1), + ('big_sync_timeout', 2), + [('bis', 1)], + ], +) +class HCI_LE_BIG_Create_Sync_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.106 LE BIG Create Sync command + ''' + + big_handle: int + sync_handle: int + encryption: int + broadcast_code: int + mse: int + big_sync_timeout: int + bis: List[int] + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[ + ('big_handle', 1), + ], + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('big_handle', 2), + ], +) +class HCI_LE_BIG_Terminate_Sync_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.107. LE BIG Terminate Sync command + ''' + + big_handle: int + + # ----------------------------------------------------------------------------- @HCI_Command.command( fields=[ @@ -5533,6 +5760,27 @@ class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event): ''' +# ----------------------------------------------------------------------------- +@HCI_LE_Meta_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('service_data', 2), + ('sync_handle', 2), + ('advertising_sid', 1), + ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC), + ('advertiser_address', Address.parse_address_preceded_by_type), + ('advertiser_phy', 1), + ('periodic_advertising_interval', 2), + ('advertiser_clock_accuracy', 1), + ] +) +class HCI_LE_Periodic_Advertising_Sync_Transfer_Received_Event(HCI_LE_Meta_Event): + ''' + See Bluetooth spec @ 7.7.65.24 LE Periodic Advertising Sync Transfer Received Event + ''' + + # ----------------------------------------------------------------------------- @HCI_LE_Meta_Event.event( [ @@ -6225,6 +6473,23 @@ class HCI_Synchronous_Connection_Changed_Event(HCI_Event): ''' +# ----------------------------------------------------------------------------- +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('max_tx_latency', 2), + ('max_rx_latency', 2), + ('min_remote_timeout', 2), + ('min_local_timeout', 2), + ] +) +class HCI_Sniff_Subrating_Event(HCI_Event): + ''' + See Bluetooth spec @ 7.7.37 Sniff Subrating Event + ''' + + # ----------------------------------------------------------------------------- @HCI_Event.event( [ diff --git a/tests/hci_test.py b/tests/hci_test.py index 72f4022d..1b69cda3 100644 --- a/tests/hci_test.py +++ b/tests/hci_test.py @@ -60,6 +60,8 @@ from bumble.hci import ( HCI_Number_Of_Completed_Packets_Event, HCI_Packet, HCI_PIN_Code_Request_Reply_Command, + HCI_Read_Local_Supported_Codecs_Command, + HCI_Read_Local_Supported_Codecs_V2_Command, HCI_Read_Local_Supported_Commands_Command, HCI_Read_Local_Supported_Features_Command, HCI_Read_Local_Version_Information_Command, @@ -476,6 +478,51 @@ def test_HCI_LE_Setup_ISO_Data_Path_Command(): basic_check(command) +# ----------------------------------------------------------------------------- +def test_HCI_Read_Local_Supported_Codecs_Command_Complete(): + returned_parameters = ( + HCI_Read_Local_Supported_Codecs_Command.parse_return_parameters( + bytes([HCI_SUCCESS, 3, CodecID.A_LOG, CodecID.CVSD, CodecID.LINEAR_PCM, 0]) + ) + ) + assert returned_parameters.standard_codec_ids == [ + CodecID.A_LOG, + CodecID.CVSD, + CodecID.LINEAR_PCM, + ] + + +# ----------------------------------------------------------------------------- +def test_HCI_Read_Local_Supported_Codecs_V2_Command_Complete(): + returned_parameters = ( + HCI_Read_Local_Supported_Codecs_V2_Command.parse_return_parameters( + bytes( + [ + HCI_SUCCESS, + 3, + CodecID.A_LOG, + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_ACL, + CodecID.CVSD, + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_SCO, + CodecID.LINEAR_PCM, + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.LE_CIS, + 0, + ] + ) + ) + ) + assert returned_parameters.standard_codec_ids == [ + CodecID.A_LOG, + CodecID.CVSD, + CodecID.LINEAR_PCM, + ] + assert returned_parameters.standard_codec_transports == [ + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_ACL, + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_SCO, + HCI_Read_Local_Supported_Codecs_V2_Command.Transport.LE_CIS, + ] + + # ----------------------------------------------------------------------------- def test_address(): a = Address('C4:F2:17:1A:1D:BB')