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 value
103 .map(|co2| {
104 self.busy.set(false);
105 app.operation = Operation::None;
106 upcalls.schedule_upcall(0, (co2 as usize, 0, 0)).ok();
107 })
108 .ok();
109 }
110 });
111 }
112 }
113
114 fn tvoc_data_available(&self, value: Result<u32, ErrorCode>) {
115 for cntr in self.apps.iter() {
116 cntr.enter(|app, upcalls| {
117 if app.operation == Operation::TVOC {
118 value
119 .map(|tvoc| {
120 self.busy.set(false);
121 app.operation = Operation::None;
122 upcalls.schedule_upcall(0, (tvoc as usize, 0, 0)).ok();
123 })
124 .ok();
125 }
126 });
127 }
128 }
129}
130
131impl SyscallDriver for AirQualitySensor<'_> {
132 fn command(
133 &self,
134 command_num: usize,
135 _: usize,
136 _: usize,
137 processid: ProcessId,
138 ) -> CommandReturn {
139 match command_num {
140 0 => CommandReturn::success(),
142
143 1 => CommandReturn::failure(ErrorCode::NOSUPPORT),
145
146 2 => self.enqueue_command(processid, Operation::CO2),
148
149 3 => self.enqueue_command(processid, Operation::TVOC),
151
152 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
153 }
154 }
155
156 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
157 self.apps.enter(processid, |_, _| {})
158 }
159}