capsules_core/virtualizers/
virtual_adc.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//! Virtual ADC Capsule
6//!
7//! Support Single Sample for now.
8
9use kernel::collections::list::{List, ListLink, ListNode};
10use kernel::hil;
11use kernel::utilities::cells::OptionalCell;
12use kernel::ErrorCode;
13
14/// ADC Mux
15pub struct MuxAdc<'a, A: hil::adc::Adc<'a>> {
16    adc: &'a A,
17    devices: List<'a, AdcDevice<'a, A>>,
18    inflight: OptionalCell<&'a AdcDevice<'a, A>>,
19}
20
21impl<'a, A: hil::adc::Adc<'a>> hil::adc::Client for MuxAdc<'a, A> {
22    fn sample_ready(&self, sample: u16) {
23        self.inflight.take().map(|inflight| {
24            for node in self.devices.iter() {
25                if node.channel == inflight.channel {
26                    node.operation.take().map(|operation| match operation {
27                        Operation::OneSample => {
28                            node.client.map(|client| client.sample_ready(sample))
29                        }
30                    });
31                }
32            }
33        });
34        self.do_next_op();
35    }
36}
37
38impl<'a, A: hil::adc::Adc<'a>> MuxAdc<'a, A> {
39    pub const fn new(adc: &'a A) -> MuxAdc<'a, A> {
40        MuxAdc {
41            adc,
42            devices: List::new(),
43            inflight: OptionalCell::empty(),
44        }
45    }
46
47    fn do_next_op(&self) {
48        if self.inflight.is_none() {
49            let mnode = self.devices.iter().find(|node| node.operation.is_some());
50            mnode.map(|node| {
51                let started = node.operation.map_or(false, |operation| match operation {
52                    Operation::OneSample => {
53                        let _ = self.adc.sample(&node.channel);
54                        true
55                    }
56                });
57                if started {
58                    self.inflight.set(node);
59                } else {
60                    self.do_next_op();
61                }
62            });
63        }
64    }
65
66    pub fn get_resolution_bits(&self) -> usize {
67        self.adc.get_resolution_bits()
68    }
69
70    pub fn get_voltage_reference_mv(&self) -> Option<usize> {
71        self.adc.get_voltage_reference_mv()
72    }
73}
74
75#[derive(Copy, Clone, PartialEq)]
76pub(crate) enum Operation {
77    OneSample,
78}
79
80/// Virtual ADC device
81pub struct AdcDevice<'a, A: hil::adc::Adc<'a>> {
82    mux: &'a MuxAdc<'a, A>,
83    channel: A::Channel,
84    operation: OptionalCell<Operation>,
85    next: ListLink<'a, AdcDevice<'a, A>>,
86    client: OptionalCell<&'a dyn hil::adc::Client>,
87}
88
89impl<'a, A: hil::adc::Adc<'a>> AdcDevice<'a, A> {
90    pub const fn new(mux: &'a MuxAdc<'a, A>, channel: A::Channel) -> AdcDevice<'a, A> {
91        let adc_user = AdcDevice {
92            mux,
93            channel,
94            operation: OptionalCell::empty(),
95            next: ListLink::empty(),
96            client: OptionalCell::empty(),
97        };
98        adc_user
99    }
100
101    pub fn add_to_mux(&'a self) {
102        self.mux.devices.push_head(self);
103    }
104}
105
106impl<'a, A: hil::adc::Adc<'a>> ListNode<'a, AdcDevice<'a, A>> for AdcDevice<'a, A> {
107    fn next(&'a self) -> &'a ListLink<'a, AdcDevice<'a, A>> {
108        &self.next
109    }
110}
111
112impl<'a, A: hil::adc::Adc<'a>> hil::adc::AdcChannel<'a> for AdcDevice<'a, A> {
113    fn sample(&self) -> Result<(), ErrorCode> {
114        self.operation.set(Operation::OneSample);
115        self.mux.do_next_op();
116        Ok(())
117    }
118
119    fn stop_sampling(&self) -> Result<(), ErrorCode> {
120        self.operation.clear();
121        self.mux.do_next_op();
122        Ok(())
123    }
124
125    fn sample_continuous(&self) -> Result<(), ErrorCode> {
126        Err(ErrorCode::NOSUPPORT)
127    }
128
129    fn get_resolution_bits(&self) -> usize {
130        self.mux.get_resolution_bits()
131    }
132
133    fn get_voltage_reference_mv(&self) -> Option<usize> {
134        self.mux.get_voltage_reference_mv()
135    }
136    fn set_client(&self, client: &'a dyn hil::adc::Client) {
137        self.client.set(client);
138    }
139}