1# -*- coding: utf-8 -*- 2# Copyright 2019 Oticon A/S 3# SPDX-License-Identifier: Apache-2.0 4 5from enum import IntEnum; 6from components.utils import *; 7from components.basic_commands import *; 8from components.address import *; 9from components.events import *; 10 11class Advertising(IntEnum): 12 CONNECTABLE_UNDIRECTED = 0 # Connectable undirected advertising (ADV_IND) (default). 13 CONNECTABLE_HDC_DIRECTED = 1 # Connectable high duty cycle directed advertising (ADV_DIRECT_IND, high duty cycle). 14 SCANNABLE_UNDIRECTED = 2 # Scannable undirected advertising (ADV_SCAN_IND). 15 NON_CONNECTABLE_UNDIRECTED = 3 # Non connectable undirected advertising (ADV_NONCONN_IND). 16 CONNECTABLE_LDC_DIRECTED = 4 # Connectable low duty cycle directed advertising (ADV_DIRECT_IND, low duty cycle). 17 18class Advertise(IntEnum): 19 DISABLE = 0 # Disable Advertising 20 ENABLE = 1 # Enable Advertising 21 22class AdvertisingFilterPolicy(IntEnum): 23 FILTER_NONE = 0 # Process scan and connection requests from all devices (i.e., the Filter Accept List is not in use) (default). 24 FILTER_SCAN_REQUESTS = 1 # Process connection requests from all devices and only scan requests from devices that are in the Filter Accept List. 25 FILTER_CONNECTION_REQUESTS = 2 # Process scan requests from all devices and only connection requests from devices that are in the Filter Accept List. 26 FILTER_BOTH_REQUESTS = 3 # Process scan and connection requests only from devices in the Filter Accept List. 27 28class AdvertiseChannel(IntEnum): 29 CHANNEL_37 = 1 # Advertise on channel 37 30 CHANNEL_38 = 2 # Advertise on channel 38 31 CHANNEL_39 = 4 # Advertise on channel 39 32 ALL_CHANNELS = 7 # Advertise on all three channels 33 34class ExtAdvertiseType(IntEnum): 35 CONNECTABLE = 1 # bit 0 ~ Connectable advertising 36 SCANNABLE = 2 # bit 1 ~ Scannable advertising 37 DIRECTED = 4 # bit 2 ~ Directed advertising 38 CONNECTABLE_HDC_DIRECTED = 8 # bit 3 ~ High Duty Cycle Directed Connectable advertising (≤ 3.75 ms Advertising Interval) 39 LEGACY = 16 # bit 4 ~ Use legacy advertising PDUs 40 ANONYMOUS = 32 # bit 5 ~ Omit advertiser's address from all PDUs ("anonymous advertising") 41 INCLUDE_TX_POWER = 64 # bit 6 ~ Include TxPower in the extended header of the advertising PDU 42 43class Advertiser: 44 """ 45 An Advertiser handles all aspects of advertising. 46 - Set Advertising parameters including Advertising data. 47 - Set Scan response data. 48 - Enable Advertising. 49 - Disable Advertising. 50 """ 51 """ 52 Constructor: 53 transport - EDTTT object 54 idx - Number; Device identifier 55 trace - Trace object 56 channels - AdvertiseChannels enum holding the channel or channels to advertise on 57 advertiseType - Advertising enum holding the type of advertising to emit 58 ownAddress - Address object with an ExtendedAddressType address (only the address type is used) 59 peerAddress - Address object with a SimpleAddressType address (both address type and address are used) 60 filterPolicy - AdvertisingFilterPolicy enum 61 advertiseData - Array of Bytes holding advertise data 62 responseData - Array of Bytes holding response data 63 """ 64 def __init__(self, transport, idx, trace, channels, advertiseType, ownAddress, peerAddress, filterPolicy=AdvertisingFilterPolicy.FILTER_NONE, \ 65 advertiseData=None, responseData=None): 66 self.transport = transport; 67 self.idx = idx; 68 self.trace = trace; 69 self.channels = channels; 70 self.advertiseType = advertiseType; 71 """ 72 Own_Address_Type parameter indicates the type of address being used in the advertising packets. 73 74 If Own_Address_Type equals 0x02 or 0x03, the Peer_Address parameter contains the peer’s Identity Address and 75 the Peer_Address_Type parameter contains the Peer’s Identity Type (i.e. 0x00 or 0x01). 76 These parameters are used to locate the corresponding local IRK in the resolving list; this IRK is used to generate 77 the own address used in the advertisement. 78 79 If Own_Address_Type equals 0x02 or 0x03, the Controller generates the peer’s Resolvable Private Address using the 80 peer’s IRK corresponding to the peer’s Identity Address contained in the Peer_Address parameter and peer’s Identity 81 Address Type (i.e. 0x00 or 0x01) contained in the Peer_Address_Type parameter. 82 83 If directed advertising is performed, i.e. when Advertising_Type is set to 0x01 (ADV_DIRECT_IND, high duty cycle) or 84 0x04 (ADV_DIRECT_IND, low duty cycle mode), then the Peer_Address_Type and Peer_Address shall be valid. 85 """ 86 self.ownAddress = ownAddress; 87 self.peerAddress = peerAddress; 88 89 self.advertiseData = [] if advertiseData is None else advertiseData[ : ]; 90 self.responseData = [] if responseData is None else responseData[ : ]; 91 """ 92 The Advertising_Interval_Min shall be less than or equal to the Advertising_Interval_Max. 93 The Advertising_Interval_Min and Advertising_Interval_Max should not be the same value to enable the Controller to 94 determine the best advertising interval given other activities. 95 96 For high duty cycle directed advertising, i.e. when Advertising_Type is 0x01 (ADV_DIRECT_IND, high duty cycle), 97 the Advertising_Interval_Min and Advertising_Interval_Max parameters are not used and shall be ignored. 98 """ 99 self.minInterval = 32; # Minimum Advertise Interval = 32 x 0.625 ms = 20.00 ms 100 self.maxInterval = 32; # Maximum Advertise Interval = 32 x 0.625 ms = 20.00 ms 101 self.filterPolicy = filterPolicy; 102 self.status = 0; 103 104 def __verifyAndShowEvent(self, expectedEvent): 105 event = get_event(self.transport, self.idx, 200); 106 self.trace.trace(7, str(event)); 107 return event.event == expectedEvent; 108 109 def __getCommandCompleteEvent(self): 110 return self.__verifyAndShowEvent(Events.BT_HCI_EVT_CMD_COMPLETE); 111 112 def __confined_array(self, data, limit): 113 dataSize = len(data) if len(data) <= limit else limit; 114 dataCopy = data[ : ]; 115 if len(data) < limit: 116 dataCopy += [0 for _ in range(limit - dataSize)]; 117 elif len(data) > limit: 118 dataCopy = dataCopy[:limit]; 119 return dataSize, dataCopy; 120 121 def __set_advertise_parameters(self): 122 123 self.status = le_set_advertising_parameters(self.transport, self.idx, self.minInterval, self.maxInterval, self.advertiseType, self.ownAddress.type, \ 124 self.peerAddress.type, self.peerAddress.address, self.channels, self.filterPolicy, 200); 125 self.trace.trace(6, "LE Set Advertising Parameters Command returns status: 0x%02X" % self.status); 126 return self.__getCommandCompleteEvent() and (self.status == 0); 127 128 def __set_advertise_data(self): 129 130 dataSize, advertiseData = self.__confined_array(self.advertiseData, 31); 131 132 self.status = le_set_advertising_data(self.transport, self.idx, dataSize, advertiseData, 200); 133 self.trace.trace(6, "LE Set Advertising Data Command returns status: 0x%02X" % self.status); 134 return self.__getCommandCompleteEvent() and (self.status == 0); 135 136 def __set_scan_response(self): 137 138 dataSize, responseData = self.__confined_array(self.responseData, 31); 139 140 self.status = le_set_scan_response_data(self.transport, self.idx, dataSize, responseData, 200); 141 self.trace.trace(6, "LE Set Scan Response Data Command returns status: 0x%02X" % self.status); 142 return self.__getCommandCompleteEvent() and (self.status == 0); 143 144 def __advertise_enable(self, enable): 145 146 self.status = le_set_advertising_enable(self.transport, self.idx, enable, 200); 147 self.trace.trace(6, "LE Set Advertising Enable Command (%s) returns status: 0x%02X" % ("Enabling" if enable else "Disabling", self.status)); 148 return self.__getCommandCompleteEvent() and (self.status == 0); 149 150 """ 151 Enable advertising - start emitting Advertise packages 152 """ 153 def enable(self, sameSetup=False): 154 if not sameSetup: 155 success = self.__set_advertise_parameters(); 156 success = success and self.__set_advertise_data(); 157 success = success and self.__set_scan_response(); 158 else: 159 success = True; 160 success = success and self.__advertise_enable(Advertise.ENABLE); 161 return success; 162 163 """ 164 Disable advertising - stop emitting Advertise packages 165 """ 166 def disable(self): 167 return self.__advertise_enable(Advertise.DISABLE); 168 169 """ 170 Check for a advertise timeout - when using connectable high duty cycle directed advertising 171 """ 172 def timeout(self, timeout=200): 173 self.status = 0; 174 if has_event(self.transport, self.idx, timeout)[0]: 175 event = get_event(self.transport, self.idx, 200); 176 self.trace.trace(7, str(event)); 177 if (event.subEvent == MetaEvents.BT_HCI_EVT_LE_CONN_COMPLETE) or (event.subEvent == MetaEvents.BT_HCI_EVT_LE_ENH_CONN_COMPLETE): 178 self.status = event.decode()[0]; 179 return self.status == 0x3C; 180