capsules_extra/
rainfall.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 rain fall 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 rain fall 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 rainfall
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::RainFallDriver` trait.
36
37use core::cell::Cell;
38
39use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
40use kernel::hil;
41use kernel::syscall::{CommandReturn, SyscallDriver};
42use kernel::{ErrorCode, ProcessId};
43
44/// Syscall driver number.
45use capsules_core::driver;
46pub const DRIVER_NUM: usize = driver::NUM::RainFall as usize;
47
48#[derive(Clone, Copy, PartialEq)]
49enum RainFallCommand {
50    ReadRainFall,
51}
52
53#[derive(Default)]
54pub struct App {
55    subscribed: bool,
56}
57
58pub struct RainFallSensor<'a, H: hil::sensors::RainFallDriver<'a>> {
59    driver: &'a H,
60    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
61    busy: Cell<bool>,
62}
63
64impl<'a, H: hil::sensors::RainFallDriver<'a>> RainFallSensor<'a, H> {
65    pub fn new(
66        driver: &'a H,
67        grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
68    ) -> RainFallSensor<'a, H> {
69        RainFallSensor {
70            driver,
71            apps: grant,
72            busy: Cell::new(false),
73        }
74    }
75
76    fn enqueue_command(
77        &self,
78        command: RainFallCommand,
79        arg1: usize,
80        processid: ProcessId,
81    ) -> CommandReturn {
82        self.apps
83            .enter(processid, |app, _| {
84                app.subscribed = true;
85
86                if !self.busy.get() {
87                    self.busy.set(true);
88                    self.call_driver(command, arg1)
89                } else {
90                    CommandReturn::success()
91                }
92            })
93            .unwrap_or_else(|err| CommandReturn::failure(err.into()))
94    }
95
96    fn call_driver(&self, command: RainFallCommand, hours: usize) -> CommandReturn {
97        match command {
98            RainFallCommand::ReadRainFall => {
99                let ret = self.driver.read_rainfall(hours);
100                if ret.is_err() {
101                    self.busy.set(false);
102                }
103                ret.into()
104            }
105        }
106    }
107}
108
109impl<'a, H: hil::sensors::RainFallDriver<'a>> hil::sensors::RainFallClient
110    for RainFallSensor<'a, H>
111{
112    fn callback(&self, value: Result<usize, ErrorCode>) {
113        self.busy.set(false);
114
115        for cntr in self.apps.iter() {
116            cntr.enter(|app, upcalls| {
117                if app.subscribed {
118                    app.subscribed = false;
119                    match value {
120                        Ok(rainfall_val) => upcalls
121                            .schedule_upcall(
122                                0,
123                                (kernel::errorcode::into_statuscode(Ok(())), rainfall_val, 0),
124                            )
125                            .ok(),
126                        Err(e) => upcalls
127                            .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0))
128                            .ok(),
129                    };
130                }
131            });
132        }
133    }
134}
135
136impl<'a, H: hil::sensors::RainFallDriver<'a>> SyscallDriver for RainFallSensor<'a, H> {
137    fn command(
138        &self,
139        command_num: usize,
140        arg1: usize,
141        _: usize,
142        processid: ProcessId,
143    ) -> CommandReturn {
144        match command_num {
145            // driver existence check
146            0 => CommandReturn::success(),
147
148            // single rainfall measurement
149            1 => self.enqueue_command(RainFallCommand::ReadRainFall, arg1, processid),
150
151            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
152        }
153    }
154
155    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
156        self.apps.enter(processid, |_, _| {})
157    }
158}