• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021-2022 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# -----------------------------------------------------------------------------
16# Imports
17# -----------------------------------------------------------------------------
18import asyncio
19import os
20import logging
21import click
22from bumble.company_ids import COMPANY_IDENTIFIERS
23
24from bumble.colors import color
25from bumble.core import name_or_number
26from bumble.hci import (
27    map_null_terminated_utf8_string,
28    HCI_SUCCESS,
29    HCI_LE_SUPPORTED_FEATURES_NAMES,
30    HCI_VERSION_NAMES,
31    LMP_VERSION_NAMES,
32    HCI_Command,
33    HCI_Command_Complete_Event,
34    HCI_Command_Status_Event,
35    HCI_READ_BD_ADDR_COMMAND,
36    HCI_Read_BD_ADDR_Command,
37    HCI_READ_LOCAL_NAME_COMMAND,
38    HCI_Read_Local_Name_Command,
39    HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND,
40    HCI_LE_Read_Maximum_Data_Length_Command,
41    HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND,
42    HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command,
43    HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND,
44    HCI_LE_Read_Maximum_Advertising_Data_Length_Command,
45)
46from bumble.host import Host
47from bumble.transport import open_transport_or_link
48
49
50# -----------------------------------------------------------------------------
51def command_succeeded(response):
52    if isinstance(response, HCI_Command_Status_Event):
53        return response.status == HCI_SUCCESS
54    if isinstance(response, HCI_Command_Complete_Event):
55        return response.return_parameters.status == HCI_SUCCESS
56    return False
57
58
59# -----------------------------------------------------------------------------
60async def get_classic_info(host):
61    if host.supports_command(HCI_READ_BD_ADDR_COMMAND):
62        response = await host.send_command(HCI_Read_BD_ADDR_Command())
63        if command_succeeded(response):
64            print()
65            print(
66                color('Classic Address:', 'yellow'), response.return_parameters.bd_addr
67            )
68
69    if host.supports_command(HCI_READ_LOCAL_NAME_COMMAND):
70        response = await host.send_command(HCI_Read_Local_Name_Command())
71        if command_succeeded(response):
72            print()
73            print(
74                color('Local Name:', 'yellow'),
75                map_null_terminated_utf8_string(response.return_parameters.local_name),
76            )
77
78
79# -----------------------------------------------------------------------------
80async def get_le_info(host):
81    print()
82
83    if host.supports_command(HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND):
84        response = await host.send_command(
85            HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command()
86        )
87        if command_succeeded(response):
88            print(
89                color('LE Number Of Supported Advertising Sets:', 'yellow'),
90                response.return_parameters.num_supported_advertising_sets,
91                '\n',
92            )
93
94    if host.supports_command(HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND):
95        response = await host.send_command(
96            HCI_LE_Read_Maximum_Advertising_Data_Length_Command()
97        )
98        if command_succeeded(response):
99            print(
100                color('LE Maximum Advertising Data Length:', 'yellow'),
101                response.return_parameters.max_advertising_data_length,
102                '\n',
103            )
104
105    if host.supports_command(HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND):
106        response = await host.send_command(HCI_LE_Read_Maximum_Data_Length_Command())
107        if command_succeeded(response):
108            print(
109                color('Maximum Data Length:', 'yellow'),
110                (
111                    f'tx:{response.return_parameters.supported_max_tx_octets}/'
112                    f'{response.return_parameters.supported_max_tx_time}, '
113                    f'rx:{response.return_parameters.supported_max_rx_octets}/'
114                    f'{response.return_parameters.supported_max_rx_time}'
115                ),
116                '\n',
117            )
118
119    print(color('LE Features:', 'yellow'))
120    for feature in host.supported_le_features:
121        print('  ', name_or_number(HCI_LE_SUPPORTED_FEATURES_NAMES, feature))
122
123
124# -----------------------------------------------------------------------------
125async def async_main(transport):
126    print('<<< connecting to HCI...')
127    async with await open_transport_or_link(transport) as (hci_source, hci_sink):
128        print('<<< connected')
129
130        host = Host(hci_source, hci_sink)
131        await host.reset()
132
133        # Print version
134        print(color('Version:', 'yellow'))
135        print(
136            color('  Manufacturer:  ', 'green'),
137            name_or_number(COMPANY_IDENTIFIERS, host.local_version.company_identifier),
138        )
139        print(
140            color('  HCI Version:   ', 'green'),
141            name_or_number(HCI_VERSION_NAMES, host.local_version.hci_version),
142        )
143        print(color('  HCI Subversion:', 'green'), host.local_version.hci_subversion)
144        print(
145            color('  LMP Version:   ', 'green'),
146            name_or_number(LMP_VERSION_NAMES, host.local_version.lmp_version),
147        )
148        print(color('  LMP Subversion:', 'green'), host.local_version.lmp_subversion)
149
150        # Get the Classic info
151        await get_classic_info(host)
152
153        # Get the LE info
154        await get_le_info(host)
155
156        # Print the list of commands supported by the controller
157        print()
158        print(color('Supported Commands:', 'yellow'))
159        for command in host.supported_commands:
160            print('  ', HCI_Command.command_name(command))
161
162
163# -----------------------------------------------------------------------------
164@click.command()
165@click.argument('transport')
166def main(transport):
167    logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
168    asyncio.run(async_main(transport))
169
170
171# -----------------------------------------------------------------------------
172if __name__ == '__main__':
173    main()
174