# 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. Defaults to the OS-specific" "app data dir, which the driver will check when trying to find firmware", 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 if output_dir == '': output_dir = rtk.rtk_firmware_dir() else: 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()