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 //! HCI packet transport
16 
17 use crate::wrapper::controller::Controller;
18 use futures::executor::block_on;
19 use pyo3::{intern, types::PyModule, PyObject, PyResult, Python};
20 
21 /// A source/sink pair for HCI packet I/O.
22 ///
23 /// See <https://google.github.io/bumble/transports/index.html>.
24 pub struct Transport(PyObject);
25 
26 impl Transport {
27     /// Open a new Transport for the provided spec, e.g. `"usb:0"` or `"android-netsim"`.
open(transport_spec: impl Into<String>) -> PyResult<Self>28     pub async fn open(transport_spec: impl Into<String>) -> PyResult<Self> {
29         Python::with_gil(|py| {
30             PyModule::import(py, intern!(py, "bumble.transport"))?
31                 .call_method1(intern!(py, "open_transport"), (transport_spec.into(),))
32                 .and_then(pyo3_asyncio::tokio::into_future)
33         })?
34         .await
35         .map(Self)
36     }
37 
38     /// Close the transport.
close(&mut self) -> PyResult<()>39     pub async fn close(&mut self) -> PyResult<()> {
40         Python::with_gil(|py| {
41             self.0
42                 .call_method0(py, intern!(py, "close"))
43                 .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
44         })?
45         .await
46         .map(|_| ())
47     }
48 
49     /// Returns the source half of the transport.
source(&self) -> PyResult<Source>50     pub fn source(&self) -> PyResult<Source> {
51         Python::with_gil(|py| self.0.getattr(py, intern!(py, "source"))).map(Source)
52     }
53 
54     /// Returns the sink half of the transport.
sink(&self) -> PyResult<Sink>55     pub fn sink(&self) -> PyResult<Sink> {
56         Python::with_gil(|py| self.0.getattr(py, intern!(py, "sink"))).map(Sink)
57     }
58 }
59 
60 impl Drop for Transport {
drop(&mut self)61     fn drop(&mut self) {
62         // don't spawn a thread to handle closing, as it may get dropped at program termination,
63         // resulting in `RuntimeWarning: coroutine ... was never awaited` from Python
64         let _ = block_on(self.close());
65     }
66 }
67 
68 /// The source side of a [Transport].
69 #[derive(Clone)]
70 pub struct Source(pub(crate) PyObject);
71 
72 impl From<Controller> for Source {
from(value: Controller) -> Self73     fn from(value: Controller) -> Self {
74         Self(value.0)
75     }
76 }
77 
78 /// The sink side of a [Transport].
79 #[derive(Clone)]
80 pub struct Sink(pub(crate) PyObject);
81 
82 impl From<Controller> for Sink {
from(value: Controller) -> Self83     fn from(value: Controller) -> Self {
84         Self(value.0)
85     }
86 }
87