capsules_extra/
ambient_light.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//! Shared userland driver for light sensors.
6//!
7//! You need a device that provides the `hil::sensors::AmbientLight` trait.
8//!
9//! ```rust,ignore
10//! # use kernel::{hil, static_init};
11//!
12//! let light = static_init!(
13//!     capsules::ambient_light::AmbientLight<'static>,
14//!     capsules::ambient_light::AmbientLight::new(isl29035,
15//!         board_kernel.create_grant(&grant_cap)));
16//! hil::sensors::AmbientLight::set_client(isl29035, ambient_light);
17//! ```
18
19use core::cell::Cell;
20
21use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
22use kernel::hil;
23use kernel::syscall::{CommandReturn, SyscallDriver};
24use kernel::{ErrorCode, ProcessId};
25
26/// Syscall driver number.
27use capsules_core::driver;
28pub const DRIVER_NUM: usize = driver::NUM::AmbientLight as usize;
29
30/// IDs for subscribed upcalls.
31mod upcall {
32    /// Subscribe to light intensity readings.
33    ///
34    /// The callback signature is `fn(lux: usize)`, where `lux` is the light
35    /// intensity in lux (lx).
36    pub const LIGHT_INTENSITY: usize = 0;
37    /// Number of upcalls.
38    pub const COUNT: u8 = 1;
39}
40
41/// Per-process metadata
42#[derive(Default)]
43pub struct App {
44    pending: bool,
45}
46
47pub struct AmbientLight<'a> {
48    sensor: &'a dyn hil::sensors::AmbientLight<'a>,
49    command_pending: Cell<bool>,
50    apps: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
51}
52
53impl<'a> AmbientLight<'a> {
54    pub fn new(
55        sensor: &'a dyn hil::sensors::AmbientLight<'a>,
56        grant: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
57    ) -> Self {
58        Self {
59            sensor,
60            command_pending: Cell::new(false),
61            apps: grant,
62        }
63    }
64
65    fn enqueue_sensor_reading(&self, processid: ProcessId) -> Result<(), ErrorCode> {
66        self.apps
67            .enter(processid, |app, _| {
68                if app.pending {
69                    Err(ErrorCode::NOMEM)
70                } else {
71                    app.pending = true;
72                    if !self.command_pending.get() {
73                        self.command_pending.set(true);
74                        let _ = self.sensor.read_light_intensity();
75                    }
76                    Ok(())
77                }
78            })
79            .unwrap_or_else(|err| err.into())
80    }
81}
82
83impl SyscallDriver for AmbientLight<'_> {
84    /// Initiate light intensity readings
85    ///
86    /// Sensor readings are coalesced if processes request them concurrently. If
87    /// multiple processes request have outstanding requests for a sensor
88    /// reading, only one command will be issued and the result is returned to
89    /// all subscribed processes.
90    ///
91    /// ### `command_num`
92    ///
93    /// - `0`: Check driver presence
94    /// - `1`: Start a light sensor reading
95    fn command(
96        &self,
97        command_num: usize,
98        _: usize,
99        _: usize,
100        processid: ProcessId,
101    ) -> CommandReturn {
102        match command_num {
103            0 => CommandReturn::success(),
104            1 => {
105                let _ = self.enqueue_sensor_reading(processid);
106                CommandReturn::success()
107            }
108            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
109        }
110    }
111
112    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
113        self.apps.enter(processid, |_, _| {})
114    }
115}
116
117impl hil::sensors::AmbientLightClient for AmbientLight<'_> {
118    fn callback(&self, lux: usize) {
119        self.command_pending.set(false);
120        self.apps.each(|_, app, upcalls| {
121            if app.pending {
122                app.pending = false;
123                upcalls
124                    .schedule_upcall(upcall::LIGHT_INTENSITY, (lux, 0, 0))
125                    .ok();
126            }
127        });
128    }
129}