capsules_extra/
lps25hb.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//! SyscallDriver for the ST LPS25HB pressure sensor.
6//!
7//! <http://www.st.com/en/mems-and-sensors/lps25hb.html>
8//!
9//! Usage
10//! -----
11//!
12//! ```rust,ignore
13//! let buffer = static_init!([u8; capsules::lps25hb::BUF_LEN], [0; capsules::lps25hb::BUF_LEN]);
14//! let lps25hb_i2c = static_init!(I2CDevice, I2CDevice::new(i2c_bus, 0x5C));
15//! let lps25hb = static_init!(
16//!     capsules::lps25hb::LPS25HB<'static>,
17//!     capsules::lps25hb::LPS25HB::new(lps25hb_i2c,
18//!         &sam4l::gpio::PA[10],
19//!         buffer));
20//! lps25hb_i2c.set_client(lps25hb);
21//! sam4l::gpio::PA[10].set_client(lps25hb);
22//! ```
23
24use core::cell::Cell;
25
26use kernel::errorcode::into_statuscode;
27use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
28use kernel::hil::gpio;
29use kernel::hil::i2c;
30use kernel::syscall::{CommandReturn, SyscallDriver};
31use kernel::utilities::cells::{OptionalCell, TakeCell};
32use kernel::{ErrorCode, ProcessId};
33
34/// Syscall driver number.
35use capsules_core::driver;
36pub const DRIVER_NUM: usize = driver::NUM::Lps25hb as usize;
37
38// Expected buffer length.
39pub const BUF_LEN: usize = 5;
40
41/// Register values
42const REGISTER_AUTO_INCREMENT: u8 = 0x80;
43
44const CTRL_REG1_POWER_ON: u8 = 0x80;
45const CTRL_REG1_BLOCK_DATA_ENABLE: u8 = 0x04;
46const CTRL_REG2_ONE_SHOT: u8 = 0x01;
47const CTRL_REG4_INTERRUPT1_DATAREADY: u8 = 0x01;
48
49#[allow(dead_code)]
50enum Registers {
51    RefPXl = 0x08,
52    RefPL = 0x09,
53    RefPH = 0x0a,
54    WhoAmI = 0x0f,
55    ResConf = 0x10,
56    CtrlReg1 = 0x20,
57    CtrlReg2 = 0x21,
58    CtrlReg3 = 0x22,
59    CtrlReg4 = 0x23,
60    IntCfgReg = 0x24,
61    IntSourceReg = 0x25,
62    StatusReg = 0x27,
63    PressOutXl = 0x28,
64    PressOutL = 0x29,
65    PressOutH = 0x2a,
66    TempOutL = 0x2b,
67    TempOutH = 0x2c,
68    FifoCtrl = 0x2e,
69    FifoStatus = 0x2f,
70    ThsPL = 0x30,
71    ThsPH = 0x31,
72    RpdsL = 0x39,
73    RpdsH = 0x3a,
74}
75
76/// States of the I2C protocol with the LPS25HB.
77#[derive(Clone, Copy, PartialEq)]
78enum State {
79    Idle,
80
81    /// Read the WHO_AM_I register. This should return 0xBB.
82    SelectWhoAmI,
83    ReadingWhoAmI,
84
85    /// Process of taking a pressure measurement.
86    /// Start with chip powered off
87    TakeMeasurementInit,
88    /// Then clear the current reading (just in case it exists)
89    /// to reset the interrupt line.
90    TakeMeasurementClear,
91    /// Enable a single shot measurement with interrupt when data is ready.
92    TakeMeasurementConfigure,
93
94    /// Read the 3 pressure registers.
95    ReadMeasurement,
96    /// Calculate pressure and call the callback with the value.
97    GotMeasurement,
98
99    /// Disable I2C and release buffer
100    Done,
101}
102
103#[derive(Default)]
104pub struct App {}
105
106pub struct LPS25HB<'a, I: i2c::I2CDevice> {
107    i2c: &'a I,
108    interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
109    state: Cell<State>,
110    buffer: TakeCell<'static, [u8]>,
111    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
112    owning_process: OptionalCell<ProcessId>,
113}
114
115impl<'a, I: i2c::I2CDevice> LPS25HB<'a, I> {
116    pub fn new(
117        i2c: &'a I,
118        interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
119        buffer: &'static mut [u8],
120        apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
121    ) -> Self {
122        // setup and return struct
123        Self {
124            i2c,
125            interrupt_pin,
126            state: Cell::new(State::Idle),
127            buffer: TakeCell::new(buffer),
128            apps,
129            owning_process: OptionalCell::empty(),
130        }
131    }
132
133    pub fn read_whoami(&self) -> Result<(), ErrorCode> {
134        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buf| {
135            // turn on i2c to send commands
136            self.i2c.enable();
137
138            buf[0] = Registers::WhoAmI as u8;
139
140            if let Err((_error, buf)) = self.i2c.write(buf, 1) {
141                self.buffer.replace(buf);
142                self.i2c.disable();
143                Err(_error.into())
144            } else {
145                self.state.set(State::SelectWhoAmI);
146                Ok(())
147            }
148        })
149    }
150
151    pub fn take_measurement(&self) -> Result<(), ErrorCode> {
152        self.interrupt_pin.make_input();
153        self.interrupt_pin
154            .enable_interrupts(gpio::InterruptEdge::RisingEdge);
155
156        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buf| {
157            // turn on i2c to send commands
158            self.i2c.enable();
159
160            buf[0] = Registers::CtrlReg1 as u8 | REGISTER_AUTO_INCREMENT;
161            buf[1] = 0;
162            buf[2] = 0;
163            buf[3] = 0;
164            buf[4] = CTRL_REG4_INTERRUPT1_DATAREADY;
165
166            if let Err((_error, buf)) = self.i2c.write(buf, 5) {
167                self.buffer.replace(buf);
168                self.i2c.disable();
169                Err(_error.into())
170            } else {
171                self.state.set(State::TakeMeasurementInit);
172                Ok(())
173            }
174        })
175    }
176}
177
178impl<I: i2c::I2CDevice> i2c::I2CClient for LPS25HB<'_, I> {
179    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
180        if status != Ok(()) {
181            self.state.set(State::Idle);
182            self.buffer.replace(buffer);
183            self.owning_process.map(|pid| {
184                let _ = self.apps.enter(pid, |_app, upcalls| {
185                    let _ = upcalls.schedule_upcall(0, (0, 0, 0));
186                });
187            });
188            return;
189        }
190        match self.state.get() {
191            State::SelectWhoAmI => {
192                if let Err((_error, buffer)) = self.i2c.read(buffer, 1) {
193                    self.state.set(State::Idle);
194                    self.buffer.replace(buffer);
195                } else {
196                    self.state.set(State::ReadingWhoAmI);
197                }
198            }
199            State::ReadingWhoAmI => {
200                self.buffer.replace(buffer);
201                self.i2c.disable();
202                self.state.set(State::Idle);
203            }
204            State::TakeMeasurementInit => {
205                buffer[0] = Registers::PressOutXl as u8 | REGISTER_AUTO_INCREMENT;
206                if let Err((error, buffer)) = self.i2c.write(buffer, 1) {
207                    self.state.set(State::Idle);
208                    self.buffer.replace(buffer);
209                    self.owning_process.map(|pid| {
210                        let _ = self.apps.enter(pid, |_app, upcalls| {
211                            let _ = upcalls
212                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0));
213                        });
214                    });
215                } else {
216                    self.state.set(State::TakeMeasurementClear);
217                }
218            }
219            State::TakeMeasurementClear => {
220                if let Err((error, buffer)) = self.i2c.read(buffer, 3) {
221                    self.state.set(State::Idle);
222                    self.buffer.replace(buffer);
223                    self.owning_process.map(|pid| {
224                        let _ = self.apps.enter(pid, |_app, upcalls| {
225                            let _ = upcalls
226                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0));
227                        });
228                    });
229                } else {
230                    self.state.set(State::TakeMeasurementConfigure);
231                }
232            }
233            State::TakeMeasurementConfigure => {
234                buffer[0] = Registers::CtrlReg1 as u8 | REGISTER_AUTO_INCREMENT;
235                buffer[1] = CTRL_REG1_POWER_ON | CTRL_REG1_BLOCK_DATA_ENABLE;
236                buffer[2] = CTRL_REG2_ONE_SHOT;
237
238                if let Err((error, buffer)) = self.i2c.write(buffer, 3) {
239                    self.state.set(State::Idle);
240                    self.buffer.replace(buffer);
241                    self.owning_process.map(|pid| {
242                        let _ = self.apps.enter(pid, |_app, upcalls| {
243                            let _ = upcalls
244                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0));
245                        });
246                    });
247                } else {
248                    self.state.set(State::Done);
249                }
250            }
251            State::ReadMeasurement => {
252                if let Err((error, buffer)) = self.i2c.read(buffer, 3) {
253                    self.state.set(State::Idle);
254                    self.buffer.replace(buffer);
255                    self.owning_process.map(|pid| {
256                        let _ = self.apps.enter(pid, |_app, upcalls| {
257                            let _ = upcalls
258                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0));
259                        });
260                    });
261                } else {
262                    self.state.set(State::GotMeasurement);
263                }
264            }
265            State::GotMeasurement => {
266                let pressure =
267                    ((buffer[2] as u32) << 16) | ((buffer[1] as u32) << 8) | (buffer[0] as u32);
268
269                // Returned as microbars
270                let pressure_ubar = (pressure * 1000) / 4096;
271
272                self.owning_process.map(|pid| {
273                    let _ = self.apps.enter(pid, |_app, upcalls| {
274                        let _ = upcalls.schedule_upcall(0, (pressure_ubar as usize, 0, 0));
275                    });
276                });
277
278                buffer[0] = Registers::CtrlReg1 as u8;
279                buffer[1] = 0;
280
281                if let Err((error, buffer)) = self.i2c.write(buffer, 2) {
282                    self.state.set(State::Idle);
283                    self.buffer.replace(buffer);
284                    self.owning_process.map(|pid| {
285                        let _ = self.apps.enter(pid, |_app, upcalls| {
286                            let _ = upcalls
287                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0));
288                        });
289                    });
290                } else {
291                    self.interrupt_pin.disable_interrupts();
292                    self.state.set(State::Done);
293                }
294            }
295            State::Done => {
296                self.buffer.replace(buffer);
297                self.i2c.disable();
298                self.state.set(State::Idle);
299            }
300            _ => {}
301        }
302    }
303}
304
305impl<I: i2c::I2CDevice> gpio::Client for LPS25HB<'_, I> {
306    fn fired(&self) {
307        self.buffer.take().map(|buf| {
308            // turn on i2c to send commands
309            self.i2c.enable();
310
311            // select sensor voltage register and read it
312            buf[0] = Registers::PressOutXl as u8 | REGISTER_AUTO_INCREMENT;
313
314            if let Err((_error, buf)) = self.i2c.write(buf, 1) {
315                self.buffer.replace(buf);
316                self.i2c.disable();
317            } else {
318                self.state.set(State::ReadMeasurement);
319            }
320        });
321    }
322}
323
324impl<I: i2c::I2CDevice> SyscallDriver for LPS25HB<'_, I> {
325    fn command(
326        &self,
327        command_num: usize,
328        _: usize,
329        _: usize,
330        process_id: ProcessId,
331    ) -> CommandReturn {
332        if command_num == 0 {
333            // Handle this first as it should be returned
334            // unconditionally
335            return CommandReturn::success();
336        }
337        // Check if this non-virtualized driver is already in use by
338        // some (alive) process
339        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
340            self.apps
341                .enter(current_process, |_, _| current_process == process_id)
342                .unwrap_or(true)
343        });
344        if match_or_empty_or_nonexistant {
345            self.owning_process.set(process_id);
346        } else {
347            return CommandReturn::failure(ErrorCode::NOMEM);
348        }
349        match command_num {
350            // Take a pressure measurement
351            1 => match self.take_measurement() {
352                Ok(()) => CommandReturn::success(),
353                Err(error) => CommandReturn::failure(error),
354            },
355            // default
356            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
357        }
358    }
359
360    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
361        self.apps.enter(processid, |_, _| {})
362    }
363}