openthread/ot/
cli.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::*;
6use std::os::raw::c_int;
7
8/// Methods from the [OpenThread "CLI" Module](https://openthread.io/reference/group/api-cli).
9pub trait Cli {
10    /// Functional equivalent of [`otsys::otCliInputLine`](crate::otsys::otCliInputLine).
11    fn cli_input_line(&self, line: &CStr);
12
13    /// Functional equivalent of [`otsys::otCliInit`](crate::otsys::otCliInit).
14    fn cli_init<'a, F>(&self, output_callback: F)
15    where
16        F: FnMut(&CStr) + 'a;
17}
18
19impl<T: Cli + ot::Boxable> Cli for ot::Box<T> {
20    fn cli_input_line(&self, line: &CStr) {
21        self.as_ref().cli_input_line(line);
22    }
23
24    fn cli_init<'a, F>(&self, output_callback: F)
25    where
26        F: FnMut(&CStr) + 'a,
27    {
28        self.as_ref().cli_init(output_callback);
29    }
30}
31
32impl Cli for Instance {
33    fn cli_input_line(&self, line: &CStr) {
34        unsafe { otCliInputLine(line.as_ptr() as *mut c_char) }
35    }
36
37    fn cli_init<'a, F>(&self, f: F)
38    where
39        F: FnMut(&CStr) + 'a,
40    {
41        unsafe extern "C" fn _ot_cli_output_callback(
42            context: *mut ::std::os::raw::c_void,
43            line: *const c_char,
44            _: *mut otsys::__va_list_tag,
45        ) -> c_int {
46            let line = unsafe { CStr::from_ptr(line) };
47
48            trace!("_ot_cli_output_callback: {:?}", line);
49
50            // Reconstitute a reference to our instance
51            let instance =
52                unsafe { Instance::ref_from_ot_ptr(context as *mut otInstance) }.unwrap();
53
54            // Get a reference to our instance backing.
55            let backing = instance.borrow_backing();
56
57            // Remove our line sender from the cell in the backing.
58            // We will replace it once we are done.
59            let mut sender_option = backing.cli_output_fn.replace(None);
60
61            if let Some(sender) = sender_option.as_mut() {
62                sender(line);
63            } else {
64                info!("_ot_cli_output_callback: Nothing to handle CLI output");
65            }
66
67            // Put the sender back into the backing so that we can
68            // use it again later.
69            if let Some(new_sender) = backing.cli_output_fn.replace(sender_option) {
70                // In this case someone called the init function while
71                // they were in the callback. We want to restore that
72                // callback.
73                backing.cli_output_fn.set(Some(new_sender));
74            }
75
76            line.to_bytes().len().try_into().unwrap()
77        }
78
79        // Put our sender closure in a box.
80        // This will go into the backing.
81        let fn_box = Box::new(f) as Box<dyn FnMut(&CStr) + 'a>;
82
83        // Prepare a function pointer for our callback closure.
84        let cb: otCliOutputCallback = Some(_ot_cli_output_callback);
85
86        // Go ahead and get our context pointer ready for the callback.
87        // In this case our context pointer is the `otInstance` pointer.
88        let fn_ptr = self.as_ot_ptr() as *mut ::std::os::raw::c_void;
89
90        unsafe {
91            // Make sure our object eventually gets cleaned up.
92            // Here we must also transmute our closure to have a 'static
93            // lifetime.
94            // SAFETY: We need to do this because the borrow checker
95            //         cannot infer the proper lifetime for the
96            //         singleton instance backing, but this is guaranteed
97            //         by the API.
98            let prev_callback =
99                self.borrow_backing().cli_output_fn.replace(std::mem::transmute::<
100                    Option<Box<dyn FnMut(&'_ CStr) + 'a>>,
101                    Option<Box<dyn FnMut(&'_ CStr) + 'static>>,
102                >(Some(fn_box)));
103
104            // Check to see if there was a previous callback registered.
105            // We only want to call `otCliInit()` if there was no
106            // previously registered callback.
107            if prev_callback.is_none() {
108                // SAFETY: This must only be called once.
109                otCliInit(self.as_ot_ptr(), cb, fn_ptr);
110            }
111        }
112    }
113}