capsules_extra/
sound_pressure.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Provides userspace with access to sound_pressure sensors.
6//!
7//! Userspace Interface
8//! -------------------
9//!
10//! ### `subscribe` System Call
11//!
12//! The `subscribe` system call supports the single `subscribe_number` zero,
13//! which is used to provide a callback that will return back the result of
14//! a sound_pressure sensor reading.
15//! The `subscribe`call return codes indicate the following:
16//!
17//! * `Ok(())`: the callback been successfully been configured.
18//! * `ENOSUPPORT`: Invalid allow_num.
19//! * `NOMEM`: No sufficient memory available.
20//! * `INVAL`: Invalid address of the buffer or other error.
21//!
22//!
23//! ### `command` System Call
24//!
25//! The `command` system call support one argument `cmd` which is used to specify the specific
26//! operation, currently the following cmd's are supported:
27//!
28//! * `0`: check whether the driver exist
29//! * `1`: read the sound_pressure
30//!
31//!
32//! The possible return from the 'command' system call indicates the following:
33//!
34//! * `Ok(())`:    The operation has been successful.
35//! * `BUSY`:      The driver is busy.
36//! * `ENOSUPPORT`: Invalid `cmd`.
37//! * `NOMEM`:     No sufficient memory available.
38//! * `INVAL`:     Invalid address of the buffer or other error.
39//!
40//! Usage
41//! -----
42//!
43//! You need a device that provides the `hil::sensors::SoundPressure` trait.
44//!
45//! ```rust,ignore
46//! # use kernel::static_init;
47//!
48//! let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
49//! let grant_sound_pressure = board_kernel.create_grant(&grant_cap);
50//!
51//! let temp = static_init!(
52//!        capsules::sound_pressure::SoundPressureSensor<'static>,
53//!        capsules::sound_pressure::SoundPressureSensor::new(si7021,
54//!                                                 board_kernel.create_grant(&grant_cap)));
55//!
56//! kernel::hil::sensors::SoundPressure::set_client(si7021, temp);
57//! ```
58
59use core::cell::Cell;
60use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
61use kernel::hil;
62use kernel::syscall::{CommandReturn, SyscallDriver};
63use kernel::{ErrorCode, ProcessId};
64
65/// Syscall driver number.
66use capsules_core::driver;
67pub const DRIVER_NUM: usize = driver::NUM::SoundPressure as usize;
68
69#[derive(Default)]
70pub struct App {
71    subscribed: bool,
72    enable: bool,
73}
74
75pub struct SoundPressureSensor<'a> {
76    driver: &'a dyn hil::sensors::SoundPressure<'a>,
77    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
78    busy: Cell<bool>,
79}
80
81impl<'a> SoundPressureSensor<'a> {
82    pub fn new(
83        driver: &'a dyn hil::sensors::SoundPressure<'a>,
84        grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
85    ) -> SoundPressureSensor<'a> {
86        SoundPressureSensor {
87            driver,
88            apps: grant,
89            busy: Cell::new(false),
90        }
91    }
92
93    fn enqueue_command(&self, processid: ProcessId) -> CommandReturn {
94        self.apps
95            .enter(processid, |app, _| {
96                if !self.busy.get() {
97                    app.subscribed = true;
98                    self.busy.set(true);
99                    let res = self.driver.read_sound_pressure();
100                    if let Ok(err) = ErrorCode::try_from(res) {
101                        CommandReturn::failure(err)
102                    } else {
103                        CommandReturn::success()
104                    }
105                } else {
106                    CommandReturn::failure(ErrorCode::BUSY)
107                }
108            })
109            .unwrap_or_else(|err| CommandReturn::failure(err.into()))
110    }
111
112    fn enable(&self) {
113        let mut enable = false;
114        for app in self.apps.iter() {
115            app.enter(|app, _| {
116                if app.enable {
117                    enable = true;
118                }
119            });
120            if enable {
121                let _ = self.driver.enable();
122            } else {
123                let _ = self.driver.disable();
124            }
125        }
126    }
127}
128
129impl hil::sensors::SoundPressureClient for SoundPressureSensor<'_> {
130    fn callback(&self, ret: Result<(), ErrorCode>, sound_val: u8) {
131        for cntr in self.apps.iter() {
132            cntr.enter(|app, upcalls| {
133                if app.subscribed {
134                    self.busy.set(false);
135                    app.subscribed = false;
136                    if ret == Ok(()) {
137                        upcalls.schedule_upcall(0, (sound_val.into(), 0, 0)).ok();
138                    }
139                }
140            });
141        }
142    }
143}
144
145impl SyscallDriver for SoundPressureSensor<'_> {
146    fn command(
147        &self,
148        command_num: usize,
149        _: usize,
150        _: usize,
151        processid: ProcessId,
152    ) -> CommandReturn {
153        match command_num {
154            // check whether the driver exists!!
155            0 => CommandReturn::success(),
156
157            // read sound_pressure
158            1 => self.enqueue_command(processid),
159
160            // enable
161            2 => {
162                let res = self
163                    .apps
164                    .enter(processid, |app, _| {
165                        app.enable = true;
166                        CommandReturn::success()
167                    })
168                    .map_err(ErrorCode::from);
169                if let Err(e) = res {
170                    CommandReturn::failure(e)
171                } else {
172                    self.enable();
173                    CommandReturn::success()
174                }
175            }
176
177            // disable
178            3 => {
179                let res = self
180                    .apps
181                    .enter(processid, |app, _| {
182                        app.enable = false;
183                        CommandReturn::success()
184                    })
185                    .map_err(ErrorCode::from);
186                if let Err(e) = res {
187                    CommandReturn::failure(e)
188                } else {
189                    self.enable();
190                    CommandReturn::success()
191                }
192            }
193            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
194        }
195    }
196
197    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
198        self.apps.enter(processid, |_, _| {})
199    }
200}