capsules_extra/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 2023.
4
5//! Provides userspace with access to barometer 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 barometer 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 barometer
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::PressureDriver` trait.
44//!
45//! ```rust,ignore
46//! # use kernel::static_init;
47//!
48//! let pressure = static_init!(
49//! capsules::temperature::PressureSensor<'static>,
50//! capsules::temperature::PressureSensor::new(si7021,
51//! board_kernel.create_grant(&grant_cap)));
52//!
53//! kernel::hil::sensors::PressureDriver::set_client(si7021, pressure);
54//! ```
55
56use core::cell::Cell;
57
58use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
59use kernel::hil;
60use kernel::syscall::{CommandReturn, SyscallDriver};
61use kernel::{ErrorCode, ProcessId};
62
63/// Syscall driver number.
64use capsules_core::driver;
65pub const DRIVER_NUM: usize = driver::NUM::Pressure as usize;
66
67#[derive(Default)]
68pub struct App {
69 subscribed: bool,
70}
71
72pub struct PressureSensor<'a, T: hil::sensors::PressureDriver<'a>> {
73 driver: &'a T,
74 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
75 busy: Cell<bool>,
76}
77
78impl<'a, T: hil::sensors::PressureDriver<'a>> PressureSensor<'a, T> {
79 pub fn new(
80 driver: &'a T,
81 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
82 ) -> PressureSensor<'a, T> {
83 PressureSensor {
84 driver,
85 apps,
86 busy: Cell::new(false),
87 }
88 }
89
90 fn enqueue_command(&self, processid: ProcessId) -> CommandReturn {
91 self.apps
92 .enter(processid, |app, _| {
93 app.subscribed = true;
94 if !self.busy.get() {
95 let res = self.driver.read_atmospheric_pressure();
96 if let Ok(err) = ErrorCode::try_from(res) {
97 CommandReturn::failure(err)
98 } else {
99 self.busy.set(true);
100 CommandReturn::success()
101 }
102 } else {
103 CommandReturn::success()
104 }
105 })
106 .unwrap_or_else(|err| CommandReturn::failure(err.into()))
107 }
108}
109
110impl<'a, T: hil::sensors::PressureDriver<'a>> hil::sensors::PressureClient
111 for PressureSensor<'a, T>
112{
113 fn callback(&self, pressure: Result<u32, ErrorCode>) {
114 self.busy.set(false);
115 for cntr in self.apps.iter() {
116 cntr.enter(|app, upcalls| {
117 if app.subscribed {
118 app.subscribed = false;
119 let result = match pressure {
120 Ok(pressure_value) => (
121 kernel::errorcode::into_statuscode(Ok(())),
122 pressure_value as usize,
123 0,
124 ),
125 Err(err) => (kernel::errorcode::into_statuscode(Err(err)), 0, 0),
126 };
127 upcalls.schedule_upcall(0, result).ok();
128 }
129 })
130 }
131 }
132}
133
134impl<'a, T: hil::sensors::PressureDriver<'a>> SyscallDriver for PressureSensor<'a, T> {
135 fn command(
136 &self,
137 command_num: usize,
138 _: usize,
139 _: usize,
140 process_id: ProcessId,
141 ) -> CommandReturn {
142 match command_num {
143 0 => CommandReturn::success(),
144 1 => self.enqueue_command(process_id),
145 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
146 }
147 }
148
149 fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> {
150 self.apps.enter(process_id, |_, _| {})
151 }
152}