1use core::cell::Cell;
22
23use enum_primitive::cast::FromPrimitive;
24use enum_primitive::enum_from_primitive;
25
26use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
27use kernel::hil::i2c;
28use kernel::hil::sensors;
29use kernel::syscall::{CommandReturn, SyscallDriver};
30use kernel::utilities::cells::{OptionalCell, TakeCell};
31use kernel::utilities::registers::register_bitfields;
32use kernel::{ErrorCode, ProcessId};
33
34use capsules_core::driver;
35
36pub const DRIVER_NUM: usize = driver::NUM::Mlx90614 as usize;
38
39register_bitfields![u16,
40 CONFIG [
41 IIR OFFSET(0) NUMBITS(3) [],
42 DUAL OFFSET(6) NUMBITS(1) [],
43 FIR OFFSET(8) NUMBITS(3) [],
44 GAIN OFFSET(11) NUMBITS(3) []
45 ]
46];
47
48#[derive(Clone, Copy, PartialEq, Debug)]
49enum State {
50 Idle,
51 IsPresent,
52 ReadAmbientTemp,
53 ReadObjTemp,
54}
55
56enum_from_primitive! {
57 enum Mlx90614Registers {
58 RAW1 = 0x04,
59 RAW2 = 0x05,
60 TA = 0x06,
61 TOBJ1 = 0x07,
62 TOBJ2 = 0x08,
63 EMISSIVITY = 0x24,
64 CONFIG = 0x25,
65 }
66}
67
68#[derive(Default)]
69pub struct App {}
70
71pub struct Mlx90614SMBus<'a, S: i2c::SMBusDevice> {
72 smbus_temp: &'a S,
73 temperature_client: OptionalCell<&'a dyn sensors::TemperatureClient>,
74 buffer: TakeCell<'static, [u8]>,
75 state: Cell<State>,
76 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
77 owning_process: OptionalCell<ProcessId>,
78}
79
80impl<'a, S: i2c::SMBusDevice> Mlx90614SMBus<'a, S> {
81 pub fn new(
82 smbus_temp: &'a S,
83 buffer: &'static mut [u8],
84 grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
85 ) -> Mlx90614SMBus<'a, S> {
86 Mlx90614SMBus {
87 smbus_temp,
88 temperature_client: OptionalCell::empty(),
89 buffer: TakeCell::new(buffer),
90 state: Cell::new(State::Idle),
91 apps: grant,
92 owning_process: OptionalCell::empty(),
93 }
94 }
95
96 fn is_present(&self) {
97 self.state.set(State::IsPresent);
98 self.buffer.take().map(|buf| {
99 buf[0] = Mlx90614Registers::RAW1 as u8;
101 self.smbus_temp.smbus_write_read(buf, 1, 1).unwrap();
102 });
103 }
104
105 fn read_ambient_temperature(&self) {
106 self.state.set(State::ReadAmbientTemp);
107 self.buffer.take().map(|buf| {
108 buf[0] = Mlx90614Registers::TA as u8;
109 self.smbus_temp.smbus_write_read(buf, 1, 1).unwrap();
110 });
111 }
112
113 fn read_object_temperature(&self) {
114 self.state.set(State::ReadObjTemp);
115 self.buffer.take().map(|buf| {
116 buf[0] = Mlx90614Registers::TOBJ1 as u8;
117 self.smbus_temp.smbus_write_read(buf, 1, 2).unwrap();
118 });
119 }
120}
121
122impl<S: i2c::SMBusDevice> i2c::I2CClient for Mlx90614SMBus<'_, S> {
123 fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
124 match self.state.get() {
125 State::Idle => {
126 self.buffer.replace(buffer);
127 }
128 State::IsPresent => {
129 let present = status.is_ok() && buffer[0] == 60;
130
131 self.owning_process.map(|pid| {
132 let _ = self.apps.enter(pid, |_app, upcalls| {
133 upcalls
134 .schedule_upcall(0, (usize::from(present), 0, 0))
135 .ok();
136 });
137 });
138 self.buffer.replace(buffer);
139 self.state.set(State::Idle);
140 }
141 State::ReadAmbientTemp | State::ReadObjTemp => {
142 let values = match status {
143 Ok(()) =>
144 {
146 Ok(((buffer[0] as usize | (buffer[1] as usize) << 8) * 2) as i32 - 27300)
147 }
148 Err(i2c_error) => Err(i2c_error.into()),
149 };
150 self.temperature_client.map(|client| {
151 client.callback(values);
152 });
153 if let Ok(temp) = values {
154 self.owning_process.map(|pid| {
155 let _ = self.apps.enter(pid, |_app, upcalls| {
156 upcalls.schedule_upcall(0, (temp as usize, 0, 0)).ok();
157 });
158 });
159 } else {
160 self.owning_process.map(|pid| {
161 let _ = self.apps.enter(pid, |_app, upcalls| {
162 upcalls.schedule_upcall(0, (0, 0, 0)).ok();
163 });
164 });
165 }
166 self.buffer.replace(buffer);
167 self.state.set(State::Idle);
168 }
169 }
170 }
171}
172
173impl<S: i2c::SMBusDevice> SyscallDriver for Mlx90614SMBus<'_, S> {
174 fn command(
175 &self,
176 command_num: usize,
177 _data1: usize,
178 _data2: usize,
179 process_id: ProcessId,
180 ) -> CommandReturn {
181 if command_num == 0 {
182 return CommandReturn::success();
185 }
186 let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
189 self.apps
190 .enter(current_process, |_, _| current_process == process_id)
191 .unwrap_or(true)
192 });
193 if match_or_empty_or_nonexistant {
194 self.owning_process.set(process_id);
195 } else {
196 return CommandReturn::failure(ErrorCode::NOMEM);
197 }
198
199 match command_num {
200 0 => CommandReturn::success(),
201 1 => {
203 if self.state.get() == State::Idle {
204 self.is_present();
205 CommandReturn::success()
206 } else {
207 CommandReturn::failure(ErrorCode::BUSY)
208 }
209 }
210 2 => {
212 if self.state.get() == State::Idle {
213 self.read_ambient_temperature();
214 CommandReturn::success()
215 } else {
216 CommandReturn::failure(ErrorCode::BUSY)
217 }
218 }
219 3 => {
221 if self.state.get() == State::Idle {
222 self.read_object_temperature();
223 CommandReturn::success()
224 } else {
225 CommandReturn::failure(ErrorCode::BUSY)
226 }
227 }
228 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
230 }
231 }
232
233 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
234 self.apps.enter(processid, |_, _| {})
235 }
236}
237
238impl<'a, S: i2c::SMBusDevice> sensors::TemperatureDriver<'a> for Mlx90614SMBus<'a, S> {
239 fn set_client(&self, temperature_client: &'a dyn sensors::TemperatureClient) {
240 self.temperature_client.replace(temperature_client);
241 }
242
243 fn read_temperature(&self) -> Result<(), ErrorCode> {
244 self.read_object_temperature();
245 Ok(())
246 }
247}