1 // Copyright 2023 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 //     http://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 //! Drivers for Realtek controllers
16 
17 use crate::wrapper::{host::Host, PyObjectExt};
18 use pyo3::{intern, types::PyModule, PyObject, PyResult, Python, ToPyObject};
19 use pyo3_asyncio::tokio::into_future;
20 
21 pub use crate::internal::drivers::rtk::{Firmware, Patch};
22 
23 /// Driver for a Realtek controller
24 pub struct Driver(PyObject);
25 
26 impl Driver {
27     /// Locate the driver for the provided host.
for_host(host: &Host, force: bool) -> PyResult<Option<Self>>28     pub async fn for_host(host: &Host, force: bool) -> PyResult<Option<Self>> {
29         Python::with_gil(|py| {
30             PyModule::import(py, intern!(py, "bumble.drivers.rtk"))?
31                 .getattr(intern!(py, "Driver"))?
32                 .call_method1(intern!(py, "for_host"), (&host.obj, force))
33                 .and_then(into_future)
34         })?
35         .await
36         .map(|obj| obj.into_option(Self))
37     }
38 
39     /// Check if the host has a known driver.
check(host: &Host) -> PyResult<bool>40     pub async fn check(host: &Host) -> PyResult<bool> {
41         Python::with_gil(|py| {
42             PyModule::import(py, intern!(py, "bumble.drivers.rtk"))?
43                 .getattr(intern!(py, "Driver"))?
44                 .call_method1(intern!(py, "check"), (&host.obj,))
45                 .and_then(|obj| obj.extract::<bool>())
46         })
47     }
48 
49     /// Find the [DriverInfo] for the host, if one matches
driver_info_for_host(host: &Host) -> PyResult<Option<DriverInfo>>50     pub async fn driver_info_for_host(host: &Host) -> PyResult<Option<DriverInfo>> {
51         Python::with_gil(|py| {
52             PyModule::import(py, intern!(py, "bumble.drivers.rtk"))?
53                 .getattr(intern!(py, "Driver"))?
54                 .call_method1(intern!(py, "driver_info_for_host"), (&host.obj,))
55                 .and_then(into_future)
56         })?
57         .await
58         .map(|obj| obj.into_option(DriverInfo))
59     }
60 
61     /// Send a command to the device to drop firmware
drop_firmware(host: &mut Host) -> PyResult<()>62     pub async fn drop_firmware(host: &mut Host) -> PyResult<()> {
63         Python::with_gil(|py| {
64             PyModule::import(py, intern!(py, "bumble.drivers.rtk"))?
65                 .getattr(intern!(py, "Driver"))?
66                 .call_method1(intern!(py, "drop_firmware"), (&host.obj,))
67                 .and_then(into_future)
68         })?
69         .await
70         .map(|_| ())
71     }
72 
73     /// Load firmware onto the device.
download_firmware(&mut self) -> PyResult<()>74     pub async fn download_firmware(&mut self) -> PyResult<()> {
75         Python::with_gil(|py| {
76             self.0
77                 .call_method0(py, intern!(py, "download_firmware"))
78                 .and_then(|coroutine| into_future(coroutine.as_ref(py)))
79         })?
80         .await
81         .map(|_| ())
82     }
83 }
84 
85 /// Metadata about a known driver & applicable device
86 pub struct DriverInfo(PyObject);
87 
88 impl DriverInfo {
89     /// Returns a list of all drivers that Bumble knows how to handle.
all_drivers() -> PyResult<Vec<DriverInfo>>90     pub fn all_drivers() -> PyResult<Vec<DriverInfo>> {
91         Python::with_gil(|py| {
92             PyModule::import(py, intern!(py, "bumble.drivers.rtk"))?
93                 .getattr(intern!(py, "Driver"))?
94                 .getattr(intern!(py, "DRIVER_INFOS"))?
95                 .iter()?
96                 .map(|r| r.map(|h| DriverInfo(h.to_object(py))))
97                 .collect::<PyResult<Vec<_>>>()
98         })
99     }
100 
101     /// The firmware file name to load from the filesystem, e.g. `foo_fw.bin`.
firmware_name(&self) -> PyResult<String>102     pub fn firmware_name(&self) -> PyResult<String> {
103         Python::with_gil(|py| {
104             self.0
105                 .getattr(py, intern!(py, "fw_name"))?
106                 .as_ref(py)
107                 .extract::<String>()
108         })
109     }
110 
111     /// The config file name, if any, to load from the filesystem, e.g. `foo_config.bin`.
config_name(&self) -> PyResult<Option<String>>112     pub fn config_name(&self) -> PyResult<Option<String>> {
113         Python::with_gil(|py| {
114             let obj = self.0.getattr(py, intern!(py, "config_name"))?;
115             let handle = obj.as_ref(py);
116 
117             if handle.is_none() {
118                 Ok(None)
119             } else {
120                 handle
121                     .extract::<String>()
122                     .map(|s| if s.is_empty() { None } else { Some(s) })
123             }
124         })
125     }
126 
127     /// Whether or not config is required.
config_needed(&self) -> PyResult<bool>128     pub fn config_needed(&self) -> PyResult<bool> {
129         Python::with_gil(|py| {
130             self.0
131                 .getattr(py, intern!(py, "config_needed"))?
132                 .as_ref(py)
133                 .extract::<bool>()
134         })
135     }
136 
137     /// ROM id
rom(&self) -> PyResult<u32>138     pub fn rom(&self) -> PyResult<u32> {
139         Python::with_gil(|py| self.0.getattr(py, intern!(py, "rom"))?.as_ref(py).extract())
140     }
141 }
142