#! /usr/bin/env python3
###############################################################################
#
#
# Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of Maxim Integrated
# Products, Inc. shall not be used except as stated in the Maxim Integrated
# Products, Inc. Branding Policy.
#
# The mere transfer of this software does not imply any licenses
# of trade secrets, proprietary technology, copyrights, patents,
# trademarks, maskwork rights, or any other form of intellectual
# property whatsoever. Maxim Integrated Products, Inc. retains all
# ownership rights.
#
##############################################################################
#
# Copyright 2023 Analog Devices, Inc.
#
# 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
#
# http://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.
#
##############################################################################
"""Contains definitions for BLE standard codes utilized in HCI packet creation/parsing."""
from __future__ import annotations
from enum import Enum, Flag
from typing import List, Union
[docs]class EventCode(Enum):
"""Supported HCI Event Codes"""
DICON_COMPLETE = 0x05
"""Disconnection complete event."""
ENC_CHANGE = 0x08
"""Encryption change event."""
READ_REMOTE_VERSION_INFO_COMPLETE = 0x0C
"""Read remote version information complete event."""
COMMAND_COMPLETE = 0x0E
"""Command complete event."""
COMMAND_STATUS = 0x0F
"""Command status event."""
HARDWARE_ERROR = 0x10
"""Hardware error event."""
NUM_COMPLETED_PACKETS = 0x13
"""Number of completed packets event."""
DATA_BUFF_OVERFLOW = 0x1A
"""Data buffer overflow event."""
ENC_KEY_REFRESH_COMPLETE = 0x30
"""Encryption key refresh complete event."""
LE_META = 0x3E
"""LE meta event."""
AUTH_PAYLOAD_TIMEOUT_EXPIRED = 0x57
"""Authenticated payload timeout expired event."""
VENDOR_SPEC = 0xFF
"""Vendor specific event."""
def _get_full_mask(mask_type: Union[EventMask, EventMaskLE]):
full_mask = None
for flag in mask_type:
if full_mask is None:
full_mask = flag
full_mask |= flag
return full_mask
[docs]class EventMask(Flag):
"""Controller Event Mask"""
INQUIRY_COMPLETE = 1 << 0
INQUIRY_RESULT = 1 << 1
CONNECTION_COMPLETE = 1 << 2
CONNECTION_REQUEST = 1 << 3
DISCONNECTION_COMPLETE = 1 << 4
AUTHENTICATION_COMPLETE = 1 << 5
REMOTE_NAME_REQUEST_COMPLETE = 1 << 6
ENCRYPTION_CHANGE = 1 << 7
CHANGE_CONNECTION_LINK_KEY_COMPLETE = 1 << 8
LINK_KEY_TYPE_CHANGED = 1 << 9
READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 1 << 10
READ_REMOTE_VERSION_INFORMATION_COMPLETE = 1 << 11
QOS_SETUP_COMPLETE = 1 << 12
HARDWARE_ERROR = 1 << 15
FLUSH_OCCURRED = 1 << 16
ROLE_CHANGE = 1 << 17
MODE_CHANGE = 1 << 19
RETURN_LINK_KEYS = 1 << 20
PIN_CODE_REQUEST = 1 << 21
LINK_KEY_REQUEST = 1 << 22
LINK_KEY_NOTIFICATION = 1 << 23
LOOPBACK_COMMAND = 1 << 24
DATA_BUFFER_OVERFLOW = 1 << 25
MAX_SLOTS_CHANGE = 1 << 26
READ_CLOCK_OFFSET_COMPLETE = 1 << 27
CONNECTION_PACKET_TYPE_CHANGED = 1 << 28
QOS_VIOLATION = 1 << 29
PREVIOUSLY_USED = 1 << 30
PAGE_SCAN_REPETITION_MODE_CHANGE = 1 << 31
FLOW_SPECIFICATION_COMPLETE = 1 << 32
INQUIRY_RESULT_WITH_RSSI = 1 << 33
READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 1 << 34
SYNCHRONOUS_CONNECTION_COMPLETE = 1 << 43
SYNCHRONOUS_CONNECTION_CHANGED = 1 << 44
SNIFF_SUBRATING = 1 << 45
EXTENDED_INQUIRY_RESULT = 1 << 46
ENCRYPTION_KEY_REFRESH_COMPLETE = 1 << 47
IO_CAPABILITY_REQUEST = 1 << 48
IO_CAPABILITY_RESPONSE = 1 << 49
USER_CONFIRMATION_REQUEST = 1 << 50
USER_PASSKEY_REQUEST = 1 << 51
REMOTE_OOB_DATA_REQUEST = 1 << 52
SIMPLE_PAIRING_COMPLETE = 1 << 53
LINK_SUPERVISION_TIMEOUT_CHANGED = 1 << 55
ENHANCED_FLUSH_COMPLETE = 1 << 56
USER_PASSKEY_NOTIFICATION = 1 << 58
KEYPRESS_NOTIFICATION = 1 << 59
REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 1 << 60
LE_META = 1 << 61
[docs] @staticmethod
def get_full_mask():
"""Get full event mask
Returns
-------
EventMask
All events in event mask
"""
return _get_full_mask(EventMask)
[docs] @staticmethod
def from_int(mask: int) -> EventMask:
"""From integer mask to EventMask class
Parameters
----------
mask : int
Interger mask.
Returns
-------
EventMask
EventMask class
"""
clear_mask = EventMask.get_full_mask()
return EventMask(mask & clear_mask.value)
[docs] def as_str_list(self) -> List[str]:
"""Get list of event mask strings
Returns
-------
List[str]
List of event mask strings
"""
flags = [flag.name for flag in EventMask if flag in self]
return flags
[docs]class EventMaskPage2(Flag):
"""Controller Event Mask"""
NUM_CMPLT_DAT_BLOCKS = 1 << 8
TRIG_CLK_CAPTURE = 1 << 14
SYNC_TRAIN_CMPLT = 1 << 15
SYNC_TRAIN_RECEIVED = 1 << 16
CONNECTIONLESS_PERIPH_BROADCAST = 1 << 17
CONNECTIONLESS_PERIPH_TIMEOUT = 1 << 18
TRUNCATED_PAGE_CMPLT = 1 << 19
PERIPH_PAGE_RESP_TIMEOUT = 1 << 20
CONNECTIONLESS_PERIPH_BROADCAST_CH_MAP_CHANGE = 1 << 21
INQUIRY_RESP_NOTIF = 1 << 22
AUTH_PAYLOAD_TIMEOUT_EXPIRED = 1 << 23
SAM_STATUS_CHANGED = 1 << 24
ENC_CHANGED = 1 << 25
[docs] @staticmethod
def get_full_mask() -> EventMaskPage2:
"""Get full event mask page 2
Returns
-------
EventMaskPage2
Full event mask page 2
"""
return _get_full_mask(EventMaskPage2)
[docs] @staticmethod
def from_int(mask: int) -> EventMaskPage2:
"""From integer mask to EventMask class
Parameters
----------
mask : int
Interger mask.
Returns
-------
EventMask
EventMask class
"""
clear_mask = EventMask.get_full_mask()
return EventMask(mask & clear_mask.value)
[docs] def as_str_list(self) -> List[str]:
"""Get list of event mask strings
Returns
-------
List[str]
List of event mask strings
"""
flags = [flag.name for flag in EventMaskPage2 if flag in self]
return flags
[docs]class EventMaskLE(Flag):
"""LE Event Mask"""
CONNECTION_COMPLETE = 1 << 0
ADV_REPORT = 1 << 1
CONN_UPDATE_COMPLETE = 1 << 2
READ_REMOTE_FEAT_CMPLT = 1 << 3
LTK_REQUEST = 1 << 4
REMOTE_CONN_PARAM_REQ = 1 << 5
DATA_LEN_CHANGE = 1 << 6
READ_LOCAL_P256_PUB_KEY_CMPLT = 1 << 7
GEN_DHKEY_CMPLT = 1 << 8
ENHANCED_CONNECTION_COMPLETE = 1 << 9
DIRECTED_ADV_REPORT = 1 << 10
PHY_UPDATE_COMPLETE = 1 << 11
EXTENDED_ADVERTISING_REPORT = 1 << 12
PERIODIC_ADV_SYNC_EST = 1 << 13
PERIODIC_ADV_REPORT = 1 << 14
PERIODIC_ADV_SYNC_LOST = 1 << 15
SCAN_TIMEOUT = 1 << 16
ADVERTISING_SET_TERMINATED = 1 << 17
SCAN_REQUEST_RECEIVED = 1 << 18
CHANNEL_SELECTION_ALGORITHM = 1 << 19
CONNECTIONLESS_IQ_REPORT = 1 << 20
CONNECTION_IQ_REPORT = 1 << 21
CTE_REQUEST_FAILED = 1 << 22
PERIODIC_ADV_SYNC_XFER_LOST = 1 << 23
CIS_ESTABLISHED = 1 << 24
CIS_REQUEST = 1 << 25
CREATE_BIG_COMPLETE = 1 << 26
TERMINATE_BIG_COMPLETE = 1 << 27
BIG_SYNC_ESTABLISHED = 1 << 28
BIG_SYNC_LOST = 1 << 29
REQUEST_PEER_SCA_COMPLETE = 1 << 30
PATH_LOSS_THRESHOLD = 1 << 31
TX_POWER_REPORTING = 1 << 32
BIG_INFO_ADV_REPORT = 1 << 33
SUBRATE_CHANGE = 1 << 34
[docs] @staticmethod
def get_full_mask() -> EventMaskLE:
"""Get full LE Event mask
Returns
-------
EventMaskLE
Event mask with all events
"""
return _get_full_mask(EventMaskLE)
[docs] def as_str_list(self) -> List[str]:
"""List of flags as strings
Returns
-------
List[str]
List of flags as string
"""
flags = [flag.name for flag in EventMaskLE if flag in self]
return flags
[docs] @staticmethod
def from_int(mask: int) -> EventMaskLE:
"""From int to EventMaskLE class
Parameters
----------
mask : int
Integer mask
Returns
-------
EventMaskLE
EventMaskLE class
"""
clear_mask = EventMaskLE.get_full_mask()
return EventMaskLE(mask & clear_mask.value)
[docs]class EventSubcode(Enum):
"""Supported LE Meta event subcodes."""
CONNECTION_CMPLT = 0x1
"""Connection complete event."""
ADVERTISING_REPORT = 0x2
"""Advertising report event."""
CONNECTION_UPDATE = 0x3
"""Connection update complete event."""
READ_REMOTE_FEATURES_COMPLETE = 0x4
"""Read remote feature complete event."""
LTK_REQUEST = 0x5
"""Long term key request event."""
REMOTE_CONNECTION_PARAMETER_REQ = 0x6
"""Remote connection parameter request event."""
DATA_LENGTH_CHANGE = 0x7
"""Data length change event."""
READ_LOCAL_P256_PUB_KEY_CMPLT = 0x8
"""Read local P-256 public key complete event."""
GENERATE_DHKEY_CMPLT = 0x9
"""Generate DHKey complete event."""
ENHANCED_CONNECTION_CMPLT = 0xA
"""Enhanced connection complete event."""
DIRECTED_ADVERTISIING_REPORT = 0xB
"""Directed advertising report event."""
PHY_UPDATE_CMPLT = 0xC
"""PHY update complete event."""
EXTENDED_ADVERTISING_REPORT = 0xD
"""Extended advertising report event."""
PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0xE
"""Periodic advertising sync established event."""
PERIODIC_ADVERTISING_REPORT = 0xF
"""Periodic advertising report event."""
PERIODIC_ADVERTISING_SYNC_LOST = 0x10
"""Periodic advertising sync lost event."""
SCAN_TIMEOUT = 0x11
"""Scan timeout event."""
ADVERTISING_SET_TERMINATED = 0x12
"""Advertising set terminated event."""
SCAN_REQUEST_RECEIVED = 0x13
"""Scan request received event."""
CHANNEL_SELECTION_ALGORITHM = 0x14
"""Channel selection algorithm event."""
CONNECTIONLESS_IQ_REPORT = 0x15
"""Connectionless IQ report event."""
CONNECTION_IQ_REPORT = 0x16
"""Connection IQ report event."""
CTE_REQUEST_FAILED = 0x17
"""CTE request failed event."""
PERIODIC_ADVERTISING_SYNC_TRANSER_RECEIVED = 0x18
"""Periodic advertising sync transfer received event."""
CIS_ESTABLISHED = 0x19
"""CIS established event."""
CIS_REQUEST = 0x1A
"""CIS request event."""
CREATE_BIG_CMPLT = 0x1B
"""Create BIG complete event."""
TERMINATE_BIG_CMPLT = 0x1C
"""Terminate BIG complete event."""
BIG_SYNC_EST = 0x1D
"""BIG sync established event."""
BIG_SYNC_LOST = 0x1E
"""BIG sync list event."""
REQUEST_PEER_SCA_CMPLT = 0x1F
"""Request peeer SCA complete event."""
PATH_LOSS_THRESHOLD = 0x20
"""Path loss threshold event."""
TRANSMIT_POWER_REPORTING = 0x21
"""Transmit power reporting event."""
BIGINFO_ADVERTISING_REPORT = 0x22
"""BIGInfo advertising report event."""
[docs]class StatusCode(Enum):
"""BLE-defined status codes."""
SUCCESS = 0x00
"""Success."""
ERROR_CODE_UNKNOWN_HCI_CMD = 0x01
"""Unknown HCI command error."""
ERROR_CODE_UNKNOWN_CONN_ID = 0x2
"""Unknown connection identifier."""
ERROR_CODE_HW_FAILURE = 0x03
"""Hardware failure."""
ERROR_CODE_PAGE_TIMEOUT = 0x04
"""Page timeout."""
ERROR_CODE_AUTH_FAILURE = 0x05
"""Authentication failure."""
ERROR_CODE_PIN_KEY_MISSING = 0x06
"""PIN or key missing."""
ERROR_CODE_MEM_CAP_EXCEEDED = 0x07
"""Memory capacity exceeded."""
ERROR_CODE_CONN_TIMEOUT = 0x08
"""Connection timeout."""
ERROR_CODE_CONN_LIMIT_EXCEEDED = 0x09
"""Connection limit exceeded."""
ERROR_CODE_SYNCH_CONN_LIMIT_EXCEEDED = 0x0A
"""Synchronous connection limit to a device exceeded."""
ERROR_CODE_ACL_CONN_ALREADY_EXISTS = 0x0B
"""Connection already exists."""
ERROR_CODE_CMD_DISALLOWED = 0x0C
"""Command disallowed."""
ERROR_CODE_CONN_REJ_LIMITED_RESOURCES = 0x0D
"""connection rejection due to limited resources."""
ERROR_CODE_CONN_REJECTED_SECURITY_REASONS = 0x0E
"""Connection rejected due to security reasons."""
ERROR_CODE_CONN_REJECTED_UNACCEPTABLE_BDADDR = 0x0F
"""Connection rejected dur to unacceptable BD address."""
ERROR_CODE_CONN_ACCEPT_TIMEOUT_EXCEEDED = 0x10
"""Connection accept timeout exceeded."""
ERROR_CODE_UNSUPPORTED_FEATURE_PARAM_VALUE = 0x11
"""Unsupported feature or parameter value."""
ERROR_CODE_INVALID_HCI_CMD_PARAMS = 0x12
"""Invalid HCI command parameters."""
ERROR_CODE_REMOTE_USER_TERM_CONN = 0x13
"""Remote user terminated connection."""
ERROR_CODE_REMOTE_DEVICE_TERM_CONN_LOW_RESOURCES = 0x14
"""Remote device terminated connection due to low resources."""
ERROR_CODE_REMOTE_DEVICE_TERM_CONN_POWER_OFF = 0x15
"""Remote device terminated connection due to power off."""
ERROR_CODE_CONN_TERM_BY_LOCAL_HOST = 0x16
"""Connection terminated by local host."""
ERROR_CODE_REPEATED_ATTEMPTS = 0x17
"""Repeated attempts."""
ERROR_CODE_PAIRING_NOT_ALLOWED = 0x18
"""Pairing not allowed."""
ERROR_CODE_UNKNOWN_LMP_PDU = 0x19
"""Unknown LMP PDU."""
ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE = 0x1A
"""Unsupported remote feature."""
ERROR_CODE_SCO_OFFSET_REJ = 0x1B
"""SCO offset rejected."""
ERROR_CODE_SCO_INTERVAL_REJ = 0x1C
"""SCO interval rejected."""
ERROR_CODE_SCO_AIR_MODE_REJ = 0x1D
"""SCO air mode rejected."""
ERROR_CODE_INVALID_LMP_PARAMS = 0x1E
"""Invalid LMP parameters / Invalid LL parameters."""
ERROR_CODE_UNSPECIFIED_ERROR = 0x1F
"""Unspecified error."""
ERROR_CODE_UNSUPPORTED_LMP_PARAM_VAL = 0x20
"""Unsupported LMP parameter value / Unsupported LL parameter value."""
ERROR_CODE_ROLE_CHANGE_NOT_ALLOWED = 0x21
"""Role change not allowed."""
ERROR_CODE_LMP_LL_RESP_TIMEOUT = 0x22
"""LMP response timeout / LL response timeout."""
ERROR_CODE_LMP_ERR_TRANSACTION_COLLISION = 0x23
"""LMP error transaction collision / LL procedure collision."""
ERROR_CODE_LMP_PDU_NOT_ALLOWED = 0x24
"""LMP PDU not allowed."""
ERROR_CODE_ENCRYPT_MODE_NOT_ACCEPTABLE = 0x25
"""Encryption mode not acceptable."""
ERROR_CODE_LINK_KEY_CAN_NOT_BE_CHANGED = 0x26
"""Link key cannot be changed."""
ERROR_CODE_REQ_QOS_NOT_SUPPORTED = 0x27
"""Requested QoS not supported."""
ERROR_CODE_INSTANT_PASSED = 0x28
"""Instance passed."""
ERROR_CODE_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29
"""Pairing with unit key not supported."""
ERROR_CODE_DIFFERENT_TRANSACTION_COLLISION = 0x2A
"""Different transaction collision."""
ERROR_CODE_RESERVED1 = 0x2B
"""Reserved for future use."""
ERROR_CODE_QOS_UNACCEPTABLE_PARAM = 0x2C
"""QoS unacceptable parameter."""
ERROR_CODE_QOS_REJ = 0x2D
"""QoS rejected."""
ERROR_CODE_CHAN_ASSESSMENT_NOT_SUPPORTED = 0x2E
"""Channel classification not supported error."""
ERROR_CODE_INSUFFICIENT_SECURITY = 0x2F
"""Insufficient security."""
ERROR_CODE_PARAM_OUT_OF_MANDATORY_RANGE = 0x30
"""Parameter out of mandatory range."""
ERROR_CODE_RESERVED2 = 0x31
"""Reserved for future use."""
ERROR_CODE_ROLE_SWITCH_PENDING = 0x32
"""Role switch pending."""
ERROR_CODE_RESERVED3 = 0x33
"""Reserved for future use."""
ERROR_CODE_RESERVED_SLOT_VIOLATION = 0x34
"""Reserved slot violation."""
ERROR_CODE_ROLE_SWITCH_FAILED = 0x35
"""Role switch failed."""
ERROR_CODE_EXTENDED_INQUIRY_RESP_TOO_LARGE = 0x36
"""Extended inquiry response too large."""
ERROR_CODE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x37
"""Secure simple pairing not supported by host."""
ERROR_CODE_HOST_BUSY_PAIRING = 0x38
"""Host busy - pairing."""
ERROR_CODE_CONN_REJ_NO_SUITABLE_CHAN_FOUND = 0x39
"""Connection rejected dur to no suitable channel found."""
ERROR_CODE_CONTROLLER_BUSY = 0x3A
"""Controller busy."""
ERROR_CODE_UNACCEPTABLE_CONN_INTERVAL = 0x3B
"""Unacceptable connection parameters."""
ERROR_CODE_ADV_TIMEOUT = 0x3C
"""Advertising timeout."""
ERROR_CODE_CONN_TERM_MIC_FAILURE = 0x3D
"""Connection terminated due to MIC failure."""
ERROR_CODE_CONN_FAILED_TO_ESTABLISH = 0x3E
"""Connection failed to be established / Synchronization timeout."""
ERROR_CODE_MAC_CONN_FAILED = 0x3F
"""MAC connection failed [previously used]."""
ERROR_CODE_COARSE_CLK_ADJ_REJ = 0x40
"""
Coarse cloack adjustment rejected but will try to adjust clock
using clock dragging.
"""
ERROR_CODE_TYPE0_SUBMAP_NOT_DEF = 0x41
"""Type0 submap noot defined."""
ERROR_CODE_UNKNOWN_ADV_ID = 0x42
"""Unknown advertising identifier."""
ERROR_CODE_LIMIT_REACHED = 0x43
"""Limit reached."""
ERROR_CODE_OP_CANCELLED_BY_HOST = 0x44
"""Operation cancelled by host."""
ERROR_CODE_PKT_TOO_LONG = 0x45
"""Packet too long."""
DECODE_FAILURE = 0xFF
"""ADI vendor specific, returns when the status is not properly set."""