openthread/ot/
joiner.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::prelude_internal::*;
6
7/// Represents the thread joiner state.
8///
9/// Functional equivalent of [`otsys::otJoinerState`](crate::otsys::otJoinerState).
10#[derive(Debug, Copy, Clone, Eq, Ord, PartialOrd, PartialEq, num_derive::FromPrimitive)]
11pub enum JoinerState {
12    /// Functional equivalent of [`otsys::OT_JOINER_STATE_IDLE`](crate::otsys::OT_JOINER_STATE_IDLE).
13    Idle = OT_JOINER_STATE_IDLE as isize,
14
15    /// Functional equivalent of [`otsys::OT_JOINER_STATE_DISCOVER`](crate::otsys::OT_JOINER_STATE_DISCOVER).
16    Discover = OT_JOINER_STATE_DISCOVER as isize,
17
18    /// Functional equivalent of [`otsys::OT_JOINER_STATE_CONNECT`](crate::otsys::OT_JOINER_STATE_CONNECT).
19    Connect = OT_JOINER_STATE_CONNECT as isize,
20
21    /// Functional equivalent of [`otsys::OT_JOINER_STATE_CONNECTED`](crate::otsys::OT_JOINER_STATE_CONNECTED).
22    Connected = OT_JOINER_STATE_CONNECTED as isize,
23
24    /// Functional equivalent of [`otsys::OT_JOINER_STATE_ENTRUST`](crate::otsys::OT_JOINER_STATE_ENTRUST).
25    Entrust = OT_JOINER_STATE_ENTRUST as isize,
26
27    /// Functional equivalent of [`otsys::OT_JOINER_STATE_JOINED`](crate::otsys::OT_JOINER_STATE_JOINED).
28    Joined = OT_JOINER_STATE_JOINED as isize,
29}
30
31impl From<otJoinerState> for JoinerState {
32    fn from(x: otJoinerState) -> Self {
33        use num::FromPrimitive;
34        Self::from_u32(x).unwrap_or_else(|| panic!("Unknown otJoinerState value: {x}"))
35    }
36}
37
38impl From<JoinerState> for otJoinerState {
39    fn from(x: JoinerState) -> Self {
40        x as otJoinerState
41    }
42}
43
44/// Methods from the [OpenThread "Joiner" Module][1].
45///
46/// [1]: https://openthread.io/reference/group/api-joiner
47pub trait Joiner {
48    /// Functional equivalent of
49    /// [`otsys::otJoinerStart`](crate::otsys::otJoinerStart).
50    #[allow(clippy::too_many_arguments)]
51    fn joiner_start<'a, F: FnOnce(Result) + 'a>(
52        &self,
53        pskd: &str,
54        provisioning_url: Option<&str>,
55        vendor_name: Option<&str>,
56        vendor_model: Option<&str>,
57        vendor_sw_version: Option<&str>,
58        vendor_data: Option<&str>,
59        callback: F,
60    ) -> Result;
61
62    /// Functional equivalent of
63    /// [`otsys::otJoinerStop`](crate::otsys::otJoinerStop).
64    fn joiner_stop(&self);
65
66    /// Functional equivalent of
67    /// [`otsys::otJoinerGetState`](crate::otsys::otJoinerGetState).
68    fn joiner_get_state(&self) -> JoinerState;
69
70    /// Similar to [`joiner_start()`], but as an async method.
71    ///
72    /// Note that the current implementation of this method will start the joining
73    /// process immediately, rather than lazily.
74    fn joiner_start_async(
75        &self,
76        pskd: &str,
77        provisioning_url: Option<&str>,
78        vendor_name: Option<&str>,
79        vendor_model: Option<&str>,
80        vendor_sw_version: Option<&str>,
81        vendor_data: Option<&str>,
82    ) -> futures::channel::oneshot::Receiver<Result> {
83        use futures::channel::oneshot::*;
84        let (sender, receiver) = channel();
85        let sender = std::sync::Arc::new(fuchsia_sync::Mutex::new(Some(sender)));
86        let sender_clone = sender.clone();
87        if let Err(err) = self.joiner_start(
88            pskd,
89            provisioning_url,
90            vendor_name,
91            vendor_model,
92            vendor_sw_version,
93            vendor_data,
94            move |result| {
95                let _ = sender_clone
96                    .lock()
97                    .take()
98                    .and_then(move |x: Sender<Result>| x.send(result).ok());
99            },
100        ) {
101            sender.lock().take().and_then(move |x: Sender<Result>| x.send(Err(err)).ok());
102        }
103        receiver
104    }
105}
106
107impl<T: Joiner + Boxable> Joiner for ot::Box<T> {
108    fn joiner_start<'a, F: FnOnce(Result) + 'a>(
109        &self,
110        pskd: &str,
111        provisioning_url: Option<&str>,
112        vendor_name: Option<&str>,
113        vendor_model: Option<&str>,
114        vendor_sw_version: Option<&str>,
115        vendor_data: Option<&str>,
116        callback: F,
117    ) -> Result {
118        self.as_ref().joiner_start(
119            pskd,
120            provisioning_url,
121            vendor_name,
122            vendor_model,
123            vendor_sw_version,
124            vendor_data,
125            callback,
126        )
127    }
128
129    fn joiner_stop(&self) {
130        self.as_ref().joiner_stop()
131    }
132
133    fn joiner_get_state(&self) -> JoinerState {
134        self.as_ref().joiner_get_state()
135    }
136}
137
138impl Joiner for Instance {
139    fn joiner_start<'a, F: FnOnce(Result) + 'a>(
140        &self,
141        pskd: &str,
142        provisioning_url: Option<&str>,
143        vendor_name: Option<&str>,
144        vendor_model: Option<&str>,
145        vendor_sw_version: Option<&str>,
146        vendor_data: Option<&str>,
147        callback: F,
148    ) -> Result {
149        unsafe extern "C" fn _ot_joiner_callback(
150            error: otError,
151            context: *mut ::std::os::raw::c_void,
152        ) {
153            trace!("_ot_joiner_callback");
154
155            // Reconstitute a reference to our closure.
156            let instance =
157                unsafe { Instance::ref_from_ot_ptr(context as *mut otInstance).unwrap() };
158
159            if let Some(func) = instance.borrow_backing().joiner_fn.take() {
160                func(Error::from(error).into_result());
161            }
162        }
163
164        let pskd = CString::new(pskd).map_err(|_| Error::InvalidArgs)?;
165        let provisioning_url =
166            provisioning_url.map(CString::new).transpose().map_err(|_| Error::InvalidArgs)?;
167        let vendor_name =
168            vendor_name.map(CString::new).transpose().map_err(|_| Error::InvalidArgs)?;
169        let vendor_model =
170            vendor_model.map(CString::new).transpose().map_err(|_| Error::InvalidArgs)?;
171        let vendor_sw_version =
172            vendor_sw_version.map(CString::new).transpose().map_err(|_| Error::InvalidArgs)?;
173        let vendor_data =
174            vendor_data.map(CString::new).transpose().map_err(|_| Error::InvalidArgs)?;
175
176        let pskd_ptr = pskd.as_bytes_with_nul().as_ptr() as *const ::std::os::raw::c_char;
177        let provisioning_url_ptr =
178            provisioning_url.as_ref().map(|x| x.as_bytes_with_nul().as_ptr()).unwrap_or(null())
179                as *const ::std::os::raw::c_char;
180        let vendor_name_ptr =
181            vendor_name.as_ref().map(|x| x.as_bytes_with_nul().as_ptr()).unwrap_or(null())
182                as *const ::std::os::raw::c_char;
183        let vendor_model_ptr =
184            vendor_model.as_ref().map(|x| x.as_bytes_with_nul().as_ptr()).unwrap_or(null())
185                as *const ::std::os::raw::c_char;
186        let vendor_sw_version_ptr =
187            vendor_sw_version.as_ref().map(|x| x.as_bytes_with_nul().as_ptr()).unwrap_or(null())
188                as *const ::std::os::raw::c_char;
189        let vendor_data_ptr =
190            vendor_data.as_ref().map(|x| x.as_bytes_with_nul().as_ptr()).unwrap_or(null())
191                as *const ::std::os::raw::c_char;
192
193        let fn_box = Some(Box::new(callback) as Box<dyn FnOnce(Result) + 'a>);
194
195        let ret = Error::from(unsafe {
196            // Make sure our object eventually gets cleaned up.
197            // Here we must also transmute our closure to have a 'static lifetime.
198            // We need to do this because the borrow checker cannot infer the
199            // proper lifetime for the singleton instance backing, but
200            // this is guaranteed by the API.
201            self.borrow_backing().joiner_fn.set(std::mem::transmute::<
202                Option<Box<dyn FnOnce(Result) + 'a>>,
203                Option<Box<dyn FnOnce(Result) + 'static>>,
204            >(fn_box));
205
206            otJoinerStart(
207                self.as_ot_ptr(),
208                pskd_ptr,
209                provisioning_url_ptr,
210                vendor_name_ptr,
211                vendor_model_ptr,
212                vendor_sw_version_ptr,
213                vendor_data_ptr,
214                Some(_ot_joiner_callback),
215                self.as_ot_ptr() as *mut ::std::os::raw::c_void,
216            )
217        })
218        .into_result();
219
220        if ret.is_err() {
221            // There was an error during otJoinerStart,
222            // Make sure the callback is cleaned up.
223            self.borrow_backing().joiner_fn.take();
224        }
225
226        ret
227    }
228
229    fn joiner_stop(&self) {
230        unsafe { otJoinerStop(self.as_ot_ptr()) }
231
232        // Make sure the callback is cleaned up.
233        self.borrow_backing().joiner_fn.take();
234    }
235
236    fn joiner_get_state(&self) -> JoinerState {
237        unsafe { otJoinerGetState(self.as_ot_ptr()) }.into()
238    }
239}