forked from auracaster/bumble_mirror
add downloader tool
This commit is contained in:
@@ -24,11 +24,12 @@ url = https://github.com/google/bumble
|
|||||||
|
|
||||||
[options]
|
[options]
|
||||||
python_requires = >=3.8
|
python_requires = >=3.8
|
||||||
packages = bumble, bumble.transport, bumble.drivers, bumble.profiles, bumble.apps, bumble.apps.link_relay, bumble.pandora
|
packages = bumble, bumble.transport, bumble.drivers, bumble.profiles, bumble.apps, bumble.apps.link_relay, bumble.pandora, bumble.tools
|
||||||
package_dir =
|
package_dir =
|
||||||
bumble = bumble
|
bumble = bumble
|
||||||
bumble.apps = apps
|
bumble.apps = apps
|
||||||
include-package-data = True
|
bumble.tools = tools
|
||||||
|
include_package_data = True
|
||||||
install_requires =
|
install_requires =
|
||||||
aiohttp ~= 3.8; platform_system!='Emscripten'
|
aiohttp ~= 3.8; platform_system!='Emscripten'
|
||||||
appdirs >= 1.4
|
appdirs >= 1.4
|
||||||
@@ -64,6 +65,8 @@ console_scripts =
|
|||||||
bumble-bench = bumble.apps.bench:main
|
bumble-bench = bumble.apps.bench:main
|
||||||
bumble-speaker = bumble.apps.speaker.speaker:main
|
bumble-speaker = bumble.apps.speaker.speaker:main
|
||||||
bumble-pandora-server = bumble.apps.pandora_server:main
|
bumble-pandora-server = bumble.apps.pandora_server:main
|
||||||
|
bumble-rtk-util = bumble.tools.rtk_util:main
|
||||||
|
bumble-rtk-fw-download = bumble.tools.rtk_fw_download:main
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
* = py.typed, *.pyi
|
* = py.typed, *.pyi
|
||||||
|
|||||||
153
tools/rtk_fw_download.py
Normal file
153
tools/rtk_fw_download.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
# Copyright 2021-2023 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
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
import logging
|
||||||
|
import pathlib
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from bumble.colors import color
|
||||||
|
from bumble.drivers import rtk
|
||||||
|
from bumble.tools import rtk_util
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Logging
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Constants
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
LINUX_KERNEL_GIT_SOURCE = (
|
||||||
|
"https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/rtl_bt",
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
REALTEK_OPENSOURCE_SOURCE = (
|
||||||
|
"https://github.com/Realtek-OpenSource/android_hardware_realtek/raw/rtk1395/bt/rtkbt/Firmware/BT",
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
LINUX_FROM_SCRATCH_SOURCE = (
|
||||||
|
"https://anduin.linuxfromscratch.org/sources/linux-firmware/rtl_bt",
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Functions
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def download_file(base_url, name, remove_suffix):
|
||||||
|
if remove_suffix:
|
||||||
|
name = name.replace(".bin", "")
|
||||||
|
|
||||||
|
url = f"{base_url}/{name}"
|
||||||
|
with urllib.request.urlopen(url) as file:
|
||||||
|
data = file.read()
|
||||||
|
print(f"Downloaded {name}: {len(data)} bytes")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
@click.command
|
||||||
|
@click.option(
|
||||||
|
"--output-dir",
|
||||||
|
default=".",
|
||||||
|
help="Output directory where the files will be saved",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--source",
|
||||||
|
type=click.Choice(["linux-kernel", "realtek-opensource", "linux-from-scratch"]),
|
||||||
|
default="linux-kernel",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("--single", help="Only download a single image set, by its base name")
|
||||||
|
@click.option("--force", is_flag=True, help="Overwrite files if they already exist")
|
||||||
|
@click.option("--parse", is_flag=True, help="Parse the FW image after saving")
|
||||||
|
def main(output_dir, source, single, force, parse):
|
||||||
|
"""Download RTK firmware images and configs."""
|
||||||
|
|
||||||
|
# Check that the output dir exists
|
||||||
|
output_dir = pathlib.Path(output_dir)
|
||||||
|
if not output_dir.is_dir():
|
||||||
|
print("Output dir does not exist or is not a directory")
|
||||||
|
return
|
||||||
|
|
||||||
|
base_url, remove_suffix = {
|
||||||
|
"linux-kernel": LINUX_KERNEL_GIT_SOURCE,
|
||||||
|
"realtek-opensource": REALTEK_OPENSOURCE_SOURCE,
|
||||||
|
"linux-from-scratch": LINUX_FROM_SCRATCH_SOURCE
|
||||||
|
}[source]
|
||||||
|
|
||||||
|
print("Downloading")
|
||||||
|
print(color("FROM:", "green"), base_url)
|
||||||
|
print(color("TO:", "green"), output_dir)
|
||||||
|
|
||||||
|
if single:
|
||||||
|
images = [(f"{single}_fw.bin", f"{single}_config.bin", True)]
|
||||||
|
else:
|
||||||
|
images = [
|
||||||
|
(
|
||||||
|
driver_info.fw_name,
|
||||||
|
driver_info.config_name,
|
||||||
|
driver_info.config_needed
|
||||||
|
)
|
||||||
|
for driver_info in rtk.Driver.DRIVER_INFOS
|
||||||
|
]
|
||||||
|
|
||||||
|
for (fw_name, config_name, config_needed) in images:
|
||||||
|
print(color("---", "yellow"))
|
||||||
|
fw_image_out = output_dir / fw_name
|
||||||
|
if not force and fw_image_out.exists():
|
||||||
|
print(color(f"{fw_image_out} already exists, skipping", "red"))
|
||||||
|
continue
|
||||||
|
if config_name:
|
||||||
|
config_image_out = output_dir / config_name
|
||||||
|
if not force and config_image_out.exists():
|
||||||
|
print(color("f{config_out} already exists, skipping", "red"))
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
fw_image = download_file(base_url, fw_name, remove_suffix)
|
||||||
|
except urllib.error.HTTPError as error:
|
||||||
|
print(f"Failed to download {fw_name}: {error}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
config_image = None
|
||||||
|
if config_name:
|
||||||
|
try:
|
||||||
|
config_image = download_file(base_url, config_name, remove_suffix)
|
||||||
|
except urllib.error.HTTPError as error:
|
||||||
|
if config_needed:
|
||||||
|
print(f"Failed to download {config_name}: {error}")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
print(f"No config available as {config_name}")
|
||||||
|
|
||||||
|
fw_image_out.write_bytes(fw_image)
|
||||||
|
if parse and config_name:
|
||||||
|
print(color("Parsing:", "cyan"), fw_name)
|
||||||
|
rtk_util.do_parse(fw_image_out)
|
||||||
|
if config_image:
|
||||||
|
config_image_out.write_bytes(config_image)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2021-2022 Google LLC
|
# Copyright 2021-2023 Google LLC
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -36,7 +36,10 @@ def do_parse(firmware_path):
|
|||||||
with open(firmware_path, 'rb') as firmware_file:
|
with open(firmware_path, 'rb') as firmware_file:
|
||||||
firmware_data = firmware_file.read()
|
firmware_data = firmware_file.read()
|
||||||
firmware = rtk.Firmware(firmware_data)
|
firmware = rtk.Firmware(firmware_data)
|
||||||
print(f'Firmware: version=0x{firmware.version:08X} project_id=0x{firmware.project_id:04X}')
|
print(
|
||||||
|
f"Firmware: version=0x{firmware.version:08X} "
|
||||||
|
f"project_id=0x{firmware.project_id:04X}"
|
||||||
|
)
|
||||||
for patch in firmware.patches:
|
for patch in firmware.patches:
|
||||||
print(
|
print(
|
||||||
f" Patch: chip_id=0x{patch[0]:04X}, "
|
f" Patch: chip_id=0x{patch[0]:04X}, "
|
||||||
@@ -110,7 +113,7 @@ async def do_info(usb_transport, force):
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@click.group()
|
@click.group()
|
||||||
def main():
|
def main():
|
||||||
pass
|
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
|
||||||
|
|
||||||
|
|
||||||
@main.command
|
@main.command
|
||||||
@@ -155,5 +158,4 @@ def info(usb_transport, force):
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
|
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user