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"""
15Drivers that can be used to customize the interaction between a host and a controller,
16like loading firmware after a cold start.
17"""
18
19# -----------------------------------------------------------------------------
20# Imports
21# -----------------------------------------------------------------------------
22from __future__ import annotations
23import logging
24import pathlib
25import platform
26from typing import Dict, Iterable, Optional, Type, TYPE_CHECKING
27
28from . import rtk, intel
29from .common import Driver
30
31if TYPE_CHECKING:
32    from bumble.host import Host
33
34# -----------------------------------------------------------------------------
35# Logging
36# -----------------------------------------------------------------------------
37logger = logging.getLogger(__name__)
38
39
40# -----------------------------------------------------------------------------
41# Functions
42# -----------------------------------------------------------------------------
43async def get_driver_for_host(host: Host) -> Optional[Driver]:
44    """Probe diver classes until one returns a valid instance for a host, or none is
45    found.
46    If a "driver" HCI metadata entry is present, only that driver class will be probed.
47    """
48    driver_classes: Dict[str, Type[Driver]] = {"rtk": rtk.Driver, "intel": intel.Driver}
49    probe_list: Iterable[str]
50    if driver_name := host.hci_metadata.get("driver"):
51        # Only probe a single driver
52        probe_list = [driver_name]
53    else:
54        # Probe all drivers
55        probe_list = driver_classes.keys()
56
57    for driver_name in probe_list:
58        if driver_class := driver_classes.get(driver_name):
59            logger.debug(f"Probing driver class: {driver_name}")
60            if driver := await driver_class.for_host(host):
61                logger.debug(f"Instantiated {driver_name} driver")
62                return driver
63        else:
64            logger.debug(f"Skipping unknown driver class: {driver_name}")
65
66    return None
67
68
69def project_data_dir() -> pathlib.Path:
70    """
71    Returns:
72        A path to an OS-specific directory for bumble data. The directory is created if
73         it doesn't exist.
74    """
75    import platformdirs
76
77    if platform.system() == 'Darwin':
78        # platformdirs doesn't handle macOS right: it doesn't assemble a bundle id
79        # out of author & project
80        return platformdirs.user_data_path(
81            appname='com.google.bumble', ensure_exists=True
82        )
83    else:
84        # windows and linux don't use the com qualifier
85        return platformdirs.user_data_path(
86            appname='bumble', appauthor='google', ensure_exists=True
87        )
88