capsules_extra/
analog_comparator.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 access to the analog comparators on a board.
6//!
7//! Usage
8//! -----
9//!
10//! ```rust,ignore
11//! # use kernel::static_init;
12//!
13//! let ac_channels = static_init!(
14//!     [&'static sam4l::acifc::AcChannel; 2],
15//!     [
16//!         &sam4l::acifc::CHANNEL_AC0,
17//!         &sam4l::acifc::CHANNEL_AC1,
18//!     ]
19//! );
20//! let analog_comparator = static_init!(
21//!     capsules::analog_comparator::AnalogComparator<'static, sam4l::acifc::Acifc>,
22//!     capsules::analog_comparator::AnalogComparator::new(&mut sam4l::acifc::ACIFC, ac_channels)
23//! );
24//! sam4l::acifc::ACIFC.set_client(analog_comparator);
25//! ```
26//!
27//! ## Number of Analog Comparators
28//! The number of analog comparators available depends on the microcontroller/board used.
29//!
30//! ## Normal or Interrupt-based Comparison
31//! For a normal comparison or an interrupt-based comparison, just one analog
32//! comparator is necessary.
33//!
34//! For more information on how this capsule works, please take a look at the
35//! README: 00007_analog_comparator.md in doc/syscalls.
36
37// Author: Danilo Verhaert <verhaert@cs.stanford.edu>
38// Last modified August 9th, 2018
39
40/// Syscall driver number.
41use capsules_core::driver;
42pub const DRIVER_NUM: usize = driver::NUM::AnalogComparator as usize;
43
44use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
45use kernel::hil;
46use kernel::syscall::{CommandReturn, SyscallDriver};
47use kernel::utilities::cells::OptionalCell;
48use kernel::{ErrorCode, ProcessId};
49
50pub struct AnalogComparator<'a, A: hil::analog_comparator::AnalogComparator<'a> + 'a> {
51    // Analog Comparator driver
52    analog_comparator: &'a A,
53    channels: &'a [&'a <A as hil::analog_comparator::AnalogComparator<'a>>::Channel],
54
55    grants: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
56    current_process: OptionalCell<ProcessId>,
57}
58
59#[derive(Default)]
60pub struct App {}
61
62impl<'a, A: hil::analog_comparator::AnalogComparator<'a>> AnalogComparator<'a, A> {
63    pub fn new(
64        analog_comparator: &'a A,
65        channels: &'a [&'a <A as hil::analog_comparator::AnalogComparator<'a>>::Channel],
66        grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
67    ) -> AnalogComparator<'a, A> {
68        AnalogComparator {
69            // Analog Comparator driver
70            analog_comparator,
71            channels,
72            grants: grant,
73            current_process: OptionalCell::empty(),
74        }
75    }
76
77    // Do a single comparison on a channel
78    fn comparison(&self, channel: usize) -> Result<bool, ErrorCode> {
79        if channel >= self.channels.len() {
80            return Err(ErrorCode::INVAL);
81        }
82        // Convert channel index
83        let chan = self.channels[channel];
84        let result = self.analog_comparator.comparison(chan);
85
86        Ok(result)
87    }
88
89    // Start comparing on a channel
90    fn start_comparing(&self, channel: usize) -> Result<(), ErrorCode> {
91        if channel >= self.channels.len() {
92            return Err(ErrorCode::INVAL);
93        }
94        // Convert channel index
95        let chan = self.channels[channel];
96        let result = self.analog_comparator.start_comparing(chan);
97
98        result
99    }
100
101    // Stop comparing on a channel
102    fn stop_comparing(&self, channel: usize) -> Result<(), ErrorCode> {
103        if channel >= self.channels.len() {
104            return Err(ErrorCode::INVAL);
105        }
106        // Convert channel index
107        let chan = self.channels[channel];
108        let result = self.analog_comparator.stop_comparing(chan);
109
110        result
111    }
112}
113
114impl<'a, A: hil::analog_comparator::AnalogComparator<'a>> SyscallDriver
115    for AnalogComparator<'a, A>
116{
117    /// Control the analog comparator.
118    ///
119    /// ### `command_num`
120    ///
121    /// - `0`: Driver existence check.
122    /// - `1`: Perform a simple comparison. Input x chooses the desired
123    ///   comparator ACx (e.g. 0 or 1 for hail, 0-3 for imix)
124    /// - `2`: Start interrupt-based comparisons. Input x chooses the desired
125    ///   comparator ACx (e.g. 0 or 1 for hail, 0-3 for imix)
126    /// - `3`: Stop interrupt-based comparisons. Input x chooses the desired
127    ///   comparator ACx (e.g. 0 or 1 for hail, 0-3 for imix)
128    /// - `4`: Get number of channels.
129    fn command(
130        &self,
131        command_num: usize,
132        channel: usize,
133        _: usize,
134        processid: ProcessId,
135    ) -> CommandReturn {
136        if command_num == 0 {
137            // Handle unconditional driver existence check.
138            return CommandReturn::success();
139        }
140
141        // Check if this driver is free, or already dedicated to this process.
142        let match_or_empty_or_nonexistant = self.current_process.map_or(true, |current_process| {
143            self.grants
144                .enter(current_process, |_, _| current_process == processid)
145                .unwrap_or(true)
146        });
147        if match_or_empty_or_nonexistant {
148            self.current_process.set(processid);
149        } else {
150            return CommandReturn::failure(ErrorCode::NOMEM);
151        }
152
153        match command_num {
154            0 => CommandReturn::success_u32(self.channels.len() as u32),
155
156            1 => match self.comparison(channel) {
157                Ok(b) => CommandReturn::success_u32(b as u32),
158                Err(e) => CommandReturn::failure(e),
159            },
160
161            2 => self.start_comparing(channel).into(),
162
163            3 => self.stop_comparing(channel).into(),
164
165            4 => CommandReturn::success_u32(self.channels.len() as u32),
166
167            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
168        }
169    }
170
171    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
172        self.grants.enter(processid, |_, _| {})
173    }
174}
175
176impl<'a, A: hil::analog_comparator::AnalogComparator<'a>> hil::analog_comparator::Client
177    for AnalogComparator<'a, A>
178{
179    /// Upcall to userland, signaling the application
180    fn fired(&self, channel: usize) {
181        self.current_process.map(|processid| {
182            let _ = self.grants.enter(processid, |_app, upcalls| {
183                upcalls.schedule_upcall(0, (channel, 0, 0)).ok();
184            });
185        });
186    }
187}