capsules_extra/
air_quality.rs1use core::cell::Cell;
27use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
28use kernel::hil;
29use kernel::syscall::{CommandReturn, SyscallDriver};
30use kernel::{ErrorCode, ProcessId};
31
32use capsules_core::driver;
34pub const DRIVER_NUM: usize = driver::NUM::AirQuality as usize;
35
36#[derive(Clone, Copy, PartialEq, Default)]
37enum Operation {
38 #[default]
39 None,
40 CO2,
41 TVOC,
42}
43
44#[derive(Default)]
45pub struct App {
46 operation: Operation,
47}
48
49pub struct AirQualitySensor<'a> {
50 driver: &'a dyn hil::sensors::AirQualityDriver<'a>,
51 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
52 busy: Cell<bool>,
53}
54
55impl<'a> AirQualitySensor<'a> {
56 pub fn new(
57 driver: &'a dyn hil::sensors::AirQualityDriver<'a>,
58 grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
59 ) -> AirQualitySensor<'a> {
60 AirQualitySensor {
61 driver,
62 apps: grant,
63 busy: Cell::new(false),
64 }
65 }
66
67 fn enqueue_command(&self, processid: ProcessId, op: Operation) -> CommandReturn {
68 self.apps
69 .enter(processid, |app, _| {
70 if !self.busy.get() {
71 self.busy.set(true);
72 app.operation = op;
73
74 let rcode = match op {
75 Operation::None => Err(ErrorCode::FAIL),
76 Operation::CO2 => self.driver.read_co2(),
77 Operation::TVOC => self.driver.read_tvoc(),
78 };
79 let eres = ErrorCode::try_from(rcode);
80
81 match eres {
82 Ok(ecode) => CommandReturn::failure(ecode),
83 _ => CommandReturn::success(),
84 }
85 } else {
86 CommandReturn::failure(ErrorCode::BUSY)
87 }
88 })
89 .unwrap_or_else(|err| CommandReturn::failure(err.into()))
90 }
91}
92
93impl hil::sensors::AirQualityClient for AirQualitySensor<'_> {
94 fn environment_specified(&self, _result: Result<(), ErrorCode>) {
95 unimplemented!();
96 }
97
98 fn co2_data_available(&self, value: Result<u32, ErrorCode>) {
99 for cntr in self.apps.iter() {
100 cntr.enter(|app, upcalls| {
101 if app.operation == Operation::CO2 {
102 let _ = value.map(|co2| {
103 self.busy.set(false);
104 app.operation = Operation::None;
105 let _ = upcalls.schedule_upcall(0, (co2 as usize, 0, 0));
106 });
107 }
108 });
109 }
110 }
111
112 fn tvoc_data_available(&self, value: Result<u32, ErrorCode>) {
113 for cntr in self.apps.iter() {
114 cntr.enter(|app, upcalls| {
115 if app.operation == Operation::TVOC {
116 let _ = value.map(|tvoc| {
117 self.busy.set(false);
118 app.operation = Operation::None;
119 let _ = upcalls.schedule_upcall(0, (tvoc as usize, 0, 0));
120 });
121 }
122 });
123 }
124 }
125}
126
127impl SyscallDriver for AirQualitySensor<'_> {
128 fn command(
129 &self,
130 command_num: usize,
131 _: usize,
132 _: usize,
133 processid: ProcessId,
134 ) -> CommandReturn {
135 match command_num {
136 0 => CommandReturn::success(),
138
139 1 => CommandReturn::failure(ErrorCode::NOSUPPORT),
141
142 2 => self.enqueue_command(processid, Operation::CO2),
144
145 3 => self.enqueue_command(processid, Operation::TVOC),
147
148 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
149 }
150 }
151
152 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
153 self.apps.enter(processid, |_, _| {})
154 }
155}