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}