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