capsules_extra/
moisture.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 moisture 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 moisture reading.
15//!
16//! ### `command` System Call
17//!
18//! The `command` system call support one argument `cmd` which is used to specify the specific
19//! operation, currently the following cmd's are supported:
20//!
21//! * `0`: check whether the driver exists
22//! * `1`: read moisture
23//!
24//!
25//! The possible return from the 'command' system call indicates the following:
26//!
27//! * `Ok(())`:    The operation has been successful.
28//! * `NOSUPPORT`: Invalid `cmd`.
29//! * `NOMEM`:     Insufficient memory available.
30//! * `INVAL`:     Invalid address of the buffer or other error.
31//!
32//! Usage
33//! -----
34//!
35//! You need a device that provides the `hil::sensors::MoistureDriver` trait.
36//!
37//! ```rust,ignore
38//! # use kernel::static_init;
39//!
40//! let moisture = static_init!(
41//!        capsules::moisture::MoistureSensor<'static>,
42//!        capsules::moisture::MoistureSensor::new(si7021,
43//!                                                board_kernel.create_grant(&grant_cap)));
44//! kernel::hil::sensors::MoistureDriver::set_client(si7021, moisture);
45//! ```
46
47use core::cell::Cell;
48
49use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
50use kernel::hil;
51use kernel::syscall::{CommandReturn, SyscallDriver};
52use kernel::{ErrorCode, ProcessId};
53
54/// Syscall driver number.
55use capsules_core::driver;
56pub const DRIVER_NUM: usize = driver::NUM::Moisture as usize;
57
58#[derive(Clone, Copy, PartialEq)]
59enum MoistureCommand {
60    ReadMoisture,
61}
62
63#[derive(Default)]
64pub struct App {
65    subscribed: bool,
66}
67
68pub struct MoistureSensor<'a, H: hil::sensors::MoistureDriver<'a>> {
69    driver: &'a H,
70    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
71    busy: Cell<bool>,
72}
73
74impl<'a, H: hil::sensors::MoistureDriver<'a>> MoistureSensor<'a, H> {
75    pub fn new(
76        driver: &'a H,
77        grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
78    ) -> MoistureSensor<'a, H> {
79        MoistureSensor {
80            driver,
81            apps: grant,
82            busy: Cell::new(false),
83        }
84    }
85
86    fn enqueue_command(
87        &self,
88        command: MoistureCommand,
89        _arg1: usize,
90        processid: ProcessId,
91    ) -> CommandReturn {
92        self.apps
93            .enter(processid, |app, _| {
94                app.subscribed = true;
95
96                if !self.busy.get() {
97                    self.busy.set(true);
98                    self.call_driver(command)
99                } else {
100                    CommandReturn::success()
101                }
102            })
103            .unwrap_or_else(|err| CommandReturn::failure(err.into()))
104    }
105
106    fn call_driver(&self, command: MoistureCommand) -> CommandReturn {
107        match command {
108            MoistureCommand::ReadMoisture => {
109                let ret = self.driver.read_moisture();
110                if ret.is_err() {
111                    self.busy.set(false);
112                }
113                ret.into()
114            }
115        }
116    }
117}
118
119impl<'a, H: hil::sensors::MoistureDriver<'a>> hil::sensors::MoistureClient
120    for MoistureSensor<'a, H>
121{
122    fn callback(&self, value: Result<usize, ErrorCode>) {
123        self.busy.set(false);
124
125        for cntr in self.apps.iter() {
126            cntr.enter(|app, upcalls| {
127                if app.subscribed {
128                    app.subscribed = false;
129                    match value {
130                        Ok(moisture_val) => upcalls
131                            .schedule_upcall(
132                                0,
133                                (kernel::errorcode::into_statuscode(Ok(())), moisture_val, 0),
134                            )
135                            .ok(),
136                        Err(e) => upcalls
137                            .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0))
138                            .ok(),
139                    };
140                }
141            });
142        }
143    }
144}
145
146impl<'a, H: hil::sensors::MoistureDriver<'a>> SyscallDriver for MoistureSensor<'a, H> {
147    fn command(
148        &self,
149        command_num: usize,
150        arg1: usize,
151        _: usize,
152        processid: ProcessId,
153    ) -> CommandReturn {
154        match command_num {
155            // driver existence check
156            0 => CommandReturn::success(),
157
158            // single moisture measurement
159            1 => self.enqueue_command(MoistureCommand::ReadMoisture, arg1, processid),
160
161            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
162        }
163    }
164
165    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
166        self.apps.enter(processid, |_, _| {})
167    }
168}