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}