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                    upcalls.schedule_upcall(0, (0, 0, 0)).ok();
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                            upcalls
212                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0))
213                                .ok();
214                        });
215                    });
216                } else {
217                    self.state.set(State::TakeMeasurementClear);
218                }
219            }
220            State::TakeMeasurementClear => {
221                if let Err((error, buffer)) = self.i2c.read(buffer, 3) {
222                    self.state.set(State::Idle);
223                    self.buffer.replace(buffer);
224                    self.owning_process.map(|pid| {
225                        let _ = self.apps.enter(pid, |_app, upcalls| {
226                            upcalls
227                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0))
228                                .ok();
229                        });
230                    });
231                } else {
232                    self.state.set(State::TakeMeasurementConfigure);
233                }
234            }
235            State::TakeMeasurementConfigure => {
236                buffer[0] = Registers::CtrlReg1 as u8 | REGISTER_AUTO_INCREMENT;
237                buffer[1] = CTRL_REG1_POWER_ON | CTRL_REG1_BLOCK_DATA_ENABLE;
238                buffer[2] = CTRL_REG2_ONE_SHOT;
239
240                if let Err((error, buffer)) = self.i2c.write(buffer, 3) {
241                    self.state.set(State::Idle);
242                    self.buffer.replace(buffer);
243                    self.owning_process.map(|pid| {
244                        let _ = self.apps.enter(pid, |_app, upcalls| {
245                            upcalls
246                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0))
247                                .ok();
248                        });
249                    });
250                } else {
251                    self.state.set(State::Done);
252                }
253            }
254            State::ReadMeasurement => {
255                if let Err((error, buffer)) = self.i2c.read(buffer, 3) {
256                    self.state.set(State::Idle);
257                    self.buffer.replace(buffer);
258                    self.owning_process.map(|pid| {
259                        let _ = self.apps.enter(pid, |_app, upcalls| {
260                            upcalls
261                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0))
262                                .ok();
263                        });
264                    });
265                } else {
266                    self.state.set(State::GotMeasurement);
267                }
268            }
269            State::GotMeasurement => {
270                let pressure =
271                    ((buffer[2] as u32) << 16) | ((buffer[1] as u32) << 8) | (buffer[0] as u32);
272
273                // Returned as microbars
274                let pressure_ubar = (pressure * 1000) / 4096;
275
276                self.owning_process.map(|pid| {
277                    let _ = self.apps.enter(pid, |_app, upcalls| {
278                        upcalls
279                            .schedule_upcall(0, (pressure_ubar as usize, 0, 0))
280                            .ok();
281                    });
282                });
283
284                buffer[0] = Registers::CtrlReg1 as u8;
285                buffer[1] = 0;
286
287                if let Err((error, buffer)) = self.i2c.write(buffer, 2) {
288                    self.state.set(State::Idle);
289                    self.buffer.replace(buffer);
290                    self.owning_process.map(|pid| {
291                        let _ = self.apps.enter(pid, |_app, upcalls| {
292                            upcalls
293                                .schedule_upcall(0, (into_statuscode(Err(error.into())), 0, 0))
294                                .ok();
295                        });
296                    });
297                } else {
298                    self.interrupt_pin.disable_interrupts();
299                    self.state.set(State::Done);
300                }
301            }
302            State::Done => {
303                self.buffer.replace(buffer);
304                self.i2c.disable();
305                self.state.set(State::Idle);
306            }
307            _ => {}
308        }
309    }
310}
311
312impl<I: i2c::I2CDevice> gpio::Client for LPS25HB<'_, I> {
313    fn fired(&self) {
314        self.buffer.take().map(|buf| {
315            // turn on i2c to send commands
316            self.i2c.enable();
317
318            // select sensor voltage register and read it
319            buf[0] = Registers::PressOutXl as u8 | REGISTER_AUTO_INCREMENT;
320
321            if let Err((_error, buf)) = self.i2c.write(buf, 1) {
322                self.buffer.replace(buf);
323                self.i2c.disable();
324            } else {
325                self.state.set(State::ReadMeasurement);
326            }
327        });
328    }
329}
330
331impl<I: i2c::I2CDevice> SyscallDriver for LPS25HB<'_, I> {
332    fn command(
333        &self,
334        command_num: usize,
335        _: usize,
336        _: usize,
337        process_id: ProcessId,
338    ) -> CommandReturn {
339        if command_num == 0 {
340            // Handle this first as it should be returned
341            // unconditionally
342            return CommandReturn::success();
343        }
344        // Check if this non-virtualized driver is already in use by
345        // some (alive) process
346        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
347            self.apps
348                .enter(current_process, |_, _| current_process == process_id)
349                .unwrap_or(true)
350        });
351        if match_or_empty_or_nonexistant {
352            self.owning_process.set(process_id);
353        } else {
354            return CommandReturn::failure(ErrorCode::NOMEM);
355        }
356        match command_num {
357            // Take a pressure measurement
358            1 => match self.take_measurement() {
359                Ok(()) => CommandReturn::success(),
360                Err(error) => CommandReturn::failure(error),
361            },
362            // default
363            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
364        }
365    }
366
367    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
368        self.apps.enter(processid, |_, _| {})
369    }
370}