| # 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 __future__ import annotations |
| import struct |
| import collections |
| import logging |
| import functools |
| from typing import Dict, Type, Union |
| |
| from .colors import color |
| from .core import ( |
| BT_BR_EDR_TRANSPORT, |
| AdvertisingData, |
| DeviceClass, |
| ProtocolError, |
| bit_flags_to_strings, |
| name_or_number, |
| padded_bytes, |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Logging |
| # ----------------------------------------------------------------------------- |
| logger = logging.getLogger(__name__) |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Utils |
| # ----------------------------------------------------------------------------- |
| def hci_command_op_code(ogf, ocf): |
| return ogf << 10 | ocf |
| |
| |
| def key_with_value(dictionary, target_value): |
| for key, value in dictionary.items(): |
| if value == target_value: |
| return key |
| return None |
| |
| |
| def indent_lines(string): |
| return '\n'.join([' ' + line for line in string.split('\n')]) |
| |
| |
| def map_null_terminated_utf8_string(utf8_bytes): |
| try: |
| terminator = utf8_bytes.find(0) |
| if terminator < 0: |
| return utf8_bytes |
| return utf8_bytes[0:terminator].decode('utf8') |
| except UnicodeDecodeError: |
| return utf8_bytes |
| |
| |
| def map_class_of_device(class_of_device): |
| ( |
| service_classes, |
| major_device_class, |
| minor_device_class, |
| ) = DeviceClass.split_class_of_device(class_of_device) |
| return ( |
| f'[{class_of_device:06X}] Services(' |
| f'{",".join(DeviceClass.service_class_labels(service_classes))}),' |
| f'Class({DeviceClass.major_device_class_name(major_device_class)}|' |
| f'{DeviceClass.minor_device_class_name(major_device_class, minor_device_class)}' |
| ')' |
| ) |
| |
| |
| def phy_list_to_bits(phys): |
| if phys is None: |
| return 0 |
| |
| phy_bits = 0 |
| for phy in phys: |
| if phy not in HCI_LE_PHY_TYPE_TO_BIT: |
| raise ValueError('invalid PHY') |
| phy_bits |= 1 << HCI_LE_PHY_TYPE_TO_BIT[phy] |
| return phy_bits |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Constants |
| # ----------------------------------------------------------------------------- |
| # fmt: off |
| # pylint: disable=line-too-long |
| |
| # HCI Version |
| HCI_VERSION_BLUETOOTH_CORE_1_0B = 0 |
| HCI_VERSION_BLUETOOTH_CORE_1_1 = 1 |
| HCI_VERSION_BLUETOOTH_CORE_1_2 = 2 |
| HCI_VERSION_BLUETOOTH_CORE_2_0_EDR = 3 |
| HCI_VERSION_BLUETOOTH_CORE_2_1_EDR = 4 |
| HCI_VERSION_BLUETOOTH_CORE_3_0_HS = 5 |
| HCI_VERSION_BLUETOOTH_CORE_4_0 = 6 |
| HCI_VERSION_BLUETOOTH_CORE_4_1 = 7 |
| HCI_VERSION_BLUETOOTH_CORE_4_2 = 8 |
| HCI_VERSION_BLUETOOTH_CORE_5_0 = 9 |
| HCI_VERSION_BLUETOOTH_CORE_5_1 = 10 |
| HCI_VERSION_BLUETOOTH_CORE_5_2 = 11 |
| HCI_VERSION_BLUETOOTH_CORE_5_3 = 12 |
| |
| HCI_VERSION_NAMES = { |
| HCI_VERSION_BLUETOOTH_CORE_1_0B: 'HCI_VERSION_BLUETOOTH_CORE_1_0B', |
| HCI_VERSION_BLUETOOTH_CORE_1_1: 'HCI_VERSION_BLUETOOTH_CORE_1_1', |
| HCI_VERSION_BLUETOOTH_CORE_1_2: 'HCI_VERSION_BLUETOOTH_CORE_1_2', |
| HCI_VERSION_BLUETOOTH_CORE_2_0_EDR: 'HCI_VERSION_BLUETOOTH_CORE_2_0_EDR', |
| HCI_VERSION_BLUETOOTH_CORE_2_1_EDR: 'HCI_VERSION_BLUETOOTH_CORE_2_1_EDR', |
| HCI_VERSION_BLUETOOTH_CORE_3_0_HS: 'HCI_VERSION_BLUETOOTH_CORE_3_0_HS', |
| HCI_VERSION_BLUETOOTH_CORE_4_0: 'HCI_VERSION_BLUETOOTH_CORE_4_0', |
| HCI_VERSION_BLUETOOTH_CORE_4_1: 'HCI_VERSION_BLUETOOTH_CORE_4_1', |
| HCI_VERSION_BLUETOOTH_CORE_4_2: 'HCI_VERSION_BLUETOOTH_CORE_4_2', |
| HCI_VERSION_BLUETOOTH_CORE_5_0: 'HCI_VERSION_BLUETOOTH_CORE_5_0', |
| HCI_VERSION_BLUETOOTH_CORE_5_1: 'HCI_VERSION_BLUETOOTH_CORE_5_1', |
| HCI_VERSION_BLUETOOTH_CORE_5_2: 'HCI_VERSION_BLUETOOTH_CORE_5_2', |
| HCI_VERSION_BLUETOOTH_CORE_5_3: 'HCI_VERSION_BLUETOOTH_CORE_5_3' |
| } |
| |
| # LMP Version |
| LMP_VERSION_NAMES = HCI_VERSION_NAMES |
| |
| # HCI Packet types |
| HCI_COMMAND_PACKET = 0x01 |
| HCI_ACL_DATA_PACKET = 0x02 |
| HCI_SYNCHRONOUS_DATA_PACKET = 0x03 |
| HCI_EVENT_PACKET = 0x04 |
| |
| # HCI Event Codes |
| HCI_INQUIRY_COMPLETE_EVENT = 0x01 |
| HCI_INQUIRY_RESULT_EVENT = 0x02 |
| HCI_CONNECTION_COMPLETE_EVENT = 0x03 |
| HCI_CONNECTION_REQUEST_EVENT = 0x04 |
| HCI_DISCONNECTION_COMPLETE_EVENT = 0x05 |
| HCI_AUTHENTICATION_COMPLETE_EVENT = 0x06 |
| HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT = 0x07 |
| HCI_ENCRYPTION_CHANGE_EVENT = 0x08 |
| HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT = 0x09 |
| HCI_LINK_KEY_TYPE_CHANGED_EVENT = 0x0A |
| HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT = 0x0B |
| HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT = 0x0C |
| HCI_QOS_SETUP_COMPLETE_EVENT = 0x0D |
| HCI_COMMAND_COMPLETE_EVENT = 0x0E |
| HCI_COMMAND_STATUS_EVENT = 0x0F |
| HCI_HARDWARE_ERROR_EVENT = 0x10 |
| HCI_FLUSH_OCCURRED_EVENT = 0x11 |
| HCI_ROLE_CHANGE_EVENT = 0x12 |
| HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT = 0x13 |
| HCI_MODE_CHANGE_EVENT = 0x14 |
| HCI_RETURN_LINK_KEYS_EVENT = 0x15 |
| HCI_PIN_CODE_REQUEST_EVENT = 0x16 |
| HCI_LINK_KEY_REQUEST_EVENT = 0x17 |
| HCI_LINK_KEY_NOTIFICATION_EVENT = 0x18 |
| HCI_LOOPBACK_COMMAND_EVENT = 0x19 |
| HCI_DATA_BUFFER_OVERFLOW_EVENT = 0x1A |
| HCI_MAX_SLOTS_CHANGE_EVENT = 0x1B |
| HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT = 0x1C |
| HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT = 0x1D |
| HCI_QOS_VIOLATION_EVENT = 0x1E |
| HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT = 0x20 |
| HCI_FLOW_SPECIFICATION_COMPLETE_EVENT = 0x21 |
| HCI_INQUIRY_RESULT_WITH_RSSI_EVENT = 0x22 |
| HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT = 0x23 |
| HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT = 0x2C |
| HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT = 0x2D |
| HCI_SNIFF_SUBRATING_EVENT = 0x2E |
| HCI_EXTENDED_INQUIRY_RESULT_EVENT = 0x2F |
| HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT = 0x30 |
| HCI_IO_CAPABILITY_REQUEST_EVENT = 0x31 |
| HCI_IO_CAPABILITY_RESPONSE_EVENT = 0x32 |
| HCI_USER_CONFIRMATION_REQUEST_EVENT = 0x33 |
| HCI_USER_PASSKEY_REQUEST_EVENT = 0x34 |
| HCI_REMOTE_OOB_DATA_REQUEST = 0x35 |
| HCI_SIMPLE_PAIRING_COMPLETE_EVENT = 0x36 |
| HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT = 0x38 |
| HCI_ENHANCED_FLUSH_COMPLETE_EVENT = 0x39 |
| HCI_USER_PASSKEY_NOTIFICATION_EVENT = 0x3B |
| HCI_KEYPRESS_NOTIFICATION_EVENT = 0x3C |
| HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT = 0x3D |
| HCI_LE_META_EVENT = 0x3E |
| HCI_NUMBER_OF_COMPLETED_DATA_BLOCKS_EVENT = 0x48 |
| HCI_TRIGGERED_CLOCK_CAPTURE_EVENT = 0X4E |
| HCI_SYNCHRONIZATION_TRAIN_COMPLETE_EVENT = 0X4F |
| HCI_SYNCHRONIZATION_TRAIN_RECEIVED_EVENT = 0X50 |
| HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_EVENT = 0X51 |
| HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_TIMEOUT_EVENT = 0X52 |
| HCI_TRUNCATED_PAGE_COMPLETE_EVENT = 0X53 |
| HCI_PERIPHERAL_PAGE_RESPONSE_TIMEOUT_EVENT = 0X54 |
| HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_CHANNEL_MAP_CHANGE_EVENT = 0X55 |
| HCI_INQUIRY_RESPONSE_NOTIFICATION_EVENT = 0X56 |
| HCI_AUTHENTICATED_PAYLOAD_TIMEOUT_EXPIRED_EVENT = 0X57 |
| HCI_SAM_STATUS_CHANGE_EVENT = 0X58 |
| |
| HCI_EVENT_NAMES = { |
| event_code: event_name for (event_name, event_code) in globals().items() |
| if event_name.startswith('HCI_') and event_name.endswith('_EVENT') |
| } |
| |
| # HCI Subevent Codes |
| HCI_LE_CONNECTION_COMPLETE_EVENT = 0x01 |
| HCI_LE_ADVERTISING_REPORT_EVENT = 0x02 |
| HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT = 0x03 |
| HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT = 0x04 |
| HCI_LE_LONG_TERM_KEY_REQUEST_EVENT = 0x05 |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT = 0x06 |
| HCI_LE_DATA_LENGTH_CHANGE_EVENT = 0x07 |
| HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT = 0x08 |
| HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT = 0x09 |
| HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT = 0x0A |
| HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT = 0x0B |
| HCI_LE_PHY_UPDATE_COMPLETE_EVENT = 0x0C |
| HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT = 0x0D |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT = 0x0E |
| HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT = 0x0F |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT = 0x10 |
| HCI_LE_SCAN_TIMEOUT_EVENT = 0x11 |
| HCI_LE_ADVERTISING_SET_TERMINATED_EVENT = 0x12 |
| HCI_LE_SCAN_REQUEST_RECEIVED_EVENT = 0x13 |
| HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT = 0x14 |
| HCI_LE_CONNECTIONLESS_IQ_REPORT_EVENT = 0X15 |
| HCI_LE_CONNECTION_IQ_REPORT_EVENT = 0X16 |
| HCI_LE_CTE_REQUEST_FAILED_EVENT = 0X17 |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_EVENT = 0X18 |
| HCI_LE_CIS_ESTABLISHED_EVENT = 0X19 |
| HCI_LE_CIS_REQUEST_EVENT = 0X1A |
| HCI_LE_CREATE_BIG_COMPLETE_EVENT = 0X1B |
| HCI_LE_TERMINATE_BIG_COMPLETE_EVENT = 0X1C |
| HCI_LE_BIG_SYNC_ESTABLISHED_EVENT = 0X1D |
| HCI_LE_BIG_SYNC_LOST_EVENT = 0X1E |
| HCI_LE_REQUEST_PEER_SCA_COMPLETE_EVENT = 0X1F |
| HCI_LE_PATH_LOSS_THRESHOLD_EVENT = 0X20 |
| HCI_LE_TRANSMIT_POWER_REPORTING_EVENT = 0X21 |
| HCI_LE_BIGINFO_ADVERTISING_REPORT_EVENT = 0X22 |
| HCI_LE_SUBRATE_CHANGE_EVENT = 0X23 |
| |
| HCI_SUBEVENT_NAMES = { |
| event_code: event_name for (event_name, event_code) in globals().items() |
| if event_name.startswith('HCI_LE_') and event_name.endswith('_EVENT') and event_code != HCI_LE_META_EVENT |
| } |
| |
| # HCI Command |
| HCI_INQUIRY_COMMAND = hci_command_op_code(0x01, 0x0001) |
| HCI_INQUIRY_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0002) |
| HCI_PERIODIC_INQUIRY_MODE_COMMAND = hci_command_op_code(0x01, 0x0003) |
| HCI_EXIT_PERIODIC_INQUIRY_MODE_COMMAND = hci_command_op_code(0x01, 0x0004) |
| HCI_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x0005) |
| HCI_DISCONNECT_COMMAND = hci_command_op_code(0x01, 0x0006) |
| HCI_CREATE_CONNECTION_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0008) |
| HCI_ACCEPT_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0009) |
| HCI_REJECT_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x000A) |
| HCI_LINK_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x000B) |
| HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000C) |
| HCI_PIN_CODE_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x000D) |
| HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000E) |
| HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND = hci_command_op_code(0x01, 0x000F) |
| HCI_AUTHENTICATION_REQUESTED_COMMAND = hci_command_op_code(0x01, 0x0011) |
| HCI_SET_CONNECTION_ENCRYPTION_COMMAND = hci_command_op_code(0x01, 0x0013) |
| HCI_CHANGE_CONNECTION_LINK_KEY_COMMAND = hci_command_op_code(0x01, 0x0015) |
| HCI_LINK_KEY_SELECTION_COMMAND = hci_command_op_code(0x01, 0x0017) |
| HCI_REMOTE_NAME_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0019) |
| HCI_REMOTE_NAME_REQUEST_CANCEL_COMMAND = hci_command_op_code(0x01, 0x001A) |
| HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001B) |
| HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001C) |
| HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x01, 0x001D) |
| HCI_READ_CLOCK_OFFSET_COMMAND = hci_command_op_code(0x01, 0x001F) |
| HCI_READ_LMP_HANDLE_COMMAND = hci_command_op_code(0x01, 0x0020) |
| HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x0028) |
| HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0029) |
| HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x002A) |
| HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002B) |
| HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002C) |
| HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002D) |
| HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002E) |
| HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002F) |
| HCI_REMOTE_OOB_DATA_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x0030) |
| HCI_REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x0033) |
| HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x0034) |
| HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x003D) |
| HCI_ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x003E) |
| HCI_TRUNCATED_PAGE_COMMAND = hci_command_op_code(0x01, 0x003F) |
| HCI_TRUNCATED_PAGE_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0040) |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_COMMAND = hci_command_op_code(0x01, 0x0041) |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_COMMAND = hci_command_op_code(0x01, 0x0042) |
| HCI_START_SYNCHRONIZATION_TRAIN_COMMAND = hci_command_op_code(0x01, 0x0043) |
| HCI_RECEIVE_SYNCHRONIZATION_TRAIN_COMMAND = hci_command_op_code(0x01, 0x0044) |
| HCI_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x0045) |
| HCI_HOLD_MODE_COMMAND = hci_command_op_code(0x02, 0x0001) |
| HCI_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0003) |
| HCI_EXIT_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0004) |
| HCI_QOS_SETUP_COMMAND = hci_command_op_code(0x02, 0x0007) |
| HCI_ROLE_DISCOVERY_COMMAND = hci_command_op_code(0x02, 0x0009) |
| HCI_SWITCH_ROLE_COMMAND = hci_command_op_code(0x02, 0x000B) |
| HCI_READ_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000C) |
| HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000D) |
| HCI_READ_DEFAULT_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000E) |
| HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000F) |
| HCI_FLOW_SPECIFICATION_COMMAND = hci_command_op_code(0x02, 0x0010) |
| HCI_SNIFF_SUBRATING_COMMAND = hci_command_op_code(0x02, 0x0011) |
| HCI_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x03, 0x0001) |
| HCI_RESET_COMMAND = hci_command_op_code(0x03, 0x0003) |
| HCI_SET_EVENT_FILTER_COMMAND = hci_command_op_code(0x03, 0x0005) |
| HCI_FLUSH_COMMAND = hci_command_op_code(0x03, 0x0008) |
| HCI_READ_PIN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0009) |
| HCI_WRITE_PIN_TYPE_COMMAND = hci_command_op_code(0x03, 0x000A) |
| HCI_READ_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x000D) |
| HCI_WRITE_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x0011) |
| HCI_DELETE_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x0012) |
| HCI_WRITE_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0013) |
| HCI_READ_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0014) |
| HCI_READ_CONNECTION_ACCEPT_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0015) |
| HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0016) |
| HCI_READ_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0017) |
| HCI_WRITE_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0018) |
| HCI_READ_SCAN_ENABLE_COMMAND = hci_command_op_code(0x03, 0x0019) |
| HCI_WRITE_SCAN_ENABLE_COMMAND = hci_command_op_code(0x03, 0x001A) |
| HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001B) |
| HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001C) |
| HCI_READ_INQUIRY_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001D) |
| HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001E) |
| HCI_READ_AUTHENTICATION_ENABLE_COMMAND = hci_command_op_code(0x03, 0x001F) |
| HCI_WRITE_AUTHENTICATION_ENABLE_COMMAND = hci_command_op_code(0x03, 0x0020) |
| HCI_READ_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0023) |
| HCI_WRITE_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0024) |
| HCI_READ_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0025) |
| HCI_WRITE_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0026) |
| HCI_READ_AUTOMATIC_FLUSH_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0027) |
| HCI_WRITE_AUTOMATIC_FLUSH_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0028) |
| HCI_READ_NUM_BROADCAST_RETRANSMISSIONS_COMMAND = hci_command_op_code(0x03, 0x0029) |
| HCI_WRITE_NUM_BROADCAST_RETRANSMISSIONS_COMMAND = hci_command_op_code(0x03, 0x002A) |
| HCI_READ_HOLD_MODE_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x002B) |
| HCI_WRITE_HOLD_MODE_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x002C) |
| HCI_READ_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x002D) |
| HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002E) |
| HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002F) |
| HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND = hci_command_op_code(0x03, 0x0031) |
| HCI_HOST_BUFFER_SIZE_COMMAND = hci_command_op_code(0x03, 0x0033) |
| HCI_HOST_NUMBER_OF_COMPLETED_PACKETS_COMMAND = hci_command_op_code(0x03, 0x0035) |
| HCI_READ_LINK_SUPERVISION_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0036) |
| HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0037) |
| HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND = hci_command_op_code(0x03, 0x0038) |
| HCI_READ_CURRENT_IAC_LAP_COMMAND = hci_command_op_code(0x03, 0x0039) |
| HCI_WRITE_CURRENT_IAC_LAP_COMMAND = hci_command_op_code(0x03, 0x003A) |
| HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x03, 0x003F) |
| HCI_READ_INQUIRY_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0042) |
| HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0043) |
| HCI_READ_INQUIRY_MODE_COMMAND = hci_command_op_code(0x03, 0x0044) |
| HCI_WRITE_INQUIRY_MODE_COMMAND = hci_command_op_code(0x03, 0x0045) |
| HCI_READ_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0046) |
| HCI_WRITE_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0047) |
| HCI_READ_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND = hci_command_op_code(0x03, 0x0048) |
| HCI_WRITE_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND = hci_command_op_code(0x03, 0x0049) |
| HCI_READ_EXTENDED_INQUIRY_RESPONSE_COMMAND = hci_command_op_code(0x03, 0x0051) |
| HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND = hci_command_op_code(0x03, 0x0052) |
| HCI_REFRESH_ENCRYPTION_KEY_COMMAND = hci_command_op_code(0x03, 0x0053) |
| HCI_READ_SIMPLE_PAIRING_MODE_COMMAND = hci_command_op_code(0x03, 0x0055) |
| HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND = hci_command_op_code(0x03, 0x0056) |
| HCI_READ_LOCAL_OOB_DATA_COMMAND = hci_command_op_code(0x03, 0x0057) |
| HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0058) |
| HCI_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0059) |
| HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND = hci_command_op_code(0x03, 0x005A) |
| HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND = hci_command_op_code(0x03, 0x005B) |
| HCI_ENHANCED_FLUSH_COMMAND = hci_command_op_code(0x03, 0x005F) |
| HCI_SEND_KEYPRESS_NOTIFICATION_COMMAND = hci_command_op_code(0x03, 0x0060) |
| HCI_SET_EVENT_MASK_PAGE_2_COMMAND = hci_command_op_code(0x03, 0x0063) |
| HCI_READ_FLOW_CONTROL_MODE_COMMAND = hci_command_op_code(0x03, 0x0066) |
| HCI_WRITE_FLOW_CONTROL_MODE_COMMAND = hci_command_op_code(0x03, 0x0067) |
| HCI_READ_ENHANCED_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0068) |
| HCI_READ_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006C) |
| HCI_WRITE_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006D) |
| HCI_SET_MWS_CHANNEL_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x006E) |
| HCI_SET_EXTERNAL_FRAME_CONFIGURATION_COMMAND = hci_command_op_code(0x03, 0x006F) |
| HCI_SET_MWS_SIGNALING_COMMAND = hci_command_op_code(0x03, 0x0070) |
| HCI_SET_MWS_TRANSPORT_LAYER_COMMAND = hci_command_op_code(0x03, 0x0071) |
| HCI_SET_MWS_SCAN_FREQUENCY_TABLE_COMMAND = hci_command_op_code(0x03, 0x0072) |
| HCI_SET_MWS_PATTERN_CONFIGURATION_COMMAND = hci_command_op_code(0x03, 0x0073) |
| HCI_SET_RESERVED_LT_ADDR_COMMAND = hci_command_op_code(0x03, 0x0074) |
| HCI_DELETE_RESERVED_LT_ADDR_COMMAND = hci_command_op_code(0x03, 0x0075) |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA_COMMAND = hci_command_op_code(0x03, 0x0076) |
| HCI_READ_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x0077) |
| HCI_WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x0078) |
| HCI_READ_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x0079) |
| HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x007A) |
| HCI_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007B) |
| HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007C) |
| HCI_READ_LOCAL_OOB_EXTENDED_DATA_COMMAND = hci_command_op_code(0x03, 0x007D) |
| HCI_READ_EXTENDED_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007E) |
| HCI_WRITE_EXTENDED_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007F) |
| HCI_READ_EXTENDED_INQUIRY_LENGTH_COMMAND = hci_command_op_code(0x03, 0x0080) |
| HCI_WRITE_EXTENDED_INQUIRY_LENGTH_COMMAND = hci_command_op_code(0x03, 0x0081) |
| HCI_SET_ECOSYSTEM_BASE_INTERVAL_COMMAND = hci_command_op_code(0x03, 0x0082) |
| HCI_CONFIGURE_DATA_PATH_COMMAND = hci_command_op_code(0x03, 0x0083) |
| HCI_SET_MIN_ENCRYPTION_KEY_SIZE_COMMAND = hci_command_op_code(0x03, 0x0084) |
| HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x04, 0x0001) |
| HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND = hci_command_op_code(0x04, 0x0002) |
| HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0003) |
| HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0004) |
| HCI_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x04, 0x0005) |
| HCI_READ_BD_ADDR_COMMAND = hci_command_op_code(0x04, 0x0009) |
| HCI_READ_DATA_BLOCK_SIZE_COMMAND = hci_command_op_code(0x04, 0x000A) |
| HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND = hci_command_op_code(0x04, 0x000B) |
| HCI_READ_LOCAL_SIMPLE_PAIRING_OPTIONS_COMMAND = hci_command_op_code(0x04, 0x000C) |
| HCI_READ_LOCAL_SUPPORTED_CODECS_V2_COMMAND = hci_command_op_code(0x04, 0x000D) |
| HCI_READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES_COMMAND = hci_command_op_code(0x04, 0x000E) |
| HCI_READ_LOCAL_SUPPORTED_CONTROLLER_DELAY_COMMAND = hci_command_op_code(0x04, 0x000F) |
| HCI_READ_FAILED_CONTACT_COUNTER_COMMAND = hci_command_op_code(0x05, 0x0001) |
| HCI_RESET_FAILED_CONTACT_COUNTER_COMMAND = hci_command_op_code(0x05, 0x0002) |
| HCI_READ_LINK_QUALITY_COMMAND = hci_command_op_code(0x05, 0x0003) |
| HCI_READ_RSSI_COMMAND = hci_command_op_code(0x05, 0x0005) |
| HCI_READ_AFH_CHANNEL_MAP_COMMAND = hci_command_op_code(0x05, 0x0006) |
| HCI_READ_CLOCK_COMMAND = hci_command_op_code(0x05, 0x0007) |
| HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND = hci_command_op_code(0x05, 0x0008) |
| HCI_GET_MWS_TRANSPORT_LAYER_CONFIGURATION_COMMAND = hci_command_op_code(0x05, 0x000C) |
| HCI_SET_TRIGGERED_CLOCK_CAPTURE_COMMAND = hci_command_op_code(0x05, 0x000D) |
| HCI_READ_LOOPBACK_MODE_COMMAND = hci_command_op_code(0x06, 0x0001) |
| HCI_WRITE_LOOPBACK_MODE_COMMAND = hci_command_op_code(0x06, 0x0002) |
| HCI_ENABLE_DEVICE_UNDER_TEST_MODE_COMMAND = hci_command_op_code(0x06, 0x0003) |
| HCI_WRITE_SIMPLE_PAIRING_DEBUG_MODE_COMMAND = hci_command_op_code(0x06, 0x0004) |
| HCI_WRITE_SECURE_CONNECTIONS_TEST_MODE_COMMAND = hci_command_op_code(0x06, 0x000A) |
| HCI_LE_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x08, 0x0001) |
| HCI_LE_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x08, 0x0002) |
| HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0003) |
| HCI_LE_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0005) |
| HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0006) |
| HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND = hci_command_op_code(0x08, 0x0007) |
| HCI_LE_SET_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0008) |
| HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0009) |
| HCI_LE_SET_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000A) |
| HCI_LE_SET_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x000B) |
| HCI_LE_SET_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000C) |
| HCI_LE_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x000D) |
| HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND = hci_command_op_code(0x08, 0x000E) |
| HCI_LE_READ_FILTER_ACCEPT_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x000F) |
| HCI_LE_CLEAR_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0010) |
| HCI_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0011) |
| HCI_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0012) |
| HCI_LE_CONNECTION_UPDATE_COMMAND = hci_command_op_code(0x08, 0x0013) |
| HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0014) |
| HCI_LE_READ_CHANNEL_MAP_COMMAND = hci_command_op_code(0x08, 0x0015) |
| HCI_LE_READ_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0016) |
| HCI_LE_ENCRYPT_COMMAND = hci_command_op_code(0x08, 0x0017) |
| HCI_LE_RAND_COMMAND = hci_command_op_code(0x08, 0x0018) |
| HCI_LE_ENABLE_ENCRYPTION_COMMAND = hci_command_op_code(0x08, 0x0019) |
| HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x001A) |
| HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x001B) |
| HCI_LE_READ_SUPPORTED_STATES_COMMAND = hci_command_op_code(0x08, 0x001C) |
| HCI_LE_RECEIVER_TEST_COMMAND = hci_command_op_code(0x08, 0x001D) |
| HCI_LE_TRANSMITTER_TEST_COMMAND = hci_command_op_code(0x08, 0x001E) |
| HCI_LE_TEST_END_COMMAND = hci_command_op_code(0x08, 0x001F) |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x0020) |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x0021) |
| HCI_LE_SET_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0022) |
| HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0023) |
| HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0024) |
| HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = hci_command_op_code(0x08, 0x0025) |
| HCI_LE_GENERATE_DHKEY_COMMAND = hci_command_op_code(0x08, 0x0026) |
| HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0027) |
| HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0028) |
| HCI_LE_CLEAR_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0029) |
| HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x002A) |
| HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002B) |
| HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002C) |
| HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND = hci_command_op_code(0x08, 0x002D) |
| HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND = hci_command_op_code(0x08, 0x002E) |
| HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x002F) |
| HCI_LE_READ_PHY_COMMAND = hci_command_op_code(0x08, 0x0030) |
| HCI_LE_SET_DEFAULT_PHY_COMMAND = hci_command_op_code(0x08, 0x0031) |
| HCI_LE_SET_PHY_COMMAND = hci_command_op_code(0x08, 0x0032) |
| HCI_LE_RECEIVER_TEST_V2_COMMAND = hci_command_op_code(0x08, 0x0033) |
| HCI_LE_TRANSMITTER_TEST_V2_COMMAND = hci_command_op_code(0x08, 0x0034) |
| HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0035) |
| HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0036) |
| HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0037) |
| HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0038) |
| HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0039) |
| HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x003A) |
| HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003B) |
| HCI_LE_REMOVE_ADVERTISING_SET_COMMAND = hci_command_op_code(0x08, 0x003C) |
| HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003D) |
| HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x003E) |
| HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x003F) |
| HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0040) |
| HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0041) |
| HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0042) |
| HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x0043) |
| HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0044) |
| HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND = hci_command_op_code(0x08, 0x0045) |
| HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0046) |
| HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0047) |
| HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0048) |
| HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0049) |
| HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x004A) |
| HCI_LE_READ_TRANSMIT_POWER_COMMAND = hci_command_op_code(0x08, 0x004B) |
| HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004C) |
| HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004D) |
| HCI_LE_SET_PRIVACY_MODE_COMMAND = hci_command_op_code(0x08, 0x004E) |
| HCI_LE_RECEIVER_TEST_V3_COMMAND = hci_command_op_code(0x08, 0x004F) |
| HCI_LE_TRANSMITTER_TEST_V3_COMMAND = hci_command_op_code(0x08, 0x0050) |
| HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0051) |
| HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0052) |
| HCI_LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0053) |
| HCI_LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0054) |
| HCI_LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0055) |
| HCI_LE_CONNECTION_CTE_REQUEST_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0056) |
| HCI_LE_CONNECTION_CTE_RESPONSE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0057) |
| HCI_LE_READ_ANTENNA_INFORMATION_COMMAND = hci_command_op_code(0x08, 0x0058) |
| HCI_LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0059) |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_COMMAND = hci_command_op_code(0x08, 0x005A) |
| HCI_LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER_COMMAND = hci_command_op_code(0x08, 0x005B) |
| HCI_LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x005C) |
| HCI_LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x005D) |
| HCI_LE_GENERATE_DHKEY_V2_COMMAND = hci_command_op_code(0x08, 0x005E) |
| HCI_LE_MODIFY_SLEEP_CLOCK_ACCURACY_COMMAND = hci_command_op_code(0x08, 0x005F) |
| HCI_LE_READ_BUFFER_SIZE_V2_COMMAND = hci_command_op_code(0x08, 0x0060) |
| HCI_LE_READ_ISO_TX_SYNC_COMMAND = hci_command_op_code(0x08, 0x0061) |
| HCI_LE_SET_CIG_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0062) |
| HCI_LE_SET_CIG_PARAMETERS_TEST_COMMAND = hci_command_op_code(0x08, 0x0063) |
| HCI_LE_CREATE_CIS_COMMAND = hci_command_op_code(0x08, 0x0064) |
| HCI_LE_REMOVE_CIG_COMMAND = hci_command_op_code(0x08, 0x0065) |
| HCI_LE_ACCEPT_CIS_REQUEST_COMMAND = hci_command_op_code(0x08, 0x0066) |
| HCI_LE_REJECT_CIS_REQUEST_COMMAND = hci_command_op_code(0x08, 0x0067) |
| HCI_LE_CREATE_BIG_COMMAND = hci_command_op_code(0x08, 0x0068) |
| HCI_LE_CREATE_BIG_TEST_COMMAND = hci_command_op_code(0x08, 0x0069) |
| HCI_LE_TERMINATE_BIG_COMMAND = hci_command_op_code(0x08, 0x006A) |
| HCI_LE_BIG_CREATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x006B) |
| HCI_LE_BIG_TERMINATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x006C) |
| HCI_LE_REQUEST_PEER_SCA_COMMAND = hci_command_op_code(0x08, 0x006D) |
| HCI_LE_SETUP_ISO_DATA_PATH_COMMAND = hci_command_op_code(0x08, 0x006E) |
| HCI_LE_REMOVE_ISO_DATA_PATH_COMMAND = hci_command_op_code(0x08, 0x006F) |
| HCI_LE_ISO_TRANSMIT_TEST_COMMAND = hci_command_op_code(0x08, 0x0070) |
| HCI_LE_ISO_RECEIVE_TEST_COMMAND = hci_command_op_code(0x08, 0x0071) |
| HCI_LE_ISO_READ_TEST_COUNTERS_COMMAND = hci_command_op_code(0x08, 0x0072) |
| HCI_LE_ISO_TEST_END_COMMAND = hci_command_op_code(0x08, 0x0073) |
| HCI_LE_SET_HOST_FEATURE_COMMAND = hci_command_op_code(0x08, 0x0074) |
| HCI_LE_READ_ISO_LINK_QUALITY_COMMAND = hci_command_op_code(0x08, 0x0075) |
| HCI_LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x08, 0x0076) |
| HCI_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x08, 0x0077) |
| HCI_LE_SET_PATH_LOSS_REPORTING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0078) |
| HCI_LE_SET_PATH_LOSS_REPORTING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0079) |
| HCI_LE_SET_TRANSMIT_POWER_REPORTING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x007A) |
| HCI_LE_TRANSMITTER_TEST_V4_COMMAND = hci_command_op_code(0x08, 0x007B) |
| HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND = hci_command_op_code(0x08, 0x007C) |
| HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D) |
| HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E) |
| |
| HCI_COMMAND_NAMES = { |
| command_code: command_name for (command_name, command_code) in globals().items() |
| if command_name.startswith('HCI_') and command_name.endswith('_COMMAND') |
| } |
| |
| # HCI Error Codes |
| # See Bluetooth spec Vol 2, Part D - 1.3 LIST OF ERROR CODES |
| HCI_SUCCESS = 0x00 |
| HCI_UNKNOWN_HCI_COMMAND_ERROR = 0x01 |
| HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR = 0x02 |
| HCI_HARDWARE_FAILURE_ERROR = 0x03 |
| HCI_PAGE_TIMEOUT_ERROR = 0x04 |
| HCI_AUTHENTICATION_FAILURE_ERROR = 0x05 |
| HCI_PIN_OR_KEY_MISSING_ERROR = 0x06 |
| HCI_MEMORY_CAPACITY_EXCEEDED_ERROR = 0x07 |
| HCI_CONNECTION_TIMEOUT_ERROR = 0x08 |
| HCI_CONNECTION_LIMIT_EXCEEDED_ERROR = 0x09 |
| HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR = 0x0A |
| HCI_CONNECTION_ALREADY_EXISTS_ERROR = 0x0B |
| HCI_COMMAND_DISALLOWED_ERROR = 0x0C |
| HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR = 0x0D |
| HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR = 0x0E |
| HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR = 0x0F |
| HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR = 0x10 |
| HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR = 0x11 |
| HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR = 0x12 |
| HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR = 0x13 |
| HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR = 0x14 |
| HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR = 0x15 |
| HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR = 0x16 |
| HCI_REPEATED_ATTEMPTS_ERROR = 0X17 |
| HCI_PAIRING_NOT_ALLOWED_ERROR = 0X18 |
| HCI_UNKNOWN_LMP_PDU_ERROR = 0X19 |
| HCI_UNSUPPORTED_REMOTE_FEATURE_ERROR = 0X1A |
| HCI_SCO_OFFSET_REJECTED_ERROR = 0X1B |
| HCI_SCO_INTERVAL_REJECTED_ERROR = 0X1C |
| HCI_SCO_AIR_MODE_REJECTED_ERROR = 0X1D |
| HCI_INVALID_LMP_OR_LL_PARAMETERS_ERROR = 0X1E |
| HCI_UNSPECIFIED_ERROR_ERROR = 0X1F |
| HCI_UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE_ERROR = 0X20 |
| HCI_ROLE_CHANGE_NOT_ALLOWED_ERROR = 0X21 |
| HCI_LMP_OR_LL_RESPONSE_TIMEOUT_ERROR = 0X22 |
| HCI_LMP_ERROR_TRANSACTION_COLLISION_OR_LL_PROCEDURE_COLLISION_ERROR = 0X23 |
| HCI_LMP_PDU_NOT_ALLOWED_ERROR = 0X24 |
| HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE_ERROR = 0X25 |
| HCI_LINK_KEY_CANNOT_BE_CHANGED_ERROR = 0X26 |
| HCI_REQUESTED_QOS_NOT_SUPPORTED_ERROR = 0X27 |
| HCI_INSTANT_PASSED_ERROR = 0X28 |
| HCI_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED_ERROR = 0X29 |
| HCI_DIFFERENT_TRANSACTION_COLLISION_ERROR = 0X2A |
| HCI_RESERVED_FOR_FUTURE_USE = 0X2B |
| HCI_QOS_UNACCEPTABLE_PARAMETER_ERROR = 0X2C |
| HCI_QOS_REJECTED_ERROR = 0X2D |
| HCI_CHANNEL_CLASSIFICATION_NOT_SUPPORTED_ERROR = 0X2E |
| HCI_INSUFFICIENT_SECURITY_ERROR = 0X2F |
| HCI_PARAMETER_OUT_OF_MANDATORY_RANGE_ERROR = 0X30 |
| HCI_ROLE_SWITCH_PENDING_ERROR = 0X32 |
| HCI_RESERVED_SLOT_VIOLATION_ERROR = 0X34 |
| HCI_ROLE_SWITCH_FAILED_ERROR = 0X35 |
| HCI_EXTENDED_INQUIRY_RESPONSE_TOO_LARGE_ERROR = 0X36 |
| HCI_SECURE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST_ERROR = 0X37 |
| HCI_HOST_BUSY_PAIRING_ERROR = 0X38 |
| HCI_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND_ERROR = 0X39 |
| HCI_CONTROLLER_BUSY_ERROR = 0X3A |
| HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR = 0X3B |
| HCI_ADVERTISING_TIMEOUT_ERROR = 0X3C |
| HCI_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE_ERROR = 0X3D |
| HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR = 0X3E |
| HCI_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING_ERROR = 0X40 |
| HCI_TYPE0_SUBMAP_NOT_DEFINED_ERROR = 0X41 |
| HCI_UNKNOWN_ADVERTISING_IDENTIFIER_ERROR = 0X42 |
| HCI_LIMIT_REACHED_ERROR = 0X43 |
| HCI_OPERATION_CANCELLED_BY_HOST_ERROR = 0X44 |
| HCI_PACKET_TOO_LONG_ERROR = 0X45 |
| |
| HCI_ERROR_NAMES = { |
| error_code: error_name for (error_name, error_code) in globals().items() |
| if error_name.startswith('HCI_') and error_name.endswith('_ERROR') |
| } |
| HCI_ERROR_NAMES[HCI_SUCCESS] = 'HCI_SUCCESS' |
| |
| # Command Status codes |
| HCI_COMMAND_STATUS_PENDING = 0 |
| |
| # LE Event Masks |
| HCI_LE_CONNECTION_COMPLETE_EVENT_MASK = (1 << 0) |
| HCI_LE_ADVERTISING_REPORT_EVENT_MASK = (1 << 1) |
| HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT_MASK = (1 << 2) |
| HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT_MASK = (1 << 3) |
| HCI_LE_LONG_TERM_KEY_REQUEST_EVENT_MASK = (1 << 4) |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT_MASK = (1 << 5) |
| HCI_LE_DATA_LENGTH_CHANGE_EVENT_MASK = (1 << 6) |
| HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT_MASK = (1 << 7) |
| HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT_MASK = (1 << 8) |
| HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT_MASK = (1 << 9) |
| HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT_MASK = (1 << 10) |
| HCI_LE_PHY_UPDATE_COMPLETE_EVENT_MASK = (1 << 11) |
| HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT_MASK = (1 << 12) |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT_MASK = (1 << 13) |
| HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT_MASK = (1 << 14) |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT_MASK = (1 << 15) |
| HCI_LE_EXTENDED_SCAN_TIMEOUT_EVENT_MASK = (1 << 16) |
| HCI_LE_EXTENDED_ADVERTISING_SET_TERMINATED_EVENT_MASK = (1 << 17) |
| HCI_LE_SCAN_REQUEST_RECEIVED_EVENT_MASK = (1 << 18) |
| HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT_MASK = (1 << 19) |
| HCI_LE_CONNECTIONLESS_IQ_REPORT_EVENT_MASK = (1 << 20) |
| HCI_LE_CONNECTION_IQ_REPORT_EVENT_MASK = (1 << 21) |
| HCI_LE_CTE_REQUEST_FAILED_EVENT_MASK = (1 << 22) |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_EVENT_MASK = (1 << 23) |
| HCI_LE_CIS_ESTABLISHED_EVENT_MASK = (1 << 24) |
| HCI_LE_CIS_REQUEST_EVENT_MASK = (1 << 25) |
| HCI_LE_CREATE_BIG_COMPLETE_EVENT_MASK = (1 << 26) |
| HCI_LE_TERMINATE_BIG_COMPLETE_EVENT_MASK = (1 << 27) |
| HCI_LE_BIG_SYNC_ESTABLISHED_EVENT_MASK = (1 << 28) |
| HCI_LE_BIG_SYNC_LOST_EVENT_MASK = (1 << 29) |
| HCI_LE_REQUEST_PEER_SCA_COMPLETE_EVENT_MASK = (1 << 30) |
| HCI_LE_PATH_LOSS_THRESHOLD_EVENT_MASK = (1 << 31) |
| HCI_LE_TRANSMIT_POWER_REPORTING_EVENT_MASK = (1 << 32) |
| HCI_LE_BIGINFO_ADVERTISING_REPORT_EVENT_MASK = (1 << 33) |
| HCI_LE_SUBRATE_CHANGE_EVENT_MASK = (1 << 34) |
| |
| HCI_LE_EVENT_MASK_NAMES = { |
| mask: mask_name for (mask_name, mask) in globals().items() |
| if mask_name.startswith('HCI_LE_') and mask_name.endswith('_EVENT_MASK') |
| } |
| |
| # ACL |
| HCI_ACL_PB_FIRST_NON_FLUSHABLE = 0 |
| HCI_ACL_PB_CONTINUATION = 1 |
| HCI_ACL_PB_FIRST_FLUSHABLE = 2 |
| HCI_ACK_PB_COMPLETE_L2CAP = 3 |
| |
| # Roles |
| HCI_CENTRAL_ROLE = 0 |
| HCI_PERIPHERAL_ROLE = 1 |
| |
| HCI_ROLE_NAMES = { |
| HCI_CENTRAL_ROLE: 'CENTRAL', |
| HCI_PERIPHERAL_ROLE: 'PERIPHERAL' |
| } |
| |
| # LE PHY Types |
| HCI_LE_1M_PHY = 1 |
| HCI_LE_2M_PHY = 2 |
| HCI_LE_CODED_PHY = 3 |
| |
| HCI_LE_PHY_NAMES = { |
| HCI_LE_1M_PHY: 'LE 1M', |
| HCI_LE_2M_PHY: 'LE 2M', |
| HCI_LE_CODED_PHY: 'LE Coded' |
| } |
| |
| HCI_LE_1M_PHY_BIT = 0 |
| HCI_LE_2M_PHY_BIT = 1 |
| HCI_LE_CODED_PHY_BIT = 2 |
| |
| HCI_LE_PHY_BIT_NAMES = ['LE_1M_PHY', 'LE_2M_PHY', 'LE_CODED_PHY'] |
| |
| HCI_LE_PHY_TYPE_TO_BIT = { |
| HCI_LE_1M_PHY: HCI_LE_1M_PHY_BIT, |
| HCI_LE_2M_PHY: HCI_LE_2M_PHY_BIT, |
| HCI_LE_CODED_PHY: HCI_LE_CODED_PHY_BIT |
| } |
| |
| # Connection Parameters |
| HCI_CONNECTION_INTERVAL_MS_PER_UNIT = 1.25 |
| HCI_CONNECTION_LATENCY_MS_PER_UNIT = 1.25 |
| HCI_SUPERVISION_TIMEOUT_MS_PER_UNIT = 10 |
| |
| # Inquiry LAP |
| HCI_LIMITED_DEDICATED_INQUIRY_LAP = 0x9E8B00 |
| HCI_GENERAL_INQUIRY_LAP = 0x9E8B33 |
| HCI_INQUIRY_LAP_NAMES = { |
| HCI_LIMITED_DEDICATED_INQUIRY_LAP: 'Limited Dedicated Inquiry', |
| HCI_GENERAL_INQUIRY_LAP: 'General Inquiry' |
| } |
| |
| # Inquiry Mode |
| HCI_STANDARD_INQUIRY_MODE = 0x00 |
| HCI_INQUIRY_WITH_RSSI_MODE = 0x01 |
| HCI_EXTENDED_INQUIRY_MODE = 0x02 |
| |
| # Page Scan Repetition Mode |
| HCI_R0_PAGE_SCAN_REPETITION_MODE = 0x00 |
| HCI_R1_PAGE_SCAN_REPETITION_MODE = 0x01 |
| HCI_R2_PAGE_SCAN_REPETITION_MODE = 0x02 |
| |
| # IO Capability |
| HCI_DISPLAY_ONLY_IO_CAPABILITY = 0x00 |
| HCI_DISPLAY_YES_NO_IO_CAPABILITY = 0x01 |
| HCI_KEYBOARD_ONLY_IO_CAPABILITY = 0x02 |
| HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY = 0x03 |
| |
| HCI_IO_CAPABILITY_NAMES = { |
| HCI_DISPLAY_ONLY_IO_CAPABILITY: 'HCI_DISPLAY_ONLY_IO_CAPABILITY', |
| HCI_DISPLAY_YES_NO_IO_CAPABILITY: 'HCI_DISPLAY_YES_NO_IO_CAPABILITY', |
| HCI_KEYBOARD_ONLY_IO_CAPABILITY: 'HCI_KEYBOARD_ONLY_IO_CAPABILITY', |
| HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: 'HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY' |
| } |
| |
| # Authentication Requirements |
| HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS = 0x00 |
| HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS = 0x01 |
| HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS = 0x02 |
| HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS = 0x03 |
| HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS = 0x04 |
| HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS = 0x05 |
| |
| HCI_AUTHENTICATION_REQUIREMENTS_NAMES = { |
| HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS', |
| HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS', |
| HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_NOT_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS', |
| HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_REQUIRED_DEDICATED_BONDING_AUTHENTICATION_REQUIREMENTS', |
| HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS', |
| HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS: 'HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS' |
| } |
| |
| # Link Key Types |
| HCI_COMBINATION_KEY_TYPE = 0X00 |
| HCI_LOCAL_UNIT_KEY_TYPE = 0X01 |
| HCI_REMOTE_UNIT_KEY_TYPE = 0X02 |
| HCI_DEBUG_COMBINATION_KEY_TYPE = 0X03 |
| HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE = 0X04 |
| HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE = 0X05 |
| HCI_CHANGED_COMBINATION_KEY_TYPE = 0X06 |
| HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE = 0X07 |
| HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE = 0X08 |
| |
| HCI_LINK_TYPE_NAMES = { |
| HCI_COMBINATION_KEY_TYPE: 'HCI_COMBINATION_KEY_TYPE', |
| HCI_LOCAL_UNIT_KEY_TYPE: 'HCI_LOCAL_UNIT_KEY_TYPE', |
| HCI_REMOTE_UNIT_KEY_TYPE: 'HCI_REMOTE_UNIT_KEY_TYPE', |
| HCI_DEBUG_COMBINATION_KEY_TYPE: 'HCI_DEBUG_COMBINATION_KEY_TYPE', |
| HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE: 'HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE', |
| HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE: 'HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE', |
| HCI_CHANGED_COMBINATION_KEY_TYPE: 'HCI_CHANGED_COMBINATION_KEY_TYPE', |
| HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE: 'HCI_UNAUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE', |
| HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE: 'HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE' |
| } |
| |
| # Address types |
| HCI_PUBLIC_DEVICE_ADDRESS_TYPE = 0x00 |
| HCI_RANDOM_DEVICE_ADDRESS_TYPE = 0x01 |
| HCI_PUBLIC_IDENTITY_ADDRESS_TYPE = 0x02 |
| HCI_RANDOM_IDENTITY_ADDRESS_TYPE = 0x03 |
| |
| # Supported Commands Flags |
| # See Bluetooth spec @ 6.27 SUPPORTED COMMANDS |
| HCI_SUPPORTED_COMMANDS_FLAGS = ( |
| # Octet 0 |
| ( |
| HCI_INQUIRY_COMMAND, |
| HCI_INQUIRY_CANCEL_COMMAND, |
| HCI_PERIODIC_INQUIRY_MODE_COMMAND, |
| HCI_EXIT_PERIODIC_INQUIRY_MODE_COMMAND, |
| HCI_CREATE_CONNECTION_COMMAND, |
| HCI_DISCONNECT_COMMAND, |
| None, |
| HCI_CREATE_CONNECTION_CANCEL_COMMAND |
| ), |
| # Octet 1 |
| ( |
| HCI_ACCEPT_CONNECTION_REQUEST_COMMAND, |
| HCI_REJECT_CONNECTION_REQUEST_COMMAND, |
| HCI_LINK_KEY_REQUEST_REPLY_COMMAND, |
| HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_PIN_CODE_REQUEST_REPLY_COMMAND, |
| HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND, |
| HCI_AUTHENTICATION_REQUESTED_COMMAND |
| ), |
| # Octet 2 |
| ( |
| HCI_SET_CONNECTION_ENCRYPTION_COMMAND, |
| HCI_CHANGE_CONNECTION_LINK_KEY_COMMAND, |
| HCI_LINK_KEY_SELECTION_COMMAND, |
| HCI_REMOTE_NAME_REQUEST_COMMAND, |
| HCI_REMOTE_NAME_REQUEST_CANCEL_COMMAND, |
| HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND, |
| HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND, |
| HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND |
| ), |
| # Octet 3 |
| ( |
| HCI_READ_CLOCK_OFFSET_COMMAND, |
| HCI_READ_LMP_HANDLE_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| None, |
| None |
| ), |
| # Octet 4 |
| ( |
| None, |
| HCI_HOLD_MODE_COMMAND, |
| HCI_SNIFF_MODE_COMMAND, |
| HCI_EXIT_SNIFF_MODE_COMMAND, |
| None, |
| None, |
| HCI_QOS_SETUP_COMMAND, |
| HCI_ROLE_DISCOVERY_COMMAND |
| ), |
| # Octet 5 |
| ( |
| HCI_SWITCH_ROLE_COMMAND, |
| HCI_READ_LINK_POLICY_SETTINGS_COMMAND, |
| HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND, |
| HCI_READ_DEFAULT_LINK_POLICY_SETTINGS_COMMAND, |
| HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND, |
| HCI_FLOW_SPECIFICATION_COMMAND, |
| HCI_SET_EVENT_MASK_COMMAND, |
| HCI_RESET_COMMAND |
| ), |
| # Octet 6 |
| ( |
| HCI_SET_EVENT_FILTER_COMMAND, |
| HCI_FLUSH_COMMAND, |
| HCI_READ_PIN_TYPE_COMMAND, |
| HCI_WRITE_PIN_TYPE_COMMAND, |
| None, |
| HCI_READ_STORED_LINK_KEY_COMMAND, |
| HCI_WRITE_STORED_LINK_KEY_COMMAND, |
| HCI_DELETE_STORED_LINK_KEY_COMMAND |
| ), |
| # Octet 7 |
| ( |
| HCI_WRITE_LOCAL_NAME_COMMAND, |
| HCI_READ_LOCAL_NAME_COMMAND, |
| HCI_READ_CONNECTION_ACCEPT_TIMEOUT_COMMAND, |
| HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND, |
| HCI_READ_PAGE_TIMEOUT_COMMAND, |
| HCI_WRITE_PAGE_TIMEOUT_COMMAND, |
| HCI_READ_SCAN_ENABLE_COMMAND, |
| HCI_WRITE_SCAN_ENABLE_COMMAND |
| ), |
| # Octet 8 |
| ( |
| HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND, |
| HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND, |
| HCI_READ_INQUIRY_SCAN_ACTIVITY_COMMAND, |
| HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND, |
| HCI_READ_AUTHENTICATION_ENABLE_COMMAND, |
| HCI_WRITE_AUTHENTICATION_ENABLE_COMMAND, |
| None, |
| None |
| ), |
| # Octet 9 |
| ( |
| HCI_READ_CLASS_OF_DEVICE_COMMAND, |
| HCI_WRITE_CLASS_OF_DEVICE_COMMAND, |
| HCI_READ_VOICE_SETTING_COMMAND, |
| HCI_WRITE_VOICE_SETTING_COMMAND, |
| HCI_READ_AUTOMATIC_FLUSH_TIMEOUT_COMMAND, |
| HCI_WRITE_AUTOMATIC_FLUSH_TIMEOUT_COMMAND, |
| HCI_READ_NUM_BROADCAST_RETRANSMISSIONS_COMMAND, |
| HCI_WRITE_NUM_BROADCAST_RETRANSMISSIONS_COMMAND |
| ), |
| # Octet 10 |
| ( |
| HCI_READ_HOLD_MODE_ACTIVITY_COMMAND, |
| HCI_WRITE_HOLD_MODE_ACTIVITY_COMMAND, |
| HCI_READ_TRANSMIT_POWER_LEVEL_COMMAND, |
| HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND, |
| HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND, |
| HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND, |
| HCI_HOST_BUFFER_SIZE_COMMAND, |
| HCI_HOST_NUMBER_OF_COMPLETED_PACKETS_COMMAND |
| ), |
| # Octet 11 |
| ( |
| HCI_READ_LINK_SUPERVISION_TIMEOUT_COMMAND, |
| HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND, |
| HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND, |
| HCI_READ_CURRENT_IAC_LAP_COMMAND, |
| HCI_WRITE_CURRENT_IAC_LAP_COMMAND, |
| None, |
| None, |
| None |
| ), |
| # Octet 12 |
| ( |
| None, |
| HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION_COMMAND, |
| None, |
| None, |
| HCI_READ_INQUIRY_SCAN_TYPE_COMMAND, |
| HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND, |
| HCI_READ_INQUIRY_MODE_COMMAND, |
| HCI_WRITE_INQUIRY_MODE_COMMAND |
| ), |
| # Octet 13 |
| ( |
| HCI_READ_PAGE_SCAN_TYPE_COMMAND, |
| HCI_WRITE_PAGE_SCAN_TYPE_COMMAND, |
| HCI_READ_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND, |
| HCI_WRITE_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| ), |
| # Octet 14 |
| ( |
| None, |
| None, |
| None, |
| HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND, |
| None, |
| HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, |
| HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND, |
| HCI_READ_BUFFER_SIZE_COMMAND |
| ), |
| # Octet 15 |
| ( |
| None, |
| HCI_READ_BD_ADDR_COMMAND, |
| HCI_READ_FAILED_CONTACT_COUNTER_COMMAND, |
| HCI_RESET_FAILED_CONTACT_COUNTER_COMMAND, |
| HCI_READ_LINK_QUALITY_COMMAND, |
| HCI_READ_RSSI_COMMAND, |
| HCI_READ_AFH_CHANNEL_MAP_COMMAND, |
| HCI_READ_CLOCK_COMMAND |
| ), |
| # Octet 16 |
| ( |
| HCI_READ_LOOPBACK_MODE_COMMAND, |
| HCI_WRITE_LOOPBACK_MODE_COMMAND, |
| HCI_ENABLE_DEVICE_UNDER_TEST_MODE_COMMAND, |
| HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND, |
| HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, |
| HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, |
| None, |
| None, |
| ), |
| # Octet 17 |
| ( |
| HCI_READ_EXTENDED_INQUIRY_RESPONSE_COMMAND, |
| HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND, |
| HCI_REFRESH_ENCRYPTION_KEY_COMMAND, |
| None, |
| HCI_SNIFF_SUBRATING_COMMAND, |
| HCI_READ_SIMPLE_PAIRING_MODE_COMMAND, |
| HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND, |
| HCI_READ_LOCAL_OOB_DATA_COMMAND |
| ), |
| # Octet 18 |
| ( |
| HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND, |
| HCI_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_COMMAND, |
| HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND, |
| HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND, |
| None, |
| None, |
| None, |
| HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND |
| ), |
| # Octet 19 |
| ( |
| HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND, |
| HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND, |
| HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_REMOTE_OOB_DATA_REQUEST_REPLY_COMMAND, |
| HCI_WRITE_SIMPLE_PAIRING_DEBUG_MODE_COMMAND, |
| HCI_ENHANCED_FLUSH_COMMAND, |
| HCI_REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY_COMMAND |
| ), |
| # Octet 20 |
| ( |
| None, |
| None, |
| HCI_SEND_KEYPRESS_NOTIFICATION_COMMAND, |
| HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND, |
| None, |
| None, |
| None, |
| ), |
| # Octet 21 |
| ( |
| None, |
| None, |
| None, |
| None, |
| None, |
| None, |
| None, |
| None, |
| ), |
| # Octet 22 |
| ( |
| None, |
| None, |
| HCI_SET_EVENT_MASK_PAGE_2_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| None, |
| ), |
| # Octet 23 |
| ( |
| HCI_READ_FLOW_CONTROL_MODE_COMMAND, |
| HCI_WRITE_FLOW_CONTROL_MODE_COMMAND, |
| HCI_READ_DATA_BLOCK_SIZE_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| None, |
| ), |
| # Octet 24 |
| ( |
| HCI_READ_ENHANCED_TRANSMIT_POWER_LEVEL_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| HCI_READ_LE_HOST_SUPPORT_COMMAND, |
| HCI_WRITE_LE_HOST_SUPPORT_COMMAND, |
| None, |
| ), |
| # Octet 25 |
| ( |
| HCI_LE_SET_EVENT_MASK_COMMAND, |
| HCI_LE_READ_BUFFER_SIZE_COMMAND, |
| HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, |
| None, |
| HCI_LE_SET_RANDOM_ADDRESS_COMMAND, |
| HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND, |
| HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND, |
| HCI_LE_SET_ADVERTISING_DATA_COMMAND, |
| ), |
| # Octet 26 |
| ( |
| HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND, |
| HCI_LE_SET_ADVERTISING_ENABLE_COMMAND, |
| HCI_LE_SET_SCAN_PARAMETERS_COMMAND, |
| HCI_LE_SET_SCAN_ENABLE_COMMAND, |
| HCI_LE_CREATE_CONNECTION_COMMAND, |
| HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND, |
| HCI_LE_READ_FILTER_ACCEPT_LIST_SIZE_COMMAND, |
| HCI_LE_CLEAR_FILTER_ACCEPT_LIST_COMMAND |
| ), |
| # Octet 27 |
| ( |
| HCI_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_COMMAND, |
| HCI_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_COMMAND, |
| HCI_LE_CONNECTION_UPDATE_COMMAND, |
| HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND, |
| HCI_LE_READ_CHANNEL_MAP_COMMAND, |
| HCI_LE_READ_REMOTE_FEATURES_COMMAND, |
| HCI_LE_ENCRYPT_COMMAND, |
| HCI_LE_RAND_COMMAND |
| ), |
| # Octet 28 |
| ( |
| HCI_LE_ENABLE_ENCRYPTION_COMMAND, |
| HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND, |
| HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_LE_READ_SUPPORTED_STATES_COMMAND, |
| HCI_LE_RECEIVER_TEST_COMMAND, |
| HCI_LE_TRANSMITTER_TEST_COMMAND, |
| HCI_LE_TEST_END_COMMAND, |
| None, |
| ), |
| # Octet 29 |
| ( |
| None, |
| None, |
| None, |
| HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND, |
| HCI_ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, |
| HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND, |
| HCI_SET_MWS_CHANNEL_PARAMETERS_COMMAND, |
| HCI_SET_EXTERNAL_FRAME_CONFIGURATION_COMMAND |
| ), |
| # Octet 30 |
| ( |
| HCI_SET_MWS_SIGNALING_COMMAND, |
| HCI_SET_MWS_TRANSPORT_LAYER_COMMAND, |
| HCI_SET_MWS_SCAN_FREQUENCY_TABLE_COMMAND, |
| HCI_GET_MWS_TRANSPORT_LAYER_CONFIGURATION_COMMAND, |
| HCI_SET_MWS_PATTERN_CONFIGURATION_COMMAND, |
| HCI_SET_TRIGGERED_CLOCK_CAPTURE_COMMAND, |
| HCI_TRUNCATED_PAGE_COMMAND, |
| HCI_TRUNCATED_PAGE_CANCEL_COMMAND |
| ), |
| # Octet 31 |
| ( |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_COMMAND, |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_COMMAND, |
| HCI_START_SYNCHRONIZATION_TRAIN_COMMAND, |
| HCI_RECEIVE_SYNCHRONIZATION_TRAIN_COMMAND, |
| HCI_SET_RESERVED_LT_ADDR_COMMAND, |
| HCI_DELETE_RESERVED_LT_ADDR_COMMAND, |
| HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA_COMMAND, |
| HCI_READ_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND |
| ), |
| # Octet 32 |
| ( |
| HCI_WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND, |
| HCI_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_COMMAND, |
| HCI_READ_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND, |
| HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND, |
| HCI_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND, |
| HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND, |
| HCI_READ_LOCAL_OOB_EXTENDED_DATA_COMMAND, |
| HCI_WRITE_SECURE_CONNECTIONS_TEST_MODE_COMMAND |
| ), |
| # Octet 33 |
| ( |
| HCI_READ_EXTENDED_PAGE_TIMEOUT_COMMAND, |
| HCI_WRITE_EXTENDED_PAGE_TIMEOUT_COMMAND, |
| HCI_READ_EXTENDED_INQUIRY_LENGTH_COMMAND, |
| HCI_WRITE_EXTENDED_INQUIRY_LENGTH_COMMAND, |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND, |
| HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND, |
| HCI_LE_SET_DATA_LENGTH_COMMAND, |
| HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND |
| ), |
| # Octet 34 |
| ( |
| HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, |
| HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND, |
| HCI_LE_GENERATE_DHKEY_COMMAND, |
| HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND, |
| HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND, |
| HCI_LE_CLEAR_RESOLVING_LIST_COMMAND, |
| HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND, |
| HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND |
| ), |
| # Octet 35 |
| ( |
| HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND, |
| HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND, |
| HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND, |
| HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND, |
| HCI_LE_READ_PHY_COMMAND, |
| HCI_LE_SET_DEFAULT_PHY_COMMAND, |
| HCI_LE_SET_PHY_COMMAND, |
| HCI_LE_RECEIVER_TEST_V2_COMMAND |
| ), |
| # Octet 36 |
| ( |
| HCI_LE_TRANSMITTER_TEST_V2_COMMAND, |
| HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND, |
| HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND, |
| HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND, |
| HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND, |
| HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND, |
| HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND, |
| HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND, |
| ), |
| # Octet 37 |
| ( |
| HCI_LE_REMOVE_ADVERTISING_SET_COMMAND, |
| HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND, |
| HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND, |
| HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND, |
| HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND, |
| HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND, |
| HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND, |
| HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND |
| ), |
| # Octet 38 |
| ( |
| HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND, |
| HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND, |
| HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND, |
| HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND, |
| HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND, |
| HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND, |
| HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND, |
| HCI_LE_READ_TRANSMIT_POWER_COMMAND |
| ), |
| # Octet 39 |
| ( |
| HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND, |
| HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND, |
| HCI_LE_SET_PRIVACY_MODE_COMMAND, |
| HCI_LE_RECEIVER_TEST_V3_COMMAND, |
| HCI_LE_TRANSMITTER_TEST_V3_COMMAND, |
| HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS_COMMAND, |
| HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE_COMMAND, |
| HCI_LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE_COMMAND, |
| ), |
| # Octet 40 |
| ( |
| HCI_LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS_COMMAND, |
| HCI_LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS_COMMAND, |
| HCI_LE_CONNECTION_CTE_REQUEST_ENABLE_COMMAND, |
| HCI_LE_CONNECTION_CTE_RESPONSE_ENABLE_COMMAND, |
| HCI_LE_READ_ANTENNA_INFORMATION_COMMAND, |
| HCI_LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE_COMMAND, |
| HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_COMMAND, |
| HCI_LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER_COMMAND |
| ), |
| # Octet 41 |
| ( |
| HCI_LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND, |
| HCI_LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND, |
| HCI_LE_GENERATE_DHKEY_V2_COMMAND, |
| HCI_READ_LOCAL_SIMPLE_PAIRING_OPTIONS_COMMAND, |
| HCI_LE_MODIFY_SLEEP_CLOCK_ACCURACY_COMMAND, |
| HCI_LE_READ_BUFFER_SIZE_V2_COMMAND, |
| HCI_LE_READ_ISO_TX_SYNC_COMMAND, |
| HCI_LE_SET_CIG_PARAMETERS_COMMAND |
| ), |
| # Octet 42 |
| ( |
| HCI_LE_SET_CIG_PARAMETERS_TEST_COMMAND, |
| HCI_LE_CREATE_CIS_COMMAND, |
| HCI_LE_REMOVE_CIG_COMMAND, |
| HCI_LE_ACCEPT_CIS_REQUEST_COMMAND, |
| HCI_LE_REJECT_CIS_REQUEST_COMMAND, |
| HCI_LE_CREATE_BIG_COMMAND, |
| HCI_LE_CREATE_BIG_TEST_COMMAND, |
| HCI_LE_TERMINATE_BIG_COMMAND, |
| ), |
| # Octet 43 |
| ( |
| HCI_LE_BIG_CREATE_SYNC_COMMAND, |
| HCI_LE_BIG_TERMINATE_SYNC_COMMAND, |
| HCI_LE_REQUEST_PEER_SCA_COMMAND, |
| HCI_LE_SETUP_ISO_DATA_PATH_COMMAND, |
| HCI_LE_REMOVE_ISO_DATA_PATH_COMMAND, |
| HCI_LE_ISO_TRANSMIT_TEST_COMMAND, |
| HCI_LE_ISO_RECEIVE_TEST_COMMAND, |
| HCI_LE_ISO_READ_TEST_COUNTERS_COMMAND |
| ), |
| # Octet 44 |
| ( |
| HCI_LE_ISO_TEST_END_COMMAND, |
| HCI_LE_SET_HOST_FEATURE_COMMAND, |
| HCI_LE_READ_ISO_LINK_QUALITY_COMMAND, |
| HCI_LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL_COMMAND, |
| HCI_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL_COMMAND, |
| HCI_LE_SET_PATH_LOSS_REPORTING_PARAMETERS_COMMAND, |
| HCI_LE_SET_PATH_LOSS_REPORTING_ENABLE_COMMAND, |
| HCI_LE_SET_TRANSMIT_POWER_REPORTING_ENABLE_COMMAND |
| ), |
| # Octet 45 |
| ( |
| HCI_LE_TRANSMITTER_TEST_V4_COMMAND, |
| HCI_SET_ECOSYSTEM_BASE_INTERVAL_COMMAND, |
| HCI_READ_LOCAL_SUPPORTED_CODECS_V2_COMMAND, |
| HCI_READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES_COMMAND, |
| HCI_READ_LOCAL_SUPPORTED_CONTROLLER_DELAY_COMMAND, |
| HCI_CONFIGURE_DATA_PATH_COMMAND, |
| HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND, |
| HCI_SET_MIN_ENCRYPTION_KEY_SIZE_COMMAND |
| ), |
| # Octet 46 |
| ( |
| HCI_LE_SET_DEFAULT_SUBRATE_COMMAND, |
| HCI_LE_SUBRATE_REQUEST_COMMAND, |
| None, |
| None, |
| None, |
| None, |
| None, |
| None |
| ) |
| ) |
| |
| # LE Supported Features |
| HCI_LE_ENCRYPTION_LE_SUPPORTED_FEATURE = 0 |
| HCI_CONNECTION_PARAMETERS_REQUEST_PROCEDURE_LE_SUPPORTED_FEATURE = 1 |
| HCI_EXTENDED_REJECT_INDICATION_LE_SUPPORTED_FEATURE = 2 |
| HCI_PERIPHERAL_INITIATED_FEATURE_EXCHANGE_LE_SUPPORTED_FEATURE = 3 |
| HCI_LE_PING_LE_SUPPORTED_FEATURE = 4 |
| HCI_LE_DATA_PACKET_LENGTH_EXTENSION_LE_SUPPORTED_FEATURE = 5 |
| HCI_LL_PRIVACY_LE_SUPPORTED_FEATURE = 6 |
| HCI_EXTENDED_SCANNER_FILTER_POLICIES_LE_SUPPORTED_FEATURE = 7 |
| HCI_LE_2M_PHY_LE_SUPPORTED_FEATURE = 8 |
| HCI_STABLE_MODULATION_INDEX_TRANSMITTER_LE_SUPPORTED_FEATURE = 9 |
| HCI_STABLE_MODULATION_INDEX_RECEIVER_LE_SUPPORTED_FEATURE = 10 |
| HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE = 11 |
| HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE = 12 |
| HCI_LE_PERIODIC_ADVERTISING_LE_SUPPORTED_FEATURE = 13 |
| HCI_CHANNEL_SELECTION_ALGORITHM_2_LE_SUPPORTED_FEATURE = 14 |
| HCI_LE_POWER_CLASS_1_LE_SUPPORTED_FEATURE = 15 |
| HCI_MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE_LE_SUPPORTED_FEATURE = 16 |
| HCI_CONNECTION_CTE_REQUEST_LE_SUPPORTED_FEATURE = 17 |
| HCI_CONNECTION_CTE_RESPONSE_LE_SUPPORTED_FEATURE = 18 |
| HCI_CONNECTIONLESS_CTE_TRANSMITTER_LE_SUPPORTED_FEATURE = 19 |
| HCI_CONNECTIONLESS_CTR_RECEIVER_LE_SUPPORTED_FEATURE = 20 |
| HCI_ANTENNA_SWITCHING_DURING_CTE_TRANSMISSION_LE_SUPPORTED_FEATURE = 21 |
| HCI_ANTENNA_SWITCHING_DURING_CTE_RECEPTION_LE_SUPPORTED_FEATURE = 22 |
| HCI_RECEIVING_CONSTANT_TONE_EXTENSIONS_LE_SUPPORTED_FEATURE = 23 |
| HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_SENDER_LE_SUPPORTED_FEATURE = 24 |
| HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECIPIENT_LE_SUPPORTED_FEATURE = 25 |
| HCI_SLEEP_CLOCK_ACCURACY_UPDATES_LE_SUPPORTED_FEATURE = 26 |
| HCI_REMOTE_PUBLIC_KEY_VALIDATION_LE_SUPPORTED_FEATURE = 27 |
| HCI_CONNECTED_ISOCHRONOUS_STREAM_CENTRAL_LE_SUPPORTED_FEATURE = 28 |
| HCI_CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL_LE_SUPPORTED_FEATURE = 29 |
| HCI_ISOCHRONOUS_BROADCASTER_LE_SUPPORTED_FEATURE = 30 |
| HCI_SYNCHRONIZED_RECEIVER_LE_SUPPORTED_FEATURE = 31 |
| HCI_CONNECTED_ISOCHRONOUS_STREAM_LE_SUPPORTED_FEATURE = 32 |
| HCI_LE_POWER_CONTROL_REQUEST_LE_SUPPORTED_FEATURE = 33 |
| HCI_LE_POWER_CONTROL_REQUEST_DUP_LE_SUPPORTED_FEATURE = 34 |
| HCI_LE_PATH_LOSS_MONITORING_LE_SUPPORTED_FEATURE = 35 |
| HCI_PERIODIC_ADVERTISING_ADI_SUPPORT_LE_SUPPORTED_FEATURE = 36 |
| HCI_CONNECTION_SUBRATING_LE_SUPPORTED_FEATURE = 37 |
| HCI_CONNECTION_SUBRATING_HOST_SUPPORT_LE_SUPPORTED_FEATURE = 38 |
| HCI_CHANNEL_CLASSIFICATION_LE_SUPPORTED_FEATURE = 39 |
| |
| HCI_LE_SUPPORTED_FEATURES_NAMES = { |
| flag: feature_name for (feature_name, flag) in globals().items() |
| if feature_name.startswith('HCI_') and feature_name.endswith('_LE_SUPPORTED_FEATURE') |
| } |
| |
| # fmt: on |
| # pylint: enable=line-too-long |
| # pylint: disable=invalid-name |
| |
| # ----------------------------------------------------------------------------- |
| # pylint: disable-next=unnecessary-lambda |
| STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)} |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_Constant: |
| @staticmethod |
| def status_name(status): |
| return HCI_ERROR_NAMES.get(status, f'0x{status:02X}') |
| |
| @staticmethod |
| def error_name(status): |
| return HCI_ERROR_NAMES.get(status, f'0x{status:02X}') |
| |
| @staticmethod |
| def role_name(role): |
| return HCI_ROLE_NAMES.get(role, str(role)) |
| |
| @staticmethod |
| def le_phy_name(phy): |
| return HCI_LE_PHY_NAMES.get(phy, str(phy)) |
| |
| @staticmethod |
| def inquiry_lap_name(lap): |
| return HCI_INQUIRY_LAP_NAMES.get(lap, f'0x{lap:06X}') |
| |
| @staticmethod |
| def io_capability_name(io_capability): |
| return HCI_IO_CAPABILITY_NAMES.get(io_capability, f'0x{io_capability:02X}') |
| |
| @staticmethod |
| def authentication_requirements_name(authentication_requirements): |
| return HCI_AUTHENTICATION_REQUIREMENTS_NAMES.get( |
| authentication_requirements, f'0x{authentication_requirements:02X}' |
| ) |
| |
| @staticmethod |
| def link_key_type_name(link_key_type): |
| return HCI_LINK_TYPE_NAMES.get(link_key_type, f'0x{link_key_type:02X}') |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_Error(ProtocolError): |
| def __init__(self, error_code): |
| super().__init__( |
| error_code, |
| error_namespace='hci', |
| error_name=HCI_Constant.error_name(error_code), |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_StatusError(ProtocolError): |
| def __init__(self, response): |
| super().__init__( |
| response.status, |
| error_namespace=HCI_Command.command_name(response.command_opcode), |
| error_name=HCI_Constant.status_name(response.status), |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Generic HCI object |
| # ----------------------------------------------------------------------------- |
| class HCI_Object: |
| @staticmethod |
| def init_from_fields(hci_object, fields, values): |
| if isinstance(values, dict): |
| for field_name, _ in fields: |
| setattr(hci_object, field_name, values[field_name]) |
| else: |
| for field_name, field_value in zip(fields, values): |
| setattr(hci_object, field_name, field_value) |
| |
| @staticmethod |
| def init_from_bytes(hci_object, data, offset, fields): |
| parsed = HCI_Object.dict_from_bytes(data, offset, fields) |
| HCI_Object.init_from_fields(hci_object, parsed.keys(), parsed.values()) |
| |
| @staticmethod |
| def dict_from_bytes(data, offset, fields): |
| result = collections.OrderedDict() |
| for (field_name, field_type) in fields: |
| # The field_type may be a dictionary with a mapper, parser, and/or size |
| if isinstance(field_type, dict): |
| if 'size' in field_type: |
| field_type = field_type['size'] |
| elif 'parser' in field_type: |
| field_type = field_type['parser'] |
| |
| # Parse the field |
| if field_type == '*': |
| # The rest of the bytes |
| field_value = data[offset:] |
| offset += len(field_value) |
| elif field_type == 1: |
| # 8-bit unsigned |
| field_value = data[offset] |
| offset += 1 |
| elif field_type == -1: |
| # 8-bit signed |
| field_value = struct.unpack_from('b', data, offset)[0] |
| offset += 1 |
| elif field_type == 2: |
| # 16-bit unsigned |
| field_value = struct.unpack_from('<H', data, offset)[0] |
| offset += 2 |
| elif field_type == '>2': |
| # 16-bit unsigned big-endian |
| field_value = struct.unpack_from('>H', data, offset)[0] |
| offset += 2 |
| elif field_type == -2: |
| # 16-bit signed |
| field_value = struct.unpack_from('<h', data, offset)[0] |
| offset += 2 |
| elif field_type == 3: |
| # 24-bit unsigned |
| padded = data[offset : offset + 3] + bytes([0]) |
| field_value = struct.unpack('<I', padded)[0] |
| offset += 3 |
| elif field_type == 4: |
| # 32-bit unsigned |
| field_value = struct.unpack_from('<I', data, offset)[0] |
| offset += 4 |
| elif field_type == '>4': |
| # 32-bit unsigned big-endian |
| field_value = struct.unpack_from('>I', data, offset)[0] |
| offset += 4 |
| elif isinstance(field_type, int) and 4 < field_type <= 256: |
| # Byte array (from 5 up to 256 bytes) |
| field_value = data[offset : offset + field_type] |
| offset += field_type |
| elif callable(field_type): |
| offset, field_value = field_type(data, offset) |
| else: |
| raise ValueError(f'unknown field type {field_type}') |
| |
| result[field_name] = field_value |
| |
| return result |
| |
| @staticmethod |
| def dict_to_bytes(hci_object, fields): |
| result = bytearray() |
| for (field_name, field_type) in fields: |
| # The field_type may be a dictionary with a mapper, parser, serializer, |
| # and/or size |
| serializer = None |
| if isinstance(field_type, dict): |
| if 'serializer' in field_type: |
| serializer = field_type['serializer'] |
| if 'size' in field_type: |
| field_type = field_type['size'] |
| |
| # Serialize the field |
| field_value = hci_object[field_name] |
| if serializer: |
| field_bytes = serializer(field_value) |
| elif field_type == 1: |
| # 8-bit unsigned |
| field_bytes = bytes([field_value]) |
| elif field_type == -1: |
| # 8-bit signed |
| field_bytes = struct.pack('b', field_value) |
| elif field_type == 2: |
| # 16-bit unsigned |
| field_bytes = struct.pack('<H', field_value) |
| elif field_type == '>2': |
| # 16-bit unsigned big-endian |
| field_bytes = struct.pack('>H', field_value) |
| elif field_type == -2: |
| # 16-bit signed |
| field_bytes = struct.pack('<h', field_value) |
| elif field_type == 3: |
| # 24-bit unsigned |
| field_bytes = struct.pack('<I', field_value)[0:3] |
| elif field_type == 4: |
| # 32-bit unsigned |
| field_bytes = struct.pack('<I', field_value) |
| elif field_type == '>4': |
| # 32-bit unsigned big-endian |
| field_bytes = struct.pack('>I', field_value) |
| elif field_type == '*': |
| if isinstance(field_value, int): |
| if 0 <= field_value <= 255: |
| field_bytes = bytes([field_value]) |
| else: |
| raise ValueError('value too large for *-typed field') |
| else: |
| field_bytes = bytes(field_value) |
| elif isinstance(field_value, (bytes, bytearray)) or hasattr( |
| field_value, 'to_bytes' |
| ): |
| field_bytes = bytes(field_value) |
| if isinstance(field_type, int) and 4 < field_type <= 256: |
| # Truncate or Pad with zeros if the field is too long or too short |
| if len(field_bytes) < field_type: |
| field_bytes += bytes(field_type - len(field_bytes)) |
| elif len(field_bytes) > field_type: |
| field_bytes = field_bytes[:field_type] |
| else: |
| raise ValueError( |
| f"don't know how to serialize type {type(field_value)}" |
| ) |
| |
| result += field_bytes |
| |
| return bytes(result) |
| |
| @classmethod |
| def from_bytes(cls, data, offset, fields): |
| return cls(fields, **cls.dict_from_bytes(data, offset, fields)) |
| |
| def to_bytes(self): |
| return HCI_Object.dict_to_bytes(self.__dict__, self.fields) |
| |
| @staticmethod |
| def parse_length_prefixed_bytes(data, offset): |
| length = data[offset] |
| return offset + 1 + length, data[offset + 1 : offset + 1 + length] |
| |
| @staticmethod |
| def serialize_length_prefixed_bytes(data, padded_size=0): |
| prefixed_size = 1 + len(data) |
| padding = ( |
| bytes(padded_size - prefixed_size) if prefixed_size < padded_size else b'' |
| ) |
| return bytes([len(data)]) + data + padding |
| |
| @staticmethod |
| def format_field_value(value, indentation): |
| if isinstance(value, bytes): |
| return value.hex() |
| |
| if isinstance(value, HCI_Object): |
| return '\n' + value.to_string(indentation) |
| |
| return str(value) |
| |
| @staticmethod |
| def format_fields(hci_object, keys, indentation='', value_mappers=None): |
| if not keys: |
| return '' |
| |
| # Measure the widest field name |
| max_field_name_length = max( |
| (len(key[0] if isinstance(key, tuple) else key) for key in keys) |
| ) |
| |
| # Build array of formatted key:value pairs |
| fields = [] |
| for key in keys: |
| value_mapper = None |
| if isinstance(key, tuple): |
| # The key has an associated specifier |
| key, specifier = key |
| |
| # Get the value mapper from the specifier |
| if isinstance(specifier, dict): |
| value_mapper = specifier.get('mapper') |
| |
| # Get the value for the field |
| value = hci_object[key] |
| |
| # Map the value if needed |
| if value_mappers: |
| value_mapper = value_mappers.get(key, value_mapper) |
| if value_mapper is not None: |
| value = value_mapper(value) |
| |
| # Get the string representation of the value |
| value_str = HCI_Object.format_field_value( |
| value, indentation=indentation + ' ' |
| ) |
| |
| # Add the field to the formatted result |
| key_str = color(f'{key + ":":{1 + max_field_name_length}}', 'cyan') |
| fields.append(f'{indentation}{key_str} {value_str}') |
| |
| return '\n'.join(fields) |
| |
| def __bytes__(self): |
| return self.to_bytes() |
| |
| def __init__(self, fields, **kwargs): |
| self.fields = fields |
| self.init_from_fields(self, fields, kwargs) |
| |
| def to_string(self, indentation='', value_mappers=None): |
| return HCI_Object.format_fields( |
| self.__dict__, self.fields, indentation, value_mappers |
| ) |
| |
| def __str__(self): |
| return self.to_string() |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Bluetooth Address |
| # ----------------------------------------------------------------------------- |
| class Address: |
| ''' |
| Bluetooth Address (see Bluetooth spec Vol 6, Part B - 1.3 DEVICE ADDRESS) |
| NOTE: the address bytes are stored in little-endian byte order here, so |
| address[0] is the LSB of the address, address[5] is the MSB. |
| ''' |
| |
| PUBLIC_DEVICE_ADDRESS = 0x00 |
| RANDOM_DEVICE_ADDRESS = 0x01 |
| PUBLIC_IDENTITY_ADDRESS = 0x02 |
| RANDOM_IDENTITY_ADDRESS = 0x03 |
| |
| ADDRESS_TYPE_NAMES = { |
| PUBLIC_DEVICE_ADDRESS: 'PUBLIC_DEVICE_ADDRESS', |
| RANDOM_DEVICE_ADDRESS: 'RANDOM_DEVICE_ADDRESS', |
| PUBLIC_IDENTITY_ADDRESS: 'PUBLIC_IDENTITY_ADDRESS', |
| RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS', |
| } |
| |
| # Type declarations |
| NIL: Address |
| ANY: Address |
| ANY_RANDOM: Address |
| |
| # pylint: disable-next=unnecessary-lambda |
| ADDRESS_TYPE_SPEC = {'size': 1, 'mapper': lambda x: Address.address_type_name(x)} |
| |
| @staticmethod |
| def address_type_name(address_type): |
| return name_or_number(Address.ADDRESS_TYPE_NAMES, address_type) |
| |
| @staticmethod |
| def from_string_for_transport(string, transport): |
| if transport == BT_BR_EDR_TRANSPORT: |
| address_type = Address.PUBLIC_DEVICE_ADDRESS |
| else: |
| address_type = Address.RANDOM_DEVICE_ADDRESS |
| return Address(string, address_type) |
| |
| @staticmethod |
| def parse_address(data, offset): |
| # Fix the type to a default value. This is used for parsing type-less Classic |
| # addresses |
| return Address.parse_address_with_type( |
| data, offset, Address.PUBLIC_DEVICE_ADDRESS |
| ) |
| |
| @staticmethod |
| def parse_address_with_type(data, offset, address_type): |
| return offset + 6, Address(data[offset : offset + 6], address_type) |
| |
| @staticmethod |
| def parse_address_preceded_by_type(data, offset): |
| address_type = data[offset - 1] |
| return Address.parse_address_with_type(data, offset, address_type) |
| |
| def __init__( |
| self, address: Union[bytes, str], address_type: int = RANDOM_DEVICE_ADDRESS |
| ): |
| ''' |
| Initialize an instance. `address` may be a byte array in little-endian |
| format, or a hex string in big-endian format (with optional ':' |
| separators between the bytes). |
| If the address is a string suffixed with '/P', `address_type` is ignored and |
| the type is set to PUBLIC_DEVICE_ADDRESS. |
| ''' |
| if isinstance(address, bytes): |
| self.address_bytes = address |
| else: |
| # Check if there's a '/P' type specifier |
| if address.endswith('P'): |
| address_type = Address.PUBLIC_DEVICE_ADDRESS |
| address = address[:-2] |
| |
| if len(address) == 12 + 5: |
| # Form with ':' separators |
| address = address.replace(':', '') |
| self.address_bytes = bytes(reversed(bytes.fromhex(address))) |
| |
| if len(self.address_bytes) != 6: |
| raise ValueError('invalid address length') |
| |
| self.address_type = address_type |
| |
| def clone(self): |
| return Address(self.address_bytes, self.address_type) |
| |
| @property |
| def is_public(self): |
| return self.address_type in ( |
| self.PUBLIC_DEVICE_ADDRESS, |
| self.PUBLIC_IDENTITY_ADDRESS, |
| ) |
| |
| @property |
| def is_random(self): |
| return not self.is_public |
| |
| @property |
| def is_resolved(self): |
| return self.address_type in ( |
| self.PUBLIC_IDENTITY_ADDRESS, |
| self.RANDOM_IDENTITY_ADDRESS, |
| ) |
| |
| @property |
| def is_resolvable(self): |
| return self.address_type == self.RANDOM_DEVICE_ADDRESS and ( |
| self.address_bytes[5] >> 6 == 1 |
| ) |
| |
| @property |
| def is_static(self): |
| return self.is_random and (self.address_bytes[5] >> 6 == 3) |
| |
| def to_bytes(self): |
| return self.address_bytes |
| |
| def __bytes__(self): |
| return self.to_bytes() |
| |
| def __hash__(self): |
| return hash(self.address_bytes) |
| |
| def __eq__(self, other): |
| return ( |
| self.address_bytes == other.address_bytes |
| and self.is_public == other.is_public |
| ) |
| |
| def __str__(self): |
| ''' |
| String representation of the address, MSB first |
| ''' |
| result = ':'.join([f'{x:02X}' for x in reversed(self.address_bytes)]) |
| if not self.is_public: |
| return result |
| return result + '/P' |
| |
| |
| # Predefined address values |
| Address.NIL = Address(b"\xff\xff\xff\xff\xff\xff", Address.PUBLIC_DEVICE_ADDRESS) |
| Address.ANY = Address(b"\x00\x00\x00\x00\x00\x00", Address.PUBLIC_DEVICE_ADDRESS) |
| Address.ANY_RANDOM = Address(b"\x00\x00\x00\x00\x00\x00", Address.RANDOM_DEVICE_ADDRESS) |
| |
| # ----------------------------------------------------------------------------- |
| class OwnAddressType: |
| PUBLIC = 0 |
| RANDOM = 1 |
| RESOLVABLE_OR_PUBLIC = 2 |
| RESOLVABLE_OR_RANDOM = 3 |
| |
| TYPE_NAMES = { |
| PUBLIC: 'PUBLIC', |
| RANDOM: 'RANDOM', |
| RESOLVABLE_OR_PUBLIC: 'RESOLVABLE_OR_PUBLIC', |
| RESOLVABLE_OR_RANDOM: 'RESOLVABLE_OR_RANDOM', |
| } |
| |
| @staticmethod |
| def type_name(type_id): |
| return name_or_number(OwnAddressType.TYPE_NAMES, type_id) |
| |
| # pylint: disable-next=unnecessary-lambda |
| TYPE_SPEC = {'size': 1, 'mapper': lambda x: OwnAddressType.type_name(x)} |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_Packet: |
| ''' |
| Abstract Base class for HCI packets |
| ''' |
| |
| hci_packet_type: int |
| |
| @staticmethod |
| def from_bytes(packet): |
| packet_type = packet[0] |
| |
| if packet_type == HCI_COMMAND_PACKET: |
| return HCI_Command.from_bytes(packet) |
| |
| if packet_type == HCI_ACL_DATA_PACKET: |
| return HCI_AclDataPacket.from_bytes(packet) |
| |
| if packet_type == HCI_EVENT_PACKET: |
| return HCI_Event.from_bytes(packet) |
| |
| return HCI_CustomPacket(packet) |
| |
| def __init__(self, name): |
| self.name = name |
| |
| def __bytes__(self) -> bytes: |
| raise NotImplementedError |
| |
| def __repr__(self) -> str: |
| return self.name |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_CustomPacket(HCI_Packet): |
| def __init__(self, payload): |
| super().__init__('HCI_CUSTOM_PACKET') |
| self.hci_packet_type = payload[0] |
| self.payload = payload |
| |
| def __bytes__(self) -> bytes: |
| return self.payload |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_Command(HCI_Packet): |
| ''' |
| See Bluetooth spec @ Vol 2, Part E - 5.4.1 HCI Command Packet |
| ''' |
| |
| hci_packet_type = HCI_COMMAND_PACKET |
| command_classes: Dict[int, Type[HCI_Command]] = {} |
| |
| @staticmethod |
| def command(fields=(), return_parameters_fields=()): |
| ''' |
| Decorator used to declare and register subclasses |
| ''' |
| |
| def inner(cls): |
| cls.name = cls.__name__.upper() |
| cls.op_code = key_with_value(HCI_COMMAND_NAMES, cls.name) |
| if cls.op_code is None: |
| raise KeyError(f'command {cls.name} not found in HCI_COMMAND_NAMES') |
| cls.fields = fields |
| cls.return_parameters_fields = return_parameters_fields |
| |
| # Patch the __init__ method to fix the op_code |
| if fields is not None: |
| |
| def init(self, parameters=None, **kwargs): |
| return HCI_Command.__init__(self, cls.op_code, parameters, **kwargs) |
| |
| cls.__init__ = init |
| |
| # Register a factory for this class |
| HCI_Command.command_classes[cls.op_code] = cls |
| |
| return cls |
| |
| return inner |
| |
| @staticmethod |
| def from_bytes(packet): |
| op_code, length = struct.unpack_from('<HB', packet, 1) |
| parameters = packet[4:] |
| if len(parameters) != length: |
| raise ValueError('invalid packet length') |
| |
| # Look for a registered class |
| cls = HCI_Command.command_classes.get(op_code) |
| if cls is None: |
| # No class registered, just use a generic instance |
| return HCI_Command(op_code, parameters) |
| |
| # Create a new instance |
| if (fields := getattr(cls, 'fields', None)) is not None: |
| self = cls.__new__(cls) |
| HCI_Command.__init__(self, op_code, parameters) |
| HCI_Object.init_from_bytes(self, parameters, 0, fields) |
| return self |
| |
| return cls.from_parameters(parameters) |
| |
| @staticmethod |
| def command_name(op_code): |
| name = HCI_COMMAND_NAMES.get(op_code) |
| if name is not None: |
| return name |
| return f'[OGF=0x{op_code >> 10:02x}, OCF=0x{op_code & 0x3FF:04x}]' |
| |
| @classmethod |
| def create_return_parameters(cls, **kwargs): |
| return HCI_Object(cls.return_parameters_fields, **kwargs) |
| |
| def __init__(self, op_code, parameters=None, **kwargs): |
| super().__init__(HCI_Command.command_name(op_code)) |
| if (fields := getattr(self, 'fields', None)) and kwargs: |
| HCI_Object.init_from_fields(self, fields, kwargs) |
| if parameters is None: |
| parameters = HCI_Object.dict_to_bytes(kwargs, fields) |
| self.op_code = op_code |
| self.parameters = parameters |
| |
| def to_bytes(self): |
| parameters = b'' if self.parameters is None else self.parameters |
| return ( |
| struct.pack('<BHB', HCI_COMMAND_PACKET, self.op_code, len(parameters)) |
| + parameters |
| ) |
| |
| def __bytes__(self): |
| return self.to_bytes() |
| |
| def __str__(self): |
| result = color(self.name, 'green') |
| if fields := getattr(self, 'fields', None): |
| result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, ' ') |
| else: |
| if self.parameters: |
| result += f': {self.parameters.hex()}' |
| return result |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('lap', {'size': 3, 'mapper': HCI_Constant.inquiry_lap_name}), |
| ('inquiry_length', 1), |
| ('num_responses', 1), |
| ] |
| ) |
| class HCI_Inquiry_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.1 Inquiry Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Inquiry_Cancel_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.2 Inquiry Cancel Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('packet_type', 2), |
| ('page_scan_repetition_mode', 1), |
| ('reserved', 1), |
| ('clock_offset', 2), |
| ('allow_role_switch', 1), |
| ] |
| ) |
| class HCI_Create_Connection_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.5 Create Connection Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), |
| ] |
| ) |
| class HCI_Disconnect_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.6 Disconnect Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_Create_Connection_Cancel_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.7 Create Connection Cancel Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('bd_addr', Address.parse_address), ('role', 1)]) |
| class HCI_Accept_Connection_Request_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.8 Accept Connection Request Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), |
| ] |
| ) |
| class HCI_Reject_Connection_Request_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.9 Reject Connection Request Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('bd_addr', Address.parse_address), ('link_key', 16)]) |
| class HCI_Link_Key_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.10 Link Key Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_Link_Key_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.11 Link Key Request Negative Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_PIN_Code_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.13 PIN Code Request Negative Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('packet_type', 2)]) |
| class HCI_Change_Connection_Packet_Type_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.14 Change Connection Packet Type Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_Authentication_Requested_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.15 Authentication Requested Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('encryption_enable', 1)]) |
| class HCI_Set_Connection_Encryption_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.16 Set Connection Encryption Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('page_scan_repetition_mode', 1), |
| ('reserved', 1), |
| ('clock_offset', 2), |
| ] |
| ) |
| class HCI_Remote_Name_Request_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.19 Remote Name Request Command |
| ''' |
| |
| R0 = 0x00 |
| R1 = 0x01 |
| R2 = 0x02 |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_Read_Remote_Supported_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.21 Read Remote Supported Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('page_number', 1)]) |
| class HCI_Read_Remote_Extended_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.22 Read Remote Extended Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_Read_Remote_Version_Information_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.23 Read Remote Version Information Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_Read_Clock_Offset_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.23 Read Clock Offset Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[ |
| ('bd_addr', Address.parse_address), |
| ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), |
| ('oob_data_present', 1), |
| ( |
| 'authentication_requirements', |
| {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}, |
| ), |
| ], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_IO_Capability_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.29 IO Capability Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_User_Confirmation_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.30 User Confirmation Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_User_Confirmation_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.31 User Confirmation Request Negative Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address), ('numeric_value', 4)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_User_Passkey_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.32 User Passkey Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ], |
| ) |
| class HCI_User_Passkey_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.33 User Passkey Request Negative Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('transmit_bandwidth', 4), |
| ('receive_bandwidth', 4), |
| ('transmit_coding_format', 5), |
| ('receive_coding_format', 5), |
| ('transmit_codec_frame_size', 2), |
| ('receive_codec_frame_size', 2), |
| ('input_bandwidth', 4), |
| ('output_bandwidth', 4), |
| ('input_coding_format', 5), |
| ('output_coding_format', 5), |
| ('input_coded_data_size', 2), |
| ('output_coded_data_size', 2), |
| ('input_pcm_data_format', 1), |
| ('output_pcm_data_format', 1), |
| ('input_pcm_sample_payload_msb_position', 1), |
| ('output_pcm_sample_payload_msb_position', 1), |
| ('input_data_path', 1), |
| ('output_data_path', 1), |
| ('input_transport_unit_size', 1), |
| ('output_transport_unit_size', 1), |
| ('max_latency', 2), |
| ('packet_type', 2), |
| ('retransmission_effort', 1), |
| ] |
| ) |
| class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.1.45 Enhanced Setup Synchronous Connection Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('sniff_max_interval', 2), |
| ('sniff_min_interval', 2), |
| ('sniff_attempt', 2), |
| ('sniff_timeout', 2), |
| ] |
| ) |
| class HCI_Sniff_Mode_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.2 Sniff Mode Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_Exit_Sniff_Mode_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.3 Exit Sniff Mode Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('role', {'size': 1, 'mapper': HCI_Constant.role_name}), |
| ] |
| ) |
| class HCI_Switch_Role_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.8 Switch Role Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('link_policy_settings', 2)]) |
| class HCI_Write_Link_Policy_Settings_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.10 Write Link Policy Settings Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('default_link_policy_settings', 2)]) |
| class HCI_Write_Default_Link_Policy_Settings_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.12 Write Default Link Policy Settings Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('maximum_latency', 2), |
| ('minimum_remote_timeout', 2), |
| ('minimum_local_timeout', 2), |
| ] |
| ) |
| class HCI_Sniff_Subrating_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.2.14 Sniff Subrating Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('event_mask', 8)]) |
| class HCI_Set_Event_Mask_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.1 Set Event Mask Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Reset_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.2 Reset Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('filter_type', 1), |
| ('filter_condition', '*'), |
| ] |
| ) |
| class HCI_Set_Event_Filter_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.3 Set Event Filter Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address), ('read_all_flag', 1)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('max_num_keys', 2), |
| ('num_keys_read', 2), |
| ], |
| ) |
| class HCI_Read_Stored_Link_Key_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.8 Read Stored Link Key Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('bd_addr', Address.parse_address), ('delete_all_flag', 1)], |
| return_parameters_fields=[('status', STATUS_SPEC), ('num_keys_deleted', 2)], |
| ) |
| class HCI_Delete_Stored_Link_Key_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.10 Delete Stored Link Key Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string})] |
| ) |
| class HCI_Write_Local_Name_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.11 Write Local Name Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}), |
| ] |
| ) |
| class HCI_Read_Local_Name_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.12 Read Local Name Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_accept_timeout', 2)]) |
| class HCI_Write_Connection_Accept_Timeout_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.14 Write Connection Accept Timeout Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('page_timeout', 2)]) |
| class HCI_Write_Page_Timeout_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.16 Write Page Timeout Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('scan_enable', 1)]) |
| class HCI_Write_Scan_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.18 Write Scan Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('page_scan_interval', 2), |
| ('page_scan_window', 2), |
| ] |
| ) |
| class HCI_Read_Page_Scan_Activity_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.19 Read Page Scan Activity Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('page_scan_interval', 2), ('page_scan_window', 2)]) |
| class HCI_Write_Page_Scan_Activity_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.20 Write Page Scan Activity Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('inquiry_scan_interval', 2), ('inquiry_scan_window', 2)]) |
| class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.22 Write Inquiry Scan Activity Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), |
| ] |
| ) |
| class HCI_Read_Class_Of_Device_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.25 Read Class of Device Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('class_of_device', {'size': 3, 'mapper': map_class_of_device})]) |
| class HCI_Write_Class_Of_Device_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.26 Write Class of Device Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('voice_setting', 2)] |
| ) |
| class HCI_Read_Voice_Setting_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.27 Read Voice Setting Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('voice_setting', 2)]) |
| class HCI_Write_Voice_Setting_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.28 Write Voice Setting Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.36 Read Synchronous Flow Control Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('synchronous_flow_control_enable', 1)]) |
| class HCI_Write_Synchronous_Flow_Control_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.37 Write Synchronous Flow Control Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('host_acl_data_packet_length', 2), |
| ('host_synchronous_data_packet_length', 1), |
| ('host_total_num_acl_data_packets', 2), |
| ('host_total_num_synchronous_data_packets', 2), |
| ] |
| ) |
| class HCI_Host_Buffer_Size_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.39 Host Buffer Size Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('handle', 2), ('link_supervision_timeout', 2)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('handle', 2), |
| ], |
| ) |
| class HCI_Write_Link_Supervision_Timeout_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.42 Write Link Supervision Timeout Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('num_support_iac', 1)] |
| ) |
| class HCI_Read_Number_Of_Supported_IAC_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.43 Read Number Of Supported IAC Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('num_current_iac', 1), |
| ('iac_lap', '*'), # TODO: this should be parsed as an array |
| ] |
| ) |
| class HCI_Read_Current_IAC_LAP_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.44 Read Current IAC LAP Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('scan_type', 1)]) |
| class HCI_Write_Inquiry_Scan_Type_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.48 Write Inquiry Scan Type Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('inquiry_mode', 1)]) |
| class HCI_Write_Inquiry_Mode_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.50 Write Inquiry Mode Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('page_scan_type', 1)] |
| ) |
| class HCI_Read_Page_Scan_Type_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.51 Read Page Scan Type Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('page_scan_type', 1)]) |
| class HCI_Write_Page_Scan_Type_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.52 Write Page Scan Type Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('fec_required', 1), |
| ( |
| 'extended_inquiry_response', |
| {'size': 240, 'serializer': lambda x: padded_bytes(x, 240)}, |
| ), |
| ] |
| ) |
| class HCI_Write_Extended_Inquiry_Response_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.56 Write Extended Inquiry Response Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('simple_pairing_mode', 1)]) |
| class HCI_Write_Simple_Pairing_Mode_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.59 Write Simple Pairing Mode Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('tx_power', -1)] |
| ) |
| class HCI_Read_Inquiry_Response_Transmit_Power_Level_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.61 Read Inquiry Response Transmit Power Level Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('erroneous_data_reporting', 1)] |
| ) |
| class HCI_Read_Default_Erroneous_Data_Reporting_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.64 Read Default Erroneous Data Reporting Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('event_mask_page_2', 8)]) |
| class HCI_Set_Event_Mask_Page_2_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.69 Set Event Mask Page 2 Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Read_LE_Host_Support_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.78 Read LE Host Support Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('le_supported_host', 1), ('simultaneous_le_host', 1)]) |
| class HCI_Write_LE_Host_Support_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.79 Write LE Host Support Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('secure_connections_host_support', 1)]) |
| class HCI_Write_Secure_Connections_Host_Support_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.92 Write Secure Connections Host Support Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('authenticated_payload_timeout', 2)]) |
| class HCI_Write_Authenticated_Payload_Timeout_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.3.94 Write Authenticated Payload Timeout Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('hci_version', 1), |
| ('hci_subversion', 2), |
| ('lmp_version', 1), |
| ('company_identifier', 2), |
| ('lmp_subversion', 2), |
| ] |
| ) |
| class HCI_Read_Local_Version_Information_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.1 Read Local Version Information Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('supported_commands', 64)] |
| ) |
| class HCI_Read_Local_Supported_Commands_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.2 Read Local Supported Commands Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Read_Local_Supported_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.3 Read Local Supported Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('page_number', 1)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('page_number', 1), |
| ('maximum_page_number', 1), |
| ('extended_lmp_features', 8), |
| ], |
| ) |
| class HCI_Read_Local_Extended_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.4 Read Local Extended Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('hc_acl_data_packet_length', 2), |
| ('hc_synchronous_data_packet_length', 1), |
| ('hc_total_num_acl_data_packets', 2), |
| ('hc_total_num_synchronous_data_packets', 2), |
| ] |
| ) |
| class HCI_Read_Buffer_Size_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.5 Read Buffer Size Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ] |
| ) |
| class HCI_Read_BD_ADDR_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.6 Read BD_ADDR Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_Read_Local_Supported_Codecs_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('handle', 2)], |
| return_parameters_fields=[('status', STATUS_SPEC), ('handle', 2), ('rssi', -1)], |
| ) |
| class HCI_Read_RSSI_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.5.4 Read RSSI Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('connection_handle', 2)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('key_size', 1), |
| ], |
| ) |
| class HCI_Read_Encryption_Key_Size_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.5.7 Read Encryption Key Size Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('le_event_mask', 8)]) |
| class HCI_LE_Set_Event_Mask_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.1 LE Set Event Mask Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('hc_le_acl_data_packet_length', 2), |
| ('hc_total_num_le_acl_data_packets', 1), |
| ] |
| ) |
| class HCI_LE_Read_Buffer_Size_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.2 LE Read Buffer Size Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[('status', STATUS_SPEC), ('le_features', 8)] |
| ) |
| class HCI_LE_Read_Local_Supported_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.3 LE Read Local Supported Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ( |
| 'random_address', |
| lambda data, offset: Address.parse_address_with_type( |
| data, offset, Address.RANDOM_DEVICE_ADDRESS |
| ), |
| ) |
| ] |
| ) |
| class HCI_LE_Set_Random_Address_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.4 LE Set Random Address Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| # pylint: disable=line-too-long,unnecessary-lambda |
| [ |
| ('advertising_interval_min', 2), |
| ('advertising_interval_max', 2), |
| ( |
| 'advertising_type', |
| { |
| 'size': 1, |
| 'mapper': lambda x: HCI_LE_Set_Advertising_Parameters_Command.advertising_type_name( |
| x |
| ), |
| }, |
| ), |
| ('own_address_type', OwnAddressType.TYPE_SPEC), |
| ('peer_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_address', Address.parse_address_preceded_by_type), |
| ('advertising_channel_map', 1), |
| ('advertising_filter_policy', 1), |
| ] |
| ) |
| class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.5 LE Set Advertising Parameters Command |
| ''' |
| |
| ADV_IND = 0x00 |
| ADV_DIRECT_IND = 0x01 |
| ADV_SCAN_IND = 0x02 |
| ADV_NONCONN_IND = 0x03 |
| ADV_DIRECT_IND_LOW_DUTY = 0x04 |
| |
| ADVERTISING_TYPE_NAMES = { |
| ADV_IND: 'ADV_IND', |
| ADV_DIRECT_IND: 'ADV_DIRECT_IND', |
| ADV_SCAN_IND: 'ADV_SCAN_IND', |
| ADV_NONCONN_IND: 'ADV_NONCONN_IND', |
| ADV_DIRECT_IND_LOW_DUTY: 'ADV_DIRECT_IND_LOW_DUTY', |
| } |
| |
| @classmethod |
| def advertising_type_name(cls, advertising_type): |
| return name_or_number(cls.ADVERTISING_TYPE_NAMES, advertising_type) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| 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 |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ( |
| 'advertising_data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': functools.partial( |
| HCI_Object.serialize_length_prefixed_bytes, padded_size=32 |
| ), |
| }, |
| ) |
| ] |
| ) |
| class HCI_LE_Set_Advertising_Data_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.7 LE Set Advertising Data Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ( |
| 'scan_response_data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': functools.partial( |
| HCI_Object.serialize_length_prefixed_bytes, padded_size=32 |
| ), |
| }, |
| ) |
| ] |
| ) |
| class HCI_LE_Set_Scan_Response_Data_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.8 LE Set Scan Response Data Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('advertising_enable', 1)]) |
| class HCI_LE_Set_Advertising_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.9 LE Set Advertising Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('le_scan_type', 1), |
| ('le_scan_interval', 2), |
| ('le_scan_window', 2), |
| ('own_address_type', OwnAddressType.TYPE_SPEC), |
| ('scanning_filter_policy', 1), |
| ] |
| ) |
| class HCI_LE_Set_Scan_Parameters_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.10 LE Set Scan Parameters Command |
| ''' |
| |
| PASSIVE_SCANNING = 0 |
| ACTIVE_SCANNING = 1 |
| |
| BASIC_UNFILTERED_POLICY = 0x00 |
| BASIC_FILTERED_POLICY = 0x01 |
| EXTENDED_UNFILTERED_POLICY = 0x02 |
| EXTENDED_FILTERED_POLICY = 0x03 |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('le_scan_enable', 1), |
| ('filter_duplicates', 1), |
| ] |
| ) |
| class HCI_LE_Set_Scan_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.11 LE Set Scan Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('le_scan_interval', 2), |
| ('le_scan_window', 2), |
| ('initiator_filter_policy', 1), |
| ('peer_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_address', Address.parse_address_preceded_by_type), |
| ('own_address_type', OwnAddressType.TYPE_SPEC), |
| ('connection_interval_min', 2), |
| ('connection_interval_max', 2), |
| ('max_latency', 2), |
| ('supervision_timeout', 2), |
| ('min_ce_length', 2), |
| ('max_ce_length', 2), |
| ] |
| ) |
| class HCI_LE_Create_Connection_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.12 LE Create Connection Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Create_Connection_Cancel_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.13 LE Create Connection Cancel Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Read_Filter_Accept_List_Size_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.14 LE Read Filter Accept List Size Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Clear_Filter_Accept_List_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.15 LE Clear Filter Accept List Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('address_type', Address.ADDRESS_TYPE_SPEC), |
| ('address', Address.parse_address_preceded_by_type), |
| ] |
| ) |
| class HCI_LE_Add_Device_To_Filter_Accept_List_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.16 LE Add Device To Filter Accept List Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('address_type', Address.ADDRESS_TYPE_SPEC), |
| ('address', Address.parse_address_preceded_by_type), |
| ] |
| ) |
| class HCI_LE_Remove_Device_From_Filter_Accept_List_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.17 LE Remove Device From Filter Accept List Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('connection_interval_min', 2), |
| ('connection_interval_max', 2), |
| ('max_latency', 2), |
| ('supervision_timeout', 2), |
| ('min_ce_length', 2), |
| ('max_ce_length', 2), |
| ] |
| ) |
| class HCI_LE_Connection_Update_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.18 LE Connection Update Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_LE_Read_Remote_Features_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.21 LE Read Remote Features Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[("status", STATUS_SPEC), ("random_number", 8)] |
| ) |
| class HCI_LE_Rand_Command(HCI_Command): |
| """ |
| See Bluetooth spec @ 7.8.23 LE Rand Command |
| """ |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('random_number', 8), |
| ('encrypted_diversifier', 2), |
| ('long_term_key', 16), |
| ] |
| ) |
| class HCI_LE_Enable_Encryption_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.24 LE Enable Encryption Command |
| (renamed from "LE Start Encryption Command" in version prior to 5.2 of the |
| specification) |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2), ('long_term_key', 16)]) |
| class HCI_LE_Long_Term_Key_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.25 LE Long Term Key Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('connection_handle', 2)]) |
| class HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.26 LE Long Term Key Request Negative Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Read_Supported_States_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.27 LE Read Supported States Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('interval_min', 2), |
| ('interval_max', 2), |
| ('max_latency', 2), |
| ('timeout', 2), |
| ('min_ce_length', 2), |
| ('max_ce_length', 2), |
| ] |
| ) |
| class HCI_LE_Remote_Connection_Parameter_Request_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.31 LE Remote Connection Parameter Request Reply Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), |
| ] |
| ) |
| class HCI_LE_Remote_Connection_Parameter_Request_Negative_Reply_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.32 LE Remote Connection Parameter Request Negative Reply |
| Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[ |
| ('connection_handle', 2), |
| ('tx_octets', 2), |
| ('tx_time', 2), |
| ], |
| return_parameters_fields=[('status', STATUS_SPEC), ('connection_handle', 2)], |
| ) |
| class HCI_LE_Set_Data_Length_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.33 LE Set Data Length Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('suggested_max_tx_octets', 2), |
| ('suggested_max_tx_time', 2), |
| ] |
| ) |
| class HCI_LE_Read_Suggested_Default_Data_Length_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.34 LE Read Suggested Default Data Length Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('suggested_max_tx_octets', 2), ('suggested_max_tx_time', 2)]) |
| class HCI_LE_Write_Suggested_Default_Data_Length_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.35 LE Write Suggested Default Data Length Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_identity_address', Address.parse_address_preceded_by_type), |
| ('peer_irk', 16), |
| ('local_irk', 16), |
| ] |
| ) |
| class HCI_LE_Add_Device_To_Resolving_List_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.38 LE Add Device To Resolving List Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Clear_Resolving_List_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.40 LE Clear Resolving List Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('address_resolution_enable', 1)]) |
| class HCI_LE_Set_Address_Resolution_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.44 LE Set Address Resolution Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('rpa_timeout', 2)]) |
| class HCI_LE_Set_Resolvable_Private_Address_Timeout_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.45 LE Set Resolvable Private Address Timeout Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('supported_max_tx_octets', 2), |
| ('supported_max_tx_time', 2), |
| ('supported_max_rx_octets', 2), |
| ('supported_max_rx_time', 2), |
| ] |
| ) |
| class HCI_LE_Read_Maximum_Data_Length_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.46 LE Read Maximum Data Length Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| fields=[('connection_handle', 2)], |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('tx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('rx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ], |
| ) |
| class HCI_LE_Read_PHY_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.47 LE Read PHY Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ( |
| 'all_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings( |
| x, HCI_LE_Set_Default_PHY_Command.ANY_PHY_BIT_NAMES |
| ), |
| }, |
| ), |
| ( |
| 'tx_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), |
| }, |
| ), |
| ( |
| 'rx_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), |
| }, |
| ), |
| ] |
| ) |
| class HCI_LE_Set_Default_PHY_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.48 LE Set Default PHY Command |
| ''' |
| |
| ANY_TX_PHY_BIT = 0 |
| ANY_RX_PHY_BIT = 1 |
| |
| ANY_PHY_BIT_NAMES = ['Any TX', 'Any RX'] |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('connection_handle', 2), |
| ( |
| 'all_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings( |
| x, HCI_LE_Set_PHY_Command.ANY_PHY_BIT_NAMES |
| ), |
| }, |
| ), |
| ( |
| 'tx_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), |
| }, |
| ), |
| ( |
| 'rx_phys', |
| { |
| 'size': 1, |
| 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), |
| }, |
| ), |
| ('phy_options', 2), |
| ] |
| ) |
| class HCI_LE_Set_PHY_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.49 LE Set PHY Command |
| ''' |
| |
| ANY_TX_PHY_BIT = 0 |
| ANY_RX_PHY_BIT = 1 |
| |
| ANY_PHY_BIT_NAMES = ['Any TX', 'Any RX'] |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('advertising_handle', 1), |
| ( |
| 'random_address', |
| lambda data, offset: Address.parse_address_with_type( |
| data, offset, Address.RANDOM_DEVICE_ADDRESS |
| ), |
| ), |
| ] |
| ) |
| class HCI_LE_Set_Advertising_Set_Random_Address_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.52 LE Set Advertising Set Random Address Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| # pylint: disable=line-too-long,unnecessary-lambda |
| fields=[ |
| ('advertising_handle', 1), |
| ( |
| 'advertising_event_properties', |
| { |
| 'size': 2, |
| 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.advertising_properties_string( |
| x |
| ), |
| }, |
| ), |
| ('primary_advertising_interval_min', 3), |
| ('primary_advertising_interval_max', 3), |
| ( |
| 'primary_advertising_channel_map', |
| { |
| 'size': 1, |
| 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.channel_map_string( |
| x |
| ), |
| }, |
| ), |
| ('own_address_type', OwnAddressType.TYPE_SPEC), |
| ('peer_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_address', Address.parse_address_preceded_by_type), |
| ('advertising_filter_policy', 1), |
| ('advertising_tx_power', 1), |
| ('primary_advertising_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('secondary_advertising_max_skip', 1), |
| ('secondary_advertising_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('advertising_sid', 1), |
| ('scan_request_notification_enable', 1), |
| ], |
| return_parameters_fields=[('status', STATUS_SPEC), ('selected_tx__power', 1)], |
| ) |
| class HCI_LE_Set_Extended_Advertising_Parameters_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.53 LE Set Extended Advertising Parameters Command |
| ''' |
| |
| CONNECTABLE_ADVERTISING = 0 |
| SCANNABLE_ADVERTISING = 1 |
| DIRECTED_ADVERTISING = 2 |
| HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING = 3 |
| USE_LEGACY_ADVERTISING_PDUS = 4 |
| ANONYMOUS_ADVERTISING = 5 |
| INCLUDE_TX_POWER = 6 |
| |
| ADVERTISING_PROPERTIES_NAMES = ( |
| 'CONNECTABLE_ADVERTISING', |
| 'SCANNABLE_ADVERTISING', |
| 'DIRECTED_ADVERTISING', |
| 'HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING', |
| 'USE_LEGACY_ADVERTISING_PDUS', |
| 'ANONYMOUS_ADVERTISING', |
| 'INCLUDE_TX_POWER', |
| ) |
| |
| CHANNEL_37 = 0 |
| CHANNEL_38 = 1 |
| CHANNEL_39 = 2 |
| |
| CHANNEL_NAMES = ('37', '38', '39') |
| |
| @classmethod |
| def advertising_properties_string(cls, properties): |
| # pylint: disable=line-too-long |
| return f'[{",".join(bit_flags_to_strings(properties, cls.ADVERTISING_PROPERTIES_NAMES))}]' |
| |
| @classmethod |
| def channel_map_string(cls, channel_map): |
| return f'[{",".join(bit_flags_to_strings(channel_map, cls.CHANNEL_NAMES))}]' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| # pylint: disable=line-too-long,unnecessary-lambda |
| [ |
| ('advertising_handle', 1), |
| ( |
| 'operation', |
| { |
| 'size': 1, |
| 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name( |
| x |
| ), |
| }, |
| ), |
| ('fragment_preference', 1), |
| ( |
| 'advertising_data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': functools.partial( |
| HCI_Object.serialize_length_prefixed_bytes |
| ), |
| }, |
| ), |
| ] |
| ) |
| class HCI_LE_Set_Extended_Advertising_Data_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.54 LE Set Extended Advertising Data Command |
| ''' |
| |
| INTERMEDIATE_FRAGMENT = 0x00 |
| FIRST_FRAGMENT = 0x01 |
| LAST_FRAGMENT = 0x02 |
| COMPLETE_DATA = 0x03 |
| UNCHANGED_DATA = 0x04 |
| |
| OPERATION_NAMES = { |
| INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT', |
| FIRST_FRAGMENT: 'FIRST_FRAGMENT', |
| LAST_FRAGMENT: 'LAST_FRAGMENT', |
| COMPLETE_DATA: 'COMPLETE_DATA', |
| UNCHANGED_DATA: 'UNCHANGED_DATA', |
| } |
| |
| @classmethod |
| def operation_name(cls, operation): |
| return name_or_number(cls.OPERATION_NAMES, operation) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| # pylint: disable=line-too-long,unnecessary-lambda |
| [ |
| ('advertising_handle', 1), |
| ( |
| 'operation', |
| { |
| 'size': 1, |
| 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name( |
| x |
| ), |
| }, |
| ), |
| ('fragment_preference', 1), |
| ( |
| 'scan_response_data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': functools.partial( |
| HCI_Object.serialize_length_prefixed_bytes |
| ), |
| }, |
| ), |
| ] |
| ) |
| class HCI_LE_Set_Extended_Scan_Response_Data_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.55 LE Set Extended Scan Response Data Command |
| ''' |
| |
| INTERMEDIATE_FRAGMENT = 0x00 |
| FIRST_FRAGMENT = 0x01 |
| LAST_FRAGMENT = 0x02 |
| COMPLETE_DATA = 0x03 |
| |
| OPERATION_NAMES = { |
| INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT', |
| FIRST_FRAGMENT: 'FIRST_FRAGMENT', |
| LAST_FRAGMENT: 'LAST_FRAGMENT', |
| COMPLETE_DATA: 'COMPLETE_DATA', |
| } |
| |
| @classmethod |
| def operation_name(cls, operation): |
| return name_or_number(cls.OPERATION_NAMES, operation) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command(fields=None) |
| class HCI_LE_Set_Extended_Advertising_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.56 LE Set Extended Advertising Enable Command |
| ''' |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| enable = parameters[0] |
| num_sets = parameters[1] |
| advertising_handles = [] |
| durations = [] |
| max_extended_advertising_events = [] |
| offset = 2 |
| for _ in range(num_sets): |
| advertising_handles.append(parameters[offset]) |
| durations.append(struct.unpack_from('<H', parameters, offset + 1)[0]) |
| max_extended_advertising_events.append(parameters[offset + 3]) |
| offset += 4 |
| |
| return cls( |
| enable, advertising_handles, durations, max_extended_advertising_events |
| ) |
| |
| def __init__( |
| self, enable, advertising_handles, durations, max_extended_advertising_events |
| ): |
| super().__init__(HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND) |
| self.enable = enable |
| self.advertising_handles = advertising_handles |
| self.durations = durations |
| self.max_extended_advertising_events = max_extended_advertising_events |
| |
| self.parameters = bytes([enable, len(advertising_handles)]) + b''.join( |
| [ |
| struct.pack( |
| '<BHB', |
| advertising_handles[i], |
| durations[i], |
| max_extended_advertising_events[i], |
| ) |
| for i in range(len(advertising_handles)) |
| ] |
| ) |
| |
| def __str__(self): |
| fields = [('enable:', self.enable)] |
| for i, advertising_handle in enumerate(self.advertising_handles): |
| fields.append( |
| (f'advertising_handle[{i}]: ', advertising_handle) |
| ) |
| fields.append((f'duration[{i}]: ', self.durations[i])) |
| fields.append( |
| ( |
| f'max_extended_advertising_events[{i}]:', |
| self.max_extended_advertising_events[i], |
| ) |
| ) |
| |
| return ( |
| color(self.name, 'green') |
| + ':\n' |
| + '\n'.join( |
| [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] |
| ) |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('max_advertising_data_length', 2), |
| ] |
| ) |
| class HCI_LE_Read_Maximum_Advertising_Data_Length_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.57 LE Read Maximum Advertising Data Length Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| return_parameters_fields=[ |
| ('status', STATUS_SPEC), |
| ('num_supported_advertising_sets', 1), |
| ] |
| ) |
| class HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.58 LE Read Number of Supported Advertising Sets Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('advertising_handle', 1)]) |
| class HCI_LE_Remove_Advertising_Set_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.59 LE Remove Advertising Set Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command() |
| class HCI_LE_Clear_Advertising_Sets_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.60 LE Clear Advertising Sets Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('enable', 1), ('advertising_handle', 1)]) |
| class HCI_LE_Set_Periodic_Advertising_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.63 LE Set Periodic Advertising Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command(fields=None) |
| class HCI_LE_Set_Extended_Scan_Parameters_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.64 LE Set Extended Scan Parameters Command |
| ''' |
| |
| PASSIVE_SCANNING = 0 |
| ACTIVE_SCANNING = 1 |
| |
| BASIC_UNFILTERED_POLICY = 0x00 |
| BASIC_FILTERED_POLICY = 0x01 |
| EXTENDED_UNFILTERED_POLICY = 0x02 |
| EXTENDED_FILTERED_POLICY = 0x03 |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| own_address_type = parameters[0] |
| scanning_filter_policy = parameters[1] |
| scanning_phys = parameters[2] |
| |
| phy_bits_set = bin(scanning_phys).count('1') |
| scan_types = [] |
| scan_intervals = [] |
| scan_windows = [] |
| for i in range(phy_bits_set): |
| scan_types.append(parameters[3 + (5 * i)]) |
| scan_intervals.append( |
| struct.unpack_from('<H', parameters, 3 + (5 * i) + 1)[0] |
| ) |
| scan_windows.append( |
| struct.unpack_from('<H', parameters, 3 + (5 * i) + 3)[0] |
| ) |
| |
| return cls( |
| own_address_type=own_address_type, |
| scanning_filter_policy=scanning_filter_policy, |
| scanning_phys=scanning_phys, |
| scan_types=scan_types, |
| scan_intervals=scan_intervals, |
| scan_windows=scan_windows, |
| ) |
| |
| def __init__( |
| self, |
| own_address_type, |
| scanning_filter_policy, |
| scanning_phys, |
| scan_types, |
| scan_intervals, |
| scan_windows, |
| ): |
| super().__init__(HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND) |
| self.own_address_type = own_address_type |
| self.scanning_filter_policy = scanning_filter_policy |
| self.scanning_phys = scanning_phys |
| self.scan_types = scan_types |
| self.scan_intervals = scan_intervals |
| self.scan_windows = scan_windows |
| |
| self.parameters = bytes( |
| [own_address_type, scanning_filter_policy, scanning_phys] |
| ) |
| phy_bits_set = bin(scanning_phys).count('1') |
| for i in range(phy_bits_set): |
| self.parameters += struct.pack( |
| '<BHH', scan_types[i], scan_intervals[i], scan_windows[i] |
| ) |
| |
| def __str__(self): |
| scanning_phys_strs = bit_flags_to_strings( |
| self.scanning_phys, HCI_LE_PHY_BIT_NAMES |
| ) |
| fields = [ |
| ( |
| 'own_address_type: ', |
| Address.address_type_name(self.own_address_type), |
| ), |
| ('scanning_filter_policy:', self.scanning_filter_policy), |
| ('scanning_phys: ', ','.join(scanning_phys_strs)), |
| ] |
| for (i, scanning_phy_str) in enumerate(scanning_phys_strs): |
| fields.append( |
| ( |
| f'{scanning_phy_str}.scan_type: ', |
| 'PASSIVE' |
| if self.scan_types[i] == self.PASSIVE_SCANNING |
| else 'ACTIVE', |
| ) |
| ) |
| fields.append( |
| (f'{scanning_phy_str}.scan_interval:', self.scan_intervals[i]) |
| ) |
| fields.append((f'{scanning_phy_str}.scan_window: ', self.scan_windows[i])) |
| |
| return ( |
| color(self.name, 'green') |
| + ':\n' |
| + '\n'.join( |
| [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] |
| ) |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [('enable', 1), ('filter_duplicates', 1), ('duration', 2), ('period', 2)] |
| ) |
| class HCI_LE_Set_Extended_Scan_Enable_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.65 LE Set Extended Scan Enable Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command(fields=None) |
| class HCI_LE_Extended_Create_Connection_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.66 LE Extended Create Connection Command |
| ''' |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| initiator_filter_policy = parameters[0] |
| own_address_type = parameters[1] |
| peer_address_type = parameters[2] |
| peer_address = Address.parse_address_preceded_by_type(parameters, 3)[1] |
| initiating_phys = parameters[9] |
| |
| phy_bits_set = bin(initiating_phys).count('1') |
| |
| def read_parameter_list(offset): |
| return [ |
| struct.unpack_from('<H', parameters, offset + 16 * i)[0] |
| for i in range(phy_bits_set) |
| ] |
| |
| return cls( |
| initiator_filter_policy=initiator_filter_policy, |
| own_address_type=own_address_type, |
| peer_address_type=peer_address_type, |
| peer_address=peer_address, |
| initiating_phys=initiating_phys, |
| scan_intervals=read_parameter_list(10), |
| scan_windows=read_parameter_list(12), |
| connection_interval_mins=read_parameter_list(14), |
| connection_interval_maxs=read_parameter_list(16), |
| max_latencies=read_parameter_list(18), |
| supervision_timeouts=read_parameter_list(20), |
| min_ce_lengths=read_parameter_list(22), |
| max_ce_lengths=read_parameter_list(24), |
| ) |
| |
| def __init__( |
| self, |
| initiator_filter_policy, |
| own_address_type, |
| peer_address_type, |
| peer_address, |
| initiating_phys, |
| scan_intervals, |
| scan_windows, |
| connection_interval_mins, |
| connection_interval_maxs, |
| max_latencies, |
| supervision_timeouts, |
| min_ce_lengths, |
| max_ce_lengths, |
| ): |
| super().__init__(HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND) |
| self.initiator_filter_policy = initiator_filter_policy |
| self.own_address_type = own_address_type |
| self.peer_address_type = peer_address_type |
| self.peer_address = peer_address |
| self.initiating_phys = initiating_phys |
| self.scan_intervals = scan_intervals |
| self.scan_windows = scan_windows |
| self.connection_interval_mins = connection_interval_mins |
| self.connection_interval_maxs = connection_interval_maxs |
| self.max_latencies = max_latencies |
| self.supervision_timeouts = supervision_timeouts |
| self.min_ce_lengths = min_ce_lengths |
| self.max_ce_lengths = max_ce_lengths |
| |
| self.parameters = ( |
| bytes([initiator_filter_policy, own_address_type, peer_address_type]) |
| + bytes(peer_address) |
| + bytes([initiating_phys]) |
| ) |
| |
| phy_bits_set = bin(initiating_phys).count('1') |
| for i in range(phy_bits_set): |
| self.parameters += struct.pack( |
| '<HHHHHHHH', |
| scan_intervals[i], |
| scan_windows[i], |
| connection_interval_mins[i], |
| connection_interval_maxs[i], |
| max_latencies[i], |
| supervision_timeouts[i], |
| min_ce_lengths[i], |
| max_ce_lengths[i], |
| ) |
| |
| def __str__(self): |
| initiating_phys_strs = bit_flags_to_strings( |
| self.initiating_phys, HCI_LE_PHY_BIT_NAMES |
| ) |
| fields = [ |
| ('initiator_filter_policy:', self.initiator_filter_policy), |
| ( |
| 'own_address_type: ', |
| OwnAddressType.type_name(self.own_address_type), |
| ), |
| ( |
| 'peer_address_type: ', |
| Address.address_type_name(self.peer_address_type), |
| ), |
| ('peer_address: ', str(self.peer_address)), |
| ('initiating_phys: ', ','.join(initiating_phys_strs)), |
| ] |
| for (i, initiating_phys_str) in enumerate(initiating_phys_strs): |
| fields.append( |
| ( |
| f'{initiating_phys_str}.scan_interval: ', |
| self.scan_intervals[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.scan_window: ', |
| self.scan_windows[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.connection_interval_min:', |
| self.connection_interval_mins[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.connection_interval_max:', |
| self.connection_interval_maxs[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.max_latency: ', |
| self.max_latencies[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.supervision_timeout: ', |
| self.supervision_timeouts[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.min_ce_length: ', |
| self.min_ce_lengths[i], |
| ) |
| ) |
| fields.append( |
| ( |
| f'{initiating_phys_str}.max_ce_length: ', |
| self.max_ce_lengths[i], |
| ) |
| ) |
| |
| return ( |
| color(self.name, 'green') |
| + ':\n' |
| + '\n'.join( |
| [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] |
| ) |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command( |
| [ |
| ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_identity_address', Address.parse_address_preceded_by_type), |
| ( |
| 'privacy_mode', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_LE_Set_Privacy_Mode_Command.privacy_mode_name( |
| x |
| ), |
| }, |
| ), |
| ] |
| ) |
| class HCI_LE_Set_Privacy_Mode_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.77 LE Set Privacy Mode Command |
| ''' |
| |
| NETWORK_PRIVACY_MODE = 0x00 |
| DEVICE_PRIVACY_MODE = 0x01 |
| |
| PRIVACY_MODE_NAMES = { |
| NETWORK_PRIVACY_MODE: 'NETWORK_PRIVACY_MODE', |
| DEVICE_PRIVACY_MODE: 'DEVICE_PRIVACY_MODE', |
| } |
| |
| @classmethod |
| def privacy_mode_name(cls, privacy_mode): |
| return name_or_number(cls.PRIVACY_MODE_NAMES, privacy_mode) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Command.command([('bit_number', 1), ('bit_value', 1)]) |
| class HCI_LE_Set_Host_Feature_Command(HCI_Command): |
| ''' |
| See Bluetooth spec @ 7.8.115 LE Set Host Feature Command |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| # HCI Events |
| # ----------------------------------------------------------------------------- |
| class HCI_Event(HCI_Packet): |
| ''' |
| See Bluetooth spec @ Vol 2, Part E - 5.4.4 HCI Event Packet |
| ''' |
| |
| hci_packet_type = HCI_EVENT_PACKET |
| event_classes: Dict[int, Type[HCI_Event]] = {} |
| meta_event_classes: Dict[int, Type[HCI_LE_Meta_Event]] = {} |
| |
| @staticmethod |
| def event(fields=()): |
| ''' |
| Decorator used to declare and register subclasses |
| ''' |
| |
| def inner(cls): |
| cls.name = cls.__name__.upper() |
| cls.event_code = key_with_value(HCI_EVENT_NAMES, cls.name) |
| if cls.event_code is None: |
| raise KeyError('event not found in HCI_EVENT_NAMES') |
| cls.fields = fields |
| |
| # Patch the __init__ method to fix the event_code |
| def init(self, parameters=None, **kwargs): |
| return HCI_Event.__init__(self, cls.event_code, parameters, **kwargs) |
| |
| cls.__init__ = init |
| |
| # Register a factory for this class |
| HCI_Event.event_classes[cls.event_code] = cls |
| |
| return cls |
| |
| return inner |
| |
| @staticmethod |
| def registered(event_class): |
| event_class.name = event_class.__name__.upper() |
| event_class.event_code = key_with_value(HCI_EVENT_NAMES, event_class.name) |
| if event_class.event_code is None: |
| raise KeyError('event not found in HCI_EVENT_NAMES') |
| |
| # Register a factory for this class |
| HCI_Event.event_classes[event_class.event_code] = event_class |
| |
| return event_class |
| |
| @staticmethod |
| def from_bytes(packet): |
| event_code = packet[1] |
| length = packet[2] |
| parameters = packet[3:] |
| if len(parameters) != length: |
| raise ValueError('invalid packet length') |
| |
| if event_code == HCI_LE_META_EVENT: |
| # We do this dispatch here and not in the subclass in order to avoid call |
| # loops |
| subevent_code = parameters[0] |
| cls = HCI_Event.meta_event_classes.get(subevent_code) |
| if cls is None: |
| # No class registered, just use a generic class instance |
| return HCI_LE_Meta_Event(subevent_code, parameters) |
| |
| else: |
| cls = HCI_Event.event_classes.get(event_code) |
| if cls is None: |
| # No class registered, just use a generic class instance |
| return HCI_Event(event_code, parameters) |
| |
| # Invoke the factory to create a new instance |
| return cls.from_parameters(parameters) |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| self = cls.__new__(cls) |
| HCI_Event.__init__(self, self.event_code, parameters) |
| if fields := getattr(self, 'fields', None): |
| HCI_Object.init_from_bytes(self, parameters, 0, fields) |
| return self |
| |
| @staticmethod |
| def event_name(event_code): |
| return name_or_number(HCI_EVENT_NAMES, event_code) |
| |
| def __init__(self, event_code, parameters=None, **kwargs): |
| super().__init__(HCI_Event.event_name(event_code)) |
| if (fields := getattr(self, 'fields', None)) and kwargs: |
| HCI_Object.init_from_fields(self, fields, kwargs) |
| if parameters is None: |
| parameters = HCI_Object.dict_to_bytes(kwargs, fields) |
| self.event_code = event_code |
| self.parameters = parameters |
| |
| def to_bytes(self): |
| parameters = b'' if self.parameters is None else self.parameters |
| return bytes([HCI_EVENT_PACKET, self.event_code, len(parameters)]) + parameters |
| |
| def __bytes__(self): |
| return self.to_bytes() |
| |
| def __str__(self): |
| result = color(self.name, 'magenta') |
| if fields := getattr(self, 'fields', None): |
| result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, ' ') |
| else: |
| if self.parameters: |
| result += f': {self.parameters.hex()}' |
| return result |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_LE_Meta_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65 LE Meta Event |
| ''' |
| |
| @staticmethod |
| def event(fields=()): |
| ''' |
| Decorator used to declare and register subclasses |
| ''' |
| |
| def inner(cls): |
| cls.name = cls.__name__.upper() |
| cls.subevent_code = key_with_value(HCI_SUBEVENT_NAMES, cls.name) |
| if cls.subevent_code is None: |
| raise KeyError('subevent not found in HCI_SUBEVENT_NAMES') |
| cls.fields = fields |
| |
| # Patch the __init__ method to fix the subevent_code |
| def init(self, parameters=None, **kwargs): |
| return HCI_LE_Meta_Event.__init__( |
| self, cls.subevent_code, parameters, **kwargs |
| ) |
| |
| cls.__init__ = init |
| |
| # Register a factory for this class |
| HCI_Event.meta_event_classes[cls.subevent_code] = cls |
| |
| return cls |
| |
| return inner |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| self = cls.__new__(cls) |
| HCI_LE_Meta_Event.__init__(self, self.subevent_code, parameters) |
| if fields := getattr(self, 'fields', None): |
| HCI_Object.init_from_bytes(self, parameters, 1, fields) |
| return self |
| |
| @staticmethod |
| def subevent_name(subevent_code): |
| return name_or_number(HCI_SUBEVENT_NAMES, subevent_code) |
| |
| def __init__(self, subevent_code, parameters, **kwargs): |
| self.subevent_code = subevent_code |
| if parameters is None and (fields := getattr(self, 'fields', None)) and kwargs: |
| parameters = bytes([subevent_code]) + HCI_Object.dict_to_bytes( |
| kwargs, fields |
| ) |
| super().__init__(HCI_LE_META_EVENT, parameters, **kwargs) |
| |
| # Override the name in order to adopt the subevent name instead |
| self.name = self.subevent_name(subevent_code) |
| |
| def __str__(self): |
| result = color(self.subevent_name(self.subevent_code), 'magenta') |
| if fields := getattr(self, 'fields', None): |
| result += ':\n' + HCI_Object.format_fields(self.__dict__, fields, ' ') |
| else: |
| if self.parameters: |
| result += f': {self.parameters.hex()}' |
| return result |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ( |
| 'role', |
| {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}, |
| ), |
| ('peer_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_address', Address.parse_address_preceded_by_type), |
| ('connection_interval', 2), |
| ('peripheral_latency', 2), |
| ('supervision_timeout', 2), |
| ('central_clock_accuracy', 1), |
| ] |
| ) |
| class HCI_LE_Connection_Complete_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.1 LE Connection Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_LE_Advertising_Report_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.2 LE Advertising Report Event |
| ''' |
| |
| subevent_code = HCI_LE_ADVERTISING_REPORT_EVENT |
| |
| # Event Types |
| ADV_IND = 0x00 |
| ADV_DIRECT_IND = 0x01 |
| ADV_SCAN_IND = 0x02 |
| ADV_NONCONN_IND = 0x03 |
| SCAN_RSP = 0x04 |
| |
| EVENT_TYPE_NAMES = { |
| ADV_IND: 'ADV_IND', # Connectable and scannable undirected advertising |
| ADV_DIRECT_IND: 'ADV_DIRECT_IND', # Connectable directed advertising |
| ADV_SCAN_IND: 'ADV_SCAN_IND', # Scannable undirected advertising |
| ADV_NONCONN_IND: 'ADV_NONCONN_IND', # Non connectable undirected advertising |
| SCAN_RSP: 'SCAN_RSP', # Scan Response |
| } |
| |
| class Report(HCI_Object): |
| FIELDS = [ |
| ('event_type', 1), |
| ('address_type', Address.ADDRESS_TYPE_SPEC), |
| ('address', Address.parse_address_preceded_by_type), |
| ( |
| 'data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': HCI_Object.serialize_length_prefixed_bytes, |
| }, |
| ), |
| ('rssi', -1), |
| ] |
| |
| @classmethod |
| def from_parameters(cls, parameters, offset): |
| return cls.from_bytes(parameters, offset, cls.FIELDS) |
| |
| def event_type_string(self): |
| return HCI_LE_Advertising_Report_Event.event_type_name(self.event_type) |
| |
| def to_string(self, indentation='', _=None): |
| return super().to_string( |
| indentation, |
| { |
| 'event_type': HCI_LE_Advertising_Report_Event.event_type_name, |
| 'address_type': Address.address_type_name, |
| 'data': lambda x: str(AdvertisingData.from_bytes(x)), |
| }, |
| ) |
| |
| @classmethod |
| def event_type_name(cls, event_type): |
| return name_or_number(cls.EVENT_TYPE_NAMES, event_type) |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| num_reports = parameters[1] |
| reports = [] |
| offset = 2 |
| for _ in range(num_reports): |
| report = cls.Report.from_parameters(parameters, offset) |
| offset += 10 + len(report.data) |
| reports.append(report) |
| |
| return cls(reports) |
| |
| def __init__(self, reports): |
| self.reports = reports[:] |
| |
| # Serialize the fields |
| parameters = bytes([HCI_LE_ADVERTISING_REPORT_EVENT, len(reports)]) + b''.join( |
| [bytes(report) for report in reports] |
| ) |
| |
| super().__init__(self.subevent_code, parameters) |
| |
| def __str__(self): |
| reports = '\n'.join( |
| [f'{i}:\n{report.to_string(" ")}' for i, report in enumerate(self.reports)] |
| ) |
| return f'{color(self.subevent_name(self.subevent_code), "magenta")}:\n{reports}' |
| |
| |
| HCI_Event.meta_event_classes[ |
| HCI_LE_ADVERTISING_REPORT_EVENT |
| ] = HCI_LE_Advertising_Report_Event |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('connection_interval', 2), |
| ('peripheral_latency', 2), |
| ('supervision_timeout', 2), |
| ] |
| ) |
| class HCI_LE_Connection_Update_Complete_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.3 LE Connection Update Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [('status', STATUS_SPEC), ('connection_handle', 2), ('le_features', 8)] |
| ) |
| class HCI_LE_Read_Remote_Features_Complete_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.4 LE Read Remote Features Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [('connection_handle', 2), ('random_number', 8), ('encryption_diversifier', 2)] |
| ) |
| class HCI_LE_Long_Term_Key_Request_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.5 LE Long Term Key Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('connection_handle', 2), |
| ('interval_min', 2), |
| ('interval_max', 2), |
| ('max_latency', 2), |
| ('timeout', 2), |
| ] |
| ) |
| class HCI_LE_Remote_Connection_Parameter_Request_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.6 LE Remote Connection Parameter Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('connection_handle', 2), |
| ('max_tx_octets', 2), |
| ('max_tx_time', 2), |
| ('max_rx_octets', 2), |
| ('max_rx_time', 2), |
| ] |
| ) |
| class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.7 LE Data Length Change Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ( |
| 'role', |
| {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}, |
| ), |
| ('peer_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('peer_address', Address.parse_address_preceded_by_type), |
| ('local_resolvable_private_address', Address.parse_address), |
| ('peer_resolvable_private_address', Address.parse_address), |
| ('connection_interval', 2), |
| ('peripheral_latency', 2), |
| ('supervision_timeout', 2), |
| ('central_clock_accuracy', 1), |
| ] |
| ) |
| class HCI_LE_Enhanced_Connection_Complete_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.10 LE Enhanced Connection Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('tx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('rx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ] |
| ) |
| class HCI_LE_PHY_Update_Complete_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.12 LE PHY Update Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_LE_Extended_Advertising_Report_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.13 LE Extended Advertising Report Event |
| ''' |
| |
| subevent_code = HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT |
| |
| # Event types flags |
| CONNECTABLE_ADVERTISING = 0 |
| SCANNABLE_ADVERTISING = 1 |
| DIRECTED_ADVERTISING = 2 |
| SCAN_RESPONSE = 3 |
| LEGACY_ADVERTISING_PDU_USED = 4 |
| |
| DATA_COMPLETE = 0x00 |
| DATA_INCOMPLETE_MORE_TO_COME = 0x01 |
| DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02 |
| |
| EVENT_TYPE_FLAG_NAMES = ( |
| 'CONNECTABLE_ADVERTISING', |
| 'SCANNABLE_ADVERTISING', |
| 'DIRECTED_ADVERTISING', |
| 'SCAN_RESPONSE', |
| 'LEGACY_ADVERTISING_PDU_USED', |
| ) |
| |
| LEGACY_PDU_TYPE_MAP = { |
| 0b0011: HCI_LE_Advertising_Report_Event.ADV_IND, |
| 0b0101: HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND, |
| 0b0010: HCI_LE_Advertising_Report_Event.ADV_SCAN_IND, |
| 0b0000: HCI_LE_Advertising_Report_Event.ADV_NONCONN_IND, |
| 0b1011: HCI_LE_Advertising_Report_Event.SCAN_RSP, |
| 0b1010: HCI_LE_Advertising_Report_Event.SCAN_RSP, |
| } |
| |
| NO_ADI_FIELD_PROVIDED = 0xFF |
| TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F |
| RSSI_NOT_AVAILABLE = 0x7F |
| ANONYMOUS_ADDRESS_TYPE = 0xFF |
| UNRESOLVED_RESOLVABLE_ADDRESS_TYPE = 0xFE |
| |
| class Report(HCI_Object): |
| FIELDS = [ |
| ('event_type', 2), |
| ('address_type', Address.ADDRESS_TYPE_SPEC), |
| ('address', Address.parse_address_preceded_by_type), |
| ('primary_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('secondary_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), |
| ('advertising_sid', 1), |
| ('tx_power', 1), |
| ('rssi', -1), |
| ('periodic_advertising_interval', 2), |
| ('direct_address_type', Address.ADDRESS_TYPE_SPEC), |
| ('direct_address', Address.parse_address_preceded_by_type), |
| ( |
| 'data', |
| { |
| 'parser': HCI_Object.parse_length_prefixed_bytes, |
| 'serializer': HCI_Object.serialize_length_prefixed_bytes, |
| }, |
| ), |
| ] |
| |
| @classmethod |
| def from_parameters(cls, parameters, offset): |
| return cls.from_bytes(parameters, offset, cls.FIELDS) |
| |
| def event_type_string(self): |
| return HCI_LE_Extended_Advertising_Report_Event.event_type_string( |
| self.event_type |
| ) |
| |
| def to_string(self, indentation='', _=None): |
| # pylint: disable=line-too-long |
| return super().to_string( |
| indentation, |
| { |
| 'event_type': HCI_LE_Extended_Advertising_Report_Event.event_type_string, |
| 'address_type': Address.address_type_name, |
| 'data': lambda x: str(AdvertisingData.from_bytes(x)), |
| }, |
| ) |
| |
| @staticmethod |
| def event_type_string(event_type): |
| event_type_flags = bit_flags_to_strings( |
| event_type & 0x1F, |
| HCI_LE_Extended_Advertising_Report_Event.EVENT_TYPE_FLAG_NAMES, |
| ) |
| event_type_flags.append( |
| ('COMPLETE', 'INCOMPLETE+', 'INCOMPLETE#', '?')[(event_type >> 5) & 3] |
| ) |
| |
| if event_type & ( |
| 1 << HCI_LE_Extended_Advertising_Report_Event.LEGACY_ADVERTISING_PDU_USED |
| ): |
| legacy_pdu_type = ( |
| HCI_LE_Extended_Advertising_Report_Event.LEGACY_PDU_TYPE_MAP.get( |
| event_type & 0x0F |
| ) |
| ) |
| if legacy_pdu_type is not None: |
| # pylint: disable=line-too-long |
| legacy_info_string = f'({HCI_LE_Advertising_Report_Event.event_type_name(legacy_pdu_type)})' |
| else: |
| legacy_info_string = '' |
| else: |
| legacy_info_string = '' |
| |
| return f'0x{event_type:04X} [{",".join(event_type_flags)}]{legacy_info_string}' |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| num_reports = parameters[1] |
| reports = [] |
| offset = 2 |
| for _ in range(num_reports): |
| report = cls.Report.from_parameters(parameters, offset) |
| offset += 24 + len(report.data) |
| reports.append(report) |
| |
| return cls(reports) |
| |
| def __init__(self, reports): |
| self.reports = reports[:] |
| |
| # Serialize the fields |
| parameters = bytes( |
| [HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT, len(reports)] |
| ) + b''.join([bytes(report) for report in reports]) |
| |
| super().__init__(self.subevent_code, parameters) |
| |
| def __str__(self): |
| reports = '\n'.join( |
| [f'{i}:\n{report.to_string(" ")}' for i, report in enumerate(self.reports)] |
| ) |
| return f'{color(self.subevent_name(self.subevent_code), "magenta")}:\n{reports}' |
| |
| |
| HCI_Event.meta_event_classes[ |
| HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT |
| ] = HCI_LE_Extended_Advertising_Report_Event |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_LE_Meta_Event.event([('connection_handle', 2), ('channel_selection_algorithm', 1)]) |
| class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event): |
| ''' |
| See Bluetooth spec @ 7.7.65.20 LE Channel Selection Algorithm Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('status', STATUS_SPEC)]) |
| class HCI_Inquiry_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.1 Inquiry Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.registered |
| class HCI_Inquiry_Result_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.2 Inquiry Result Event |
| ''' |
| |
| RESPONSE_FIELDS = [ |
| ('bd_addr', Address.parse_address), |
| ('page_scan_repetition_mode', 1), |
| ('reserved', 1), |
| ('reserved', 1), |
| ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), |
| ('clock_offset', 2), |
| ] |
| |
| @staticmethod |
| def from_parameters(parameters): |
| num_responses = parameters[0] |
| responses = [] |
| offset = 1 |
| for _ in range(num_responses): |
| response = HCI_Object.from_bytes( |
| parameters, offset, HCI_Inquiry_Result_Event.RESPONSE_FIELDS |
| ) |
| offset += 14 |
| responses.append(response) |
| |
| return HCI_Inquiry_Result_Event(responses) |
| |
| def __init__(self, responses): |
| self.responses = responses[:] |
| |
| # Serialize the fields |
| parameters = bytes([HCI_INQUIRY_RESULT_EVENT, len(responses)]) + b''.join( |
| [bytes(response) for response in responses] |
| ) |
| |
| super().__init__(HCI_INQUIRY_RESULT_EVENT, parameters) |
| |
| def __str__(self): |
| responses = '\n'.join( |
| [response.to_string(indentation=' ') for response in self.responses] |
| ) |
| return f'{color("HCI_INQUIRY_RESULT_EVENT", "magenta")}:\n{responses}' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('bd_addr', Address.parse_address), |
| ( |
| 'link_type', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x), |
| }, |
| ), |
| ('encryption_enabled', 1), |
| ] |
| ) |
| class HCI_Connection_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.3 Connection Complete Event |
| ''' |
| |
| SCO_LINK_TYPE = 0x00 |
| ACL_LINK_TYPE = 0x01 |
| ESCO_LINK_TYPE = 0x02 |
| |
| LINK_TYPE_NAMES = { |
| SCO_LINK_TYPE: 'SCO', |
| ACL_LINK_TYPE: 'ACL', |
| ESCO_LINK_TYPE: 'eSCO', |
| } |
| |
| @staticmethod |
| def link_type_name(link_type): |
| return name_or_number(HCI_Connection_Complete_Event.LINK_TYPE_NAMES, link_type) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('class_of_device', 3), |
| ( |
| 'link_type', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x), |
| }, |
| ), |
| ] |
| ) |
| class HCI_Connection_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.4 Connection Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), |
| ] |
| ) |
| class HCI_Disconnection_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.5 Disconnection Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('status', STATUS_SPEC), ('connection_handle', 2)]) |
| class HCI_Authentication_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.6 Authentication Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ('remote_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}), |
| ] |
| ) |
| class HCI_Remote_Name_Request_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.7 Remote Name Request Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ( |
| 'encryption_enabled', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_Encryption_Change_Event.encryption_enabled_name( |
| x |
| ), |
| }, |
| ), |
| ] |
| ) |
| class HCI_Encryption_Change_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.8 Encryption Change Event |
| ''' |
| |
| OFF = 0x00 |
| E0_OR_AES_CCM = 0x01 |
| AES_CCM = 0x02 |
| |
| ENCRYPTION_ENABLED_NAMES = { |
| OFF: 'OFF', |
| E0_OR_AES_CCM: 'E0_OR_AES_CCM', |
| AES_CCM: 'AES_CCM', |
| } |
| |
| @staticmethod |
| def encryption_enabled_name(encryption_enabled): |
| return name_or_number( |
| HCI_Encryption_Change_Event.ENCRYPTION_ENABLED_NAMES, encryption_enabled |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [('status', STATUS_SPEC), ('connection_handle', 2), ('lmp_features', 8)] |
| ) |
| class HCI_Read_Remote_Supported_Features_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.11 Read Remote Supported Features Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('version', 1), |
| ('manufacturer_name', 2), |
| ('subversion', 2), |
| ] |
| ) |
| class HCI_Read_Remote_Version_Information_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.12 Read Remote Version Information Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('num_hci_command_packets', 1), |
| ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}), |
| ('return_parameters', '*'), |
| ] |
| ) |
| class HCI_Command_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.14 Command Complete Event |
| ''' |
| |
| return_parameters = b'' |
| |
| def map_return_parameters(self, return_parameters): |
| '''Map simple 'status' return parameters to their named constant form''' |
| |
| if isinstance(return_parameters, bytes) and len(return_parameters) == 1: |
| # Byte-array form |
| return HCI_Constant.status_name(return_parameters[0]) |
| |
| if isinstance(return_parameters, int): |
| # Already converted to an integer status code |
| return HCI_Constant.status_name(return_parameters) |
| |
| return return_parameters |
| |
| @staticmethod |
| def from_parameters(parameters): |
| self = HCI_Command_Complete_Event.__new__(HCI_Command_Complete_Event) |
| HCI_Event.__init__(self, self.event_code, parameters) |
| HCI_Object.init_from_bytes( |
| self, parameters, 0, HCI_Command_Complete_Event.fields |
| ) |
| |
| # Parse the return parameters |
| if ( |
| isinstance(self.return_parameters, bytes) |
| and len(self.return_parameters) == 1 |
| ): |
| # All commands with 1-byte return parameters return a 'status' field, |
| # convert it to an integer |
| self.return_parameters = self.return_parameters[0] |
| else: |
| cls = HCI_Command.command_classes.get(self.command_opcode) |
| if cls and cls.return_parameters_fields: |
| self.return_parameters = HCI_Object.from_bytes( |
| self.return_parameters, 0, cls.return_parameters_fields |
| ) |
| self.return_parameters.fields = cls.return_parameters_fields |
| |
| return self |
| |
| def __str__(self): |
| return f'{color(self.name, "magenta")}:\n' + HCI_Object.format_fields( |
| self.__dict__, |
| self.fields, |
| ' ', |
| {'return_parameters': self.map_return_parameters}, |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ( |
| 'status', |
| # pylint: disable-next=unnecessary-lambda |
| {'size': 1, 'mapper': lambda x: HCI_Command_Status_Event.status_name(x)}, |
| ), |
| ('num_hci_command_packets', 1), |
| ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}), |
| ] |
| ) |
| class HCI_Command_Status_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.15 Command Complete Event |
| ''' |
| |
| PENDING = 0 |
| |
| @staticmethod |
| def status_name(status): |
| if status == HCI_Command_Status_Event.PENDING: |
| return 'PENDING' |
| |
| return HCI_Constant.error_name(status) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('bd_addr', Address.parse_address), |
| ('new_role', {'size': 1, 'mapper': HCI_Constant.role_name}), |
| ] |
| ) |
| class HCI_Role_Change_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.18 Role Change Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.registered |
| class HCI_Number_Of_Completed_Packets_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.19 Number Of Completed Packets Event |
| ''' |
| |
| @classmethod |
| def from_parameters(cls, parameters): |
| self = cls.__new__(cls) |
| self.parameters = parameters |
| num_handles = parameters[0] |
| self.connection_handles = [] |
| self.num_completed_packets = [] |
| for i in range(num_handles): |
| self.connection_handles.append( |
| struct.unpack_from('<H', parameters, 1 + i * 4)[0] |
| ) |
| self.num_completed_packets.append( |
| struct.unpack_from('<H', parameters, 1 + i * 4 + 2)[0] |
| ) |
| |
| return self |
| |
| def __init__(self, connection_handle_and_completed_packets_list): |
| self.connection_handles = [] |
| self.num_completed_packets = [] |
| parameters = bytes([len(connection_handle_and_completed_packets_list)]) |
| for handle, completed_packets in connection_handle_and_completed_packets_list: |
| self.connection_handles.append(handle) |
| self.num_completed_packets.append(completed_packets) |
| parameters += struct.pack('<H', handle) |
| parameters += struct.pack('<H', completed_packets) |
| super().__init__(HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT, parameters) |
| |
| def __str__(self): |
| lines = [ |
| color(self.name, 'magenta') + ':', |
| color(' number_of_handles: ', 'cyan') |
| + f'{len(self.connection_handles)}', |
| ] |
| for i, connection_handle in enumerate(self.connection_handles): |
| lines.append( |
| color(f' connection_handle[{i}]: ', 'cyan') |
| + f'{connection_handle}' |
| ) |
| lines.append( |
| color(f' num_completed_packets[{i}]: ', 'cyan') |
| + f'{self.num_completed_packets[i]}' |
| ) |
| return '\n'.join(lines) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ( |
| 'current_mode', |
| # pylint: disable-next=unnecessary-lambda |
| {'size': 1, 'mapper': lambda x: HCI_Mode_Change_Event.mode_name(x)}, |
| ), |
| ('interval', 2), |
| ] |
| ) |
| class HCI_Mode_Change_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.20 Mode Change Event |
| ''' |
| |
| ACTIVE_MODE = 0x00 |
| HOLD_MODE = 0x01 |
| SNIFF_MODE = 0x02 |
| |
| MODE_NAMES = { |
| ACTIVE_MODE: 'ACTIVE_MODE', |
| HOLD_MODE: 'HOLD_MODE', |
| SNIFF_MODE: 'SNIFF_MODE', |
| } |
| |
| @staticmethod |
| def mode_name(mode): |
| return name_or_number(HCI_Mode_Change_Event.MODE_NAMES, mode) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address)]) |
| class HCI_PIN_Code_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.22 PIN Code Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address)]) |
| class HCI_Link_Key_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.24 7.7.23 Link Key Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('link_key', 16), |
| ('key_type', {'size': 1, 'mapper': HCI_Constant.link_key_type_name}), |
| ] |
| ) |
| class HCI_Link_Key_Notification_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.24 Link Key Notification Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('connection_handle', 2), ('lmp_max_slots', 1)]) |
| class HCI_Max_Slots_Change_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.27 Max Slots Change Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [('status', STATUS_SPEC), ('connection_handle', 2), ('clock_offset', 2)] |
| ) |
| class HCI_Read_Clock_Offset_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.28 Read Clock Offset Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [('status', STATUS_SPEC), ('connection_handle', 2), ('packet_type', 2)] |
| ) |
| class HCI_Connection_Packet_Type_Changed_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.29 Connection Packet Type Changed Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address), ('page_scan_repetition_mode', 1)]) |
| class HCI_Page_Scan_Repetition_Mode_Change_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.31 Page Scan Repetition Mode Change Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.registered |
| class HCI_Inquiry_Result_With_RSSI_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.33 Inquiry Result with RSSI Event |
| ''' |
| |
| RESPONSE_FIELDS = [ |
| ('bd_addr', Address.parse_address), |
| ('page_scan_repetition_mode', 1), |
| ('reserved', 1), |
| ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), |
| ('clock_offset', 2), |
| ('rssi', -1), |
| ] |
| |
| @staticmethod |
| def from_parameters(parameters): |
| num_responses = parameters[0] |
| responses = [] |
| offset = 1 |
| for _ in range(num_responses): |
| response = HCI_Object.from_bytes( |
| parameters, offset, HCI_Inquiry_Result_With_RSSI_Event.RESPONSE_FIELDS |
| ) |
| offset += 14 |
| responses.append(response) |
| |
| return HCI_Inquiry_Result_With_RSSI_Event(responses) |
| |
| def __init__(self, responses): |
| self.responses = responses[:] |
| |
| # Serialize the fields |
| parameters = bytes( |
| [HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, len(responses)] |
| ) + b''.join([bytes(response) for response in responses]) |
| |
| super().__init__(HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, parameters) |
| |
| def __str__(self): |
| responses = '\n'.join( |
| [response.to_string(indentation=' ') for response in self.responses] |
| ) |
| return f'{color("HCI_INQUIRY_RESULT_WITH_RSSI_EVENT", "magenta")}:\n{responses}' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('page_number', 1), |
| ('maximum_page_number', 1), |
| ('extended_lmp_features', 8), |
| ] |
| ) |
| class HCI_Read_Remote_Extended_Features_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.34 Read Remote Extended Features Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| # pylint: disable=line-too-long |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('bd_addr', Address.parse_address), |
| ( |
| 'link_type', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.link_type_name( |
| x |
| ), |
| }, |
| ), |
| ('transmission_interval', 1), |
| ('retransmission_window', 1), |
| ('rx_packet_length', 2), |
| ('tx_packet_length', 2), |
| ( |
| 'air_mode', |
| { |
| 'size': 1, |
| # pylint: disable-next=unnecessary-lambda |
| 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.air_mode_name( |
| x |
| ), |
| }, |
| ), |
| ] |
| ) |
| class HCI_Synchronous_Connection_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.35 Synchronous Connection Complete Event |
| ''' |
| |
| SCO_CONNECTION_LINK_TYPE = 0x00 |
| ESCO_CONNECTION_LINK_TYPE = 0x02 |
| |
| LINK_TYPE_NAMES = { |
| SCO_CONNECTION_LINK_TYPE: 'SCO', |
| ESCO_CONNECTION_LINK_TYPE: 'eSCO', |
| } |
| |
| U_LAW_LOG_AIR_MODE = 0x00 |
| A_LAW_LOG_AIR_MORE = 0x01 |
| CVSD_AIR_MODE = 0x02 |
| TRANSPARENT_DATA_AIR_MODE = 0x03 |
| |
| AIR_MODE_NAMES = { |
| U_LAW_LOG_AIR_MODE: 'u-law log', |
| A_LAW_LOG_AIR_MORE: 'A-law log', |
| CVSD_AIR_MODE: 'CVSD', |
| TRANSPARENT_DATA_AIR_MODE: 'Transparent Data', |
| } |
| |
| @staticmethod |
| def link_type_name(link_type): |
| return name_or_number( |
| HCI_Synchronous_Connection_Complete_Event.LINK_TYPE_NAMES, link_type |
| ) |
| |
| @staticmethod |
| def air_mode_name(air_mode): |
| return name_or_number( |
| HCI_Synchronous_Connection_Complete_Event.AIR_MODE_NAMES, air_mode |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('status', STATUS_SPEC), |
| ('connection_handle', 2), |
| ('transmission_interval', 1), |
| ('retransmission_window', 1), |
| ('rx_packet_length', 2), |
| ('tx_packet_length', 2), |
| ] |
| ) |
| class HCI_Synchronous_Connection_Changed_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.36 Synchronous Connection Changed Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('num_responses', 1), |
| ('bd_addr', Address.parse_address), |
| ('page_scan_repetition_mode', 1), |
| ('reserved', 1), |
| ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), |
| ('clock_offset', 2), |
| ('rssi', -1), |
| ('extended_inquiry_response', 240), |
| ] |
| ) |
| class HCI_Extended_Inquiry_Result_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.38 Extended Inquiry Result Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('status', STATUS_SPEC), ('connection_handle', 2)]) |
| class HCI_Encryption_Key_Refresh_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.39 Encryption Key Refresh Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address)]) |
| class HCI_IO_Capability_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.40 IO Capability Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event( |
| [ |
| ('bd_addr', Address.parse_address), |
| ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), |
| ('oob_data_present', 1), |
| ( |
| 'authentication_requirements', |
| {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}, |
| ), |
| ] |
| ) |
| class HCI_IO_Capability_Response_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.41 IO Capability Response Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address), ('numeric_value', 4)]) |
| class HCI_User_Confirmation_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.42 User Confirmation Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address)]) |
| class HCI_User_Passkey_Request_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.43 User Passkey Request Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('status', STATUS_SPEC), ('bd_addr', Address.parse_address)]) |
| class HCI_Simple_Pairing_Complete_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.45 Simple Pairing Complete Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('connection_handle', 2), ('link_supervision_timeout', 2)]) |
| class HCI_Link_Supervision_Timeout_Changed_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.46 Link Supervision Timeout Changed Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address), ('passkey', 4)]) |
| class HCI_User_Passkey_Notification_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.48 User Passkey Notification Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| @HCI_Event.event([('bd_addr', Address.parse_address), ('host_supported_features', 8)]) |
| class HCI_Remote_Host_Supported_Features_Notification_Event(HCI_Event): |
| ''' |
| See Bluetooth spec @ 7.7.50 Remote Host Supported Features Notification Event |
| ''' |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_AclDataPacket: |
| ''' |
| See Bluetooth spec @ 5.4.2 HCI ACL Data Packets |
| ''' |
| |
| hci_packet_type = HCI_ACL_DATA_PACKET |
| |
| @staticmethod |
| def from_bytes(packet): |
| # Read the header |
| h, data_total_length = struct.unpack_from('<HH', packet, 1) |
| connection_handle = h & 0xFFF |
| pb_flag = (h >> 12) & 3 |
| bc_flag = (h >> 14) & 3 |
| data = packet[5:] |
| if len(data) != data_total_length: |
| raise ValueError('invalid packet length') |
| return HCI_AclDataPacket( |
| connection_handle, pb_flag, bc_flag, data_total_length, data |
| ) |
| |
| def to_bytes(self): |
| h = (self.pb_flag << 12) | (self.bc_flag << 14) | self.connection_handle |
| return ( |
| struct.pack('<BHH', HCI_ACL_DATA_PACKET, h, self.data_total_length) |
| + self.data |
| ) |
| |
| def __init__(self, connection_handle, pb_flag, bc_flag, data_total_length, data): |
| self.connection_handle = connection_handle |
| self.pb_flag = pb_flag |
| self.bc_flag = bc_flag |
| self.data_total_length = data_total_length |
| self.data = data |
| |
| def __bytes__(self): |
| return self.to_bytes() |
| |
| def __str__(self): |
| return ( |
| f'{color("ACL", "blue")}: ' |
| f'handle=0x{self.connection_handle:04x}' |
| f'pb={self.pb_flag}, bc={self.bc_flag}, ' |
| f'data_total_length={self.data_total_length}, ' |
| f'data={self.data.hex()}' |
| ) |
| |
| |
| # ----------------------------------------------------------------------------- |
| class HCI_AclDataPacketAssembler: |
| def __init__(self, callback): |
| self.callback = callback |
| self.current_data = None |
| self.l2cap_pdu_length = 0 |
| |
| def feed_packet(self, packet): |
| if packet.pb_flag in ( |
| HCI_ACL_PB_FIRST_NON_FLUSHABLE, |
| HCI_ACL_PB_FIRST_FLUSHABLE, |
| ): |
| (l2cap_pdu_length,) = struct.unpack_from('<H', packet.data, 0) |
| self.current_data = packet.data |
| self.l2cap_pdu_length = l2cap_pdu_length |
| elif packet.pb_flag == HCI_ACL_PB_CONTINUATION: |
| if self.current_data is None: |
| logger.warning('!!! ACL continuation without start') |
| return |
| self.current_data += packet.data |
| |
| if len(self.current_data) == self.l2cap_pdu_length + 4: |
| # The packet is complete, invoke the callback |
| logger.debug(f'<<< ACL PDU: {self.current_data.hex()}') |
| self.callback(self.current_data) |
| |
| # Reset |
| self.current_data = None |
| self.l2cap_pdu_length = 0 |
| else: |
| # Sanity check |
| if len(self.current_data) > self.l2cap_pdu_length + 4: |
| logger.warning('!!! ACL data exceeds L2CAP PDU') |
| self.current_data = None |
| self.l2cap_pdu_length = 0 |