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 //! Host-side types
16 
17 use crate::wrapper::{
18     transport::{Sink, Source},
19     wrap_python_async,
20 };
21 use pyo3::{intern, prelude::PyModule, types::PyDict, PyObject, PyResult, Python, ToPyObject};
22 use pyo3_asyncio::tokio::into_future;
23 
24 /// Host HCI commands
25 pub struct Host {
26     pub(crate) obj: PyObject,
27 }
28 
29 impl Host {
30     /// Create a Host that wraps the provided obj
from(obj: PyObject) -> Self31     pub(crate) fn from(obj: PyObject) -> Self {
32         Self { obj }
33     }
34 
35     /// Create a new Host
new(source: Source, sink: Sink) -> PyResult<Self>36     pub async fn new(source: Source, sink: Sink) -> PyResult<Self> {
37         Python::with_gil(|py| {
38             let host_ctr =
39                 PyModule::import(py, intern!(py, "bumble.host"))?.getattr(intern!(py, "Host"))?;
40 
41             let kwargs = PyDict::new(py);
42             kwargs.set_item("controller_source", source.0)?;
43             kwargs.set_item("controller_sink", sink.0)?;
44 
45             // Needed for Python 3.8-3.9, in which the Semaphore object, when constructed, calls
46             // `get_event_loop`.
47             wrap_python_async(py, host_ctr)?
48                 .call((), Some(kwargs))
49                 .and_then(into_future)
50         })?
51         .await
52         .map(|any| Self { obj: any })
53     }
54 
55     /// Send a reset command and perform other reset tasks.
reset(&mut self, driver_factory: DriverFactory) -> PyResult<()>56     pub async fn reset(&mut self, driver_factory: DriverFactory) -> PyResult<()> {
57         Python::with_gil(|py| {
58             let kwargs = match driver_factory {
59                 DriverFactory::None => {
60                     let kw = PyDict::new(py);
61                     kw.set_item("driver_factory", py.None())?;
62                     Some(kw)
63                 }
64                 DriverFactory::Auto => {
65                     // leave the default in place
66                     None
67                 }
68             };
69             self.obj
70                 .call_method(py, intern!(py, "reset"), (), kwargs)
71                 .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
72         })?
73         .await
74         .map(|_| ())
75     }
76 }
77 
78 impl ToPyObject for Host {
to_object(&self, _py: Python<'_>) -> PyObject79     fn to_object(&self, _py: Python<'_>) -> PyObject {
80         self.obj.clone()
81     }
82 }
83 
84 /// Driver factory to use when initializing a host
85 #[derive(Debug, Clone)]
86 pub enum DriverFactory {
87     /// Do not load drivers
88     None,
89     /// Load appropriate driver, if any is found
90     Auto,
91 }
92