1use core::cell::Cell;
16use kernel::deferred_call::{DeferredCall, DeferredCallClient};
17use kernel::hil::i2c::{self, I2CClient, I2CDevice};
18use kernel::hil::sensors::{AirQualityClient, AirQualityDriver};
19use kernel::utilities::cells::{OptionalCell, TakeCell};
20use kernel::ErrorCode;
21
22const STATUS: u8 = 0x00;
23const MEAS_MODE: u8 = 0x01;
24const ALG_RESULT_DATA: u8 = 0x02;
25#[allow(dead_code)]
26const RAW_DATA: u8 = 0x03;
27#[allow(dead_code)]
28const ENV_DATA: u8 = 0x05;
29#[allow(dead_code)]
30const NTC: u8 = 0x06;
31#[allow(dead_code)]
32const THRESHOLDS: u8 = 0x10;
33#[allow(dead_code)]
34const BASELINE: u8 = 0x11;
35const HW_ID: u8 = 0x20;
36#[allow(dead_code)]
37const HW_VERSION: u8 = 0x21;
38#[allow(dead_code)]
39const FW_BOOT_VERSION: u8 = 0x23;
40#[allow(dead_code)]
41const FW_APP_VERSION: u8 = 0x24;
42#[allow(dead_code)]
43const ERROR_ID: u8 = 0xE0;
44const APP_START: u8 = 0xF4;
45const SW_RESET: u8 = 0xFF;
46
47#[derive(Clone, Copy, PartialEq)]
48enum DeviceState {
49 Identify,
50 Reset,
51 StatusCheck,
52 StartApp,
53 Normal,
54}
55
56#[allow(dead_code)]
57#[derive(Clone, Copy, PartialEq)]
58enum Operation {
59 None,
60 Setup,
61 SetEnv,
62 CO2,
63 TVOC,
64}
65
66pub struct Ccs811<'a> {
67 buffer: TakeCell<'static, [u8]>,
68 i2c: &'a dyn I2CDevice,
69 client: OptionalCell<&'a dyn AirQualityClient>,
70 state: Cell<DeviceState>,
71 op: Cell<Operation>,
72
73 deferred_call: DeferredCall,
75 deferred_count: Cell<usize>,
76}
77
78impl<'a> Ccs811<'a> {
79 pub fn new(i2c: &'a dyn I2CDevice, buffer: &'static mut [u8]) -> Self {
80 Self {
81 buffer: TakeCell::new(buffer),
82 i2c,
83 client: OptionalCell::empty(),
84 state: Cell::new(DeviceState::Identify),
85 op: Cell::new(Operation::Setup),
86 deferred_call: DeferredCall::new(),
87 deferred_count: Cell::new(0),
88 }
89 }
90
91 pub fn startup(&self) {
92 self.buffer.take().map(|buffer| {
93 if self.state.get() == DeviceState::Identify {
94 buffer[0] = HW_ID;
96 self.i2c.write_read(buffer, 1, 1).unwrap();
97 }
98 });
99 }
100}
101
102impl<'a> AirQualityDriver<'a> for Ccs811<'a> {
103 fn set_client(&self, client: &'a dyn AirQualityClient) {
104 self.client.set(client);
105 }
106
107 fn specify_environment(
108 &self,
109 temp: Option<i32>,
110 humidity: Option<u32>,
111 ) -> Result<(), ErrorCode> {
112 if self.state.get() != DeviceState::Normal {
113 return Err(ErrorCode::BUSY);
114 }
115
116 if self.op.get() != Operation::None {
117 return Err(ErrorCode::BUSY);
118 }
119
120 self.buffer.take().map(|buffer| {
121 buffer[0] = 0x05;
123 buffer[1] = 0x64;
124 buffer[2] = 0x00;
125 buffer[3] = 0x64;
126 buffer[4] = 0x00;
127
128 if let Some(hum) = humidity {
130 buffer[1] = hum as u8 * 2;
131 }
132 if let Some(t) = temp {
133 if t < -25 {
134 buffer[3] = 0;
135 } else {
136 buffer[3] = (t as u8 + 25) * 2;
137 }
138 }
139
140 self.op.set(Operation::SetEnv);
141 self.i2c.write(buffer, 5).unwrap();
142 });
143
144 Ok(())
145 }
146
147 fn read_co2(&self) -> Result<(), ErrorCode> {
148 if self.state.get() != DeviceState::Normal {
149 return Err(ErrorCode::BUSY);
150 }
151
152 if self.op.get() != Operation::None {
153 return Err(ErrorCode::BUSY);
154 }
155
156 self.buffer.take().map(|buffer| {
157 buffer[0] = ALG_RESULT_DATA;
158
159 self.op.set(Operation::CO2);
160 self.i2c.write_read(buffer, 1, 6).unwrap();
161 });
162
163 Ok(())
164 }
165
166 fn read_tvoc(&self) -> Result<(), ErrorCode> {
167 if self.state.get() != DeviceState::Normal {
168 return Err(ErrorCode::BUSY);
169 }
170
171 if self.op.get() != Operation::None {
172 return Err(ErrorCode::BUSY);
173 }
174
175 self.buffer.take().map(|buffer| {
176 buffer[0] = ALG_RESULT_DATA;
177
178 self.op.set(Operation::TVOC);
179 self.i2c.write_read(buffer, 1, 6).unwrap();
180 });
181
182 Ok(())
183 }
184}
185
186impl I2CClient for Ccs811<'_> {
187 fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
188 if status.is_err() {
189 match self.op.get() {
190 Operation::None | Operation::Setup => (),
191 Operation::SetEnv => {
192 self.client
193 .map(|client| client.environment_specified(Err(ErrorCode::FAIL)));
194 }
195 Operation::CO2 => {
196 self.client
197 .map(|client| client.co2_data_available(Err(ErrorCode::FAIL)));
198 }
199 Operation::TVOC => {
200 self.client
201 .map(|client| client.tvoc_data_available(Err(ErrorCode::FAIL)));
202 }
203 }
204 self.buffer.replace(buffer);
205 self.op.set(Operation::None);
206 return;
207 }
208
209 match self.state.get() {
210 DeviceState::Identify => {
211 if buffer[0] != 0x81 {
212 self.buffer.replace(buffer);
215 return;
216 }
217
218 buffer[0] = SW_RESET;
219 buffer[1] = 0x11;
220 buffer[2] = 0xE5;
221 buffer[3] = 0x72;
222 buffer[4] = 0x8A;
223 self.i2c.write(buffer, 5).unwrap();
224 self.state.set(DeviceState::Reset);
225 }
226 DeviceState::Reset => {
227 self.deferred_call.set();
228 self.buffer.replace(buffer);
229 }
230 DeviceState::StatusCheck => {
231 if buffer[0] & 0x01 == 0x01 {
232 self.buffer.replace(buffer);
233 return;
234 }
235
236 if buffer[0] & 0x04 == 0x04 {
237 self.buffer.replace(buffer);
238 return;
239 }
240
241 buffer[0] = APP_START;
242 self.i2c.write(buffer, 1).unwrap();
243 self.state.set(DeviceState::StartApp);
244 }
245 DeviceState::StartApp => {
246 buffer[0] = MEAS_MODE;
247 buffer[1] = (1 << 4) | (0 << 3) | (0 << 2);
251 self.i2c.write(buffer, 2).unwrap();
252
253 self.state.set(DeviceState::Normal);
254 }
255 DeviceState::Normal => {
256 match self.op.get() {
257 Operation::None => (),
258 Operation::Setup => {
259 self.buffer.replace(buffer);
260 self.deferred_call.set();
261 return;
262 }
263 Operation::SetEnv => {
264 self.client
265 .map(|client| client.environment_specified(Ok(())));
266 }
267 Operation::CO2 => {
268 let co2 = (buffer[0] as u32) << 8 | buffer[1] as u32;
269 let status = buffer[4];
270 let _error_id = buffer[5];
271
272 if status & 0x01 == 0x01 {
273 self.client
274 .map(|client| client.co2_data_available(Err(ErrorCode::FAIL)));
275 }
276
277 self.client.map(|client| client.co2_data_available(Ok(co2)));
278 }
279 Operation::TVOC => {
280 let tvoc = (buffer[2] as u32) << 8 | buffer[3] as u32;
281 let status = buffer[4];
282 let _error_id = buffer[5];
283
284 if status & 0x01 == 0x01 {
285 self.client
286 .map(|client| client.tvoc_data_available(Err(ErrorCode::FAIL)));
287 }
288
289 self.client
290 .map(|client| client.tvoc_data_available(Ok(tvoc)));
291 }
292 }
293 self.buffer.replace(buffer);
294 self.op.set(Operation::None);
295 }
296 }
297 }
298}
299
300impl DeferredCallClient for Ccs811<'_> {
301 fn handle_deferred_call(&self) {
302 if self.deferred_count.get() > 1000 {
303 match self.state.get() {
304 DeviceState::Reset => {
305 self.buffer.take().map(|buffer| {
306 buffer[0] = STATUS;
307 self.i2c.write_read(buffer, 1, 1).unwrap();
308
309 self.state.set(DeviceState::StatusCheck);
310 });
311 }
312 DeviceState::Normal => {
313 self.op.set(Operation::None);
314 }
315 _ => unreachable!(),
316 }
317 } else {
318 self.deferred_count.set(self.deferred_count.get() + 1);
319 self.deferred_call.set();
320 }
321 }
322
323 fn register(&'static self) {
324 self.deferred_call.register(self);
325 }
326}