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}