openthread/ot/
cli.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::prelude_internal::*;
use std::os::raw::c_int;

/// Methods from the [OpenThread "CLI" Module](https://openthread.io/reference/group/api-cli).
pub trait Cli {
    /// Functional equivalent of [`otsys::otCliInputLine`](crate::otsys::otCliInputLine).
    fn cli_input_line(&self, line: &CStr);

    /// Functional equivalent of [`otsys::otCliInit`](crate::otsys::otCliInit).
    fn cli_init<'a, F>(&self, output_callback: F)
    where
        F: FnMut(&CStr) + 'a;
}

impl<T: Cli + ot::Boxable> Cli for ot::Box<T> {
    fn cli_input_line(&self, line: &CStr) {
        self.as_ref().cli_input_line(line);
    }

    fn cli_init<'a, F>(&self, output_callback: F)
    where
        F: FnMut(&CStr) + 'a,
    {
        self.as_ref().cli_init(output_callback);
    }
}

impl Cli for Instance {
    fn cli_input_line(&self, line: &CStr) {
        unsafe { otCliInputLine(line.as_ptr() as *mut c_char) }
    }

    fn cli_init<'a, F>(&self, f: F)
    where
        F: FnMut(&CStr) + 'a,
    {
        unsafe extern "C" fn _ot_cli_output_callback(
            context: *mut ::std::os::raw::c_void,
            line: *const c_char,
            _: *mut otsys::__va_list_tag,
        ) -> c_int {
            let line = CStr::from_ptr(line);

            trace!("_ot_cli_output_callback: {:?}", line);

            // Reconstitute a reference to our instance
            let instance = Instance::ref_from_ot_ptr(context as *mut otInstance).unwrap();

            // Get a reference to our instance backing.
            let backing = instance.borrow_backing();

            // Remove our line sender from the cell in the backing.
            // We will replace it once we are done.
            let mut sender_option = backing.cli_output_fn.replace(None);

            if let Some(sender) = sender_option.as_mut() {
                sender(line);
            } else {
                info!("_ot_cli_output_callback: Nothing to handle CLI output");
            }

            // Put the sender back into the backing so that we can
            // use it again later.
            if let Some(new_sender) = backing.cli_output_fn.replace(sender_option) {
                // In this case someone called the init function while
                // they were in the callback. We want to restore that
                // callback.
                backing.cli_output_fn.set(Some(new_sender));
            }

            line.to_bytes().len().try_into().unwrap()
        }

        // Put our sender closure in a box.
        // This will go into the backing.
        let fn_box = Box::new(f) as Box<dyn FnMut(&CStr) + 'a>;

        // Prepare a function pointer for our callback closure.
        let cb: otCliOutputCallback = Some(_ot_cli_output_callback);

        // Go ahead and get our context pointer ready for the callback.
        // In this case our context pointer is the `otInstance` pointer.
        let fn_ptr = self.as_ot_ptr() as *mut ::std::os::raw::c_void;

        unsafe {
            // Make sure our object eventually gets cleaned up.
            // Here we must also transmute our closure to have a 'static
            // lifetime.
            // SAFETY: We need to do this because the borrow checker
            //         cannot infer the proper lifetime for the
            //         singleton instance backing, but this is guaranteed
            //         by the API.
            let prev_callback =
                self.borrow_backing().cli_output_fn.replace(std::mem::transmute::<
                    Option<Box<dyn FnMut(&'_ CStr) + 'a>>,
                    Option<Box<dyn FnMut(&'_ CStr) + 'static>>,
                >(Some(fn_box)));

            // Check to see if there was a previous callback registered.
            // We only want to call `otCliInit()` if there was no
            // previously registered callback.
            if prev_callback.is_none() {
                // SAFETY: This must only be called once.
                otCliInit(self.as_ot_ptr(), cb, fn_ptr);
            }
        }
    }
}