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 let _ = upcalls.schedule_upcall(0, (usize::from(present), 0, 0));
134 });
135 });
136 self.buffer.replace(buffer);
137 self.state.set(State::Idle);
138 }
139 State::ReadAmbientTemp | State::ReadObjTemp => {
140 let values = match status {
141 Ok(()) =>
142 {
144 Ok(((buffer[0] as usize | (buffer[1] as usize) << 8) * 2) as i32 - 27300)
145 }
146 Err(i2c_error) => Err(i2c_error.into()),
147 };
148 self.temperature_client.map(|client| {
149 client.callback(values);
150 });
151 if let Ok(temp) = values {
152 self.owning_process.map(|pid| {
153 let _ = self.apps.enter(pid, |_app, upcalls| {
154 let _ = upcalls.schedule_upcall(0, (temp as usize, 0, 0));
155 });
156 });
157 } else {
158 self.owning_process.map(|pid| {
159 let _ = self.apps.enter(pid, |_app, upcalls| {
160 let _ = upcalls.schedule_upcall(0, (0, 0, 0));
161 });
162 });
163 }
164 self.buffer.replace(buffer);
165 self.state.set(State::Idle);
166 }
167 }
168 }
169}
170
171impl<S: i2c::SMBusDevice> SyscallDriver for Mlx90614SMBus<'_, S> {
172 fn command(
173 &self,
174 command_num: usize,
175 _data1: usize,
176 _data2: usize,
177 process_id: ProcessId,
178 ) -> CommandReturn {
179 if command_num == 0 {
180 return CommandReturn::success();
183 }
184 let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
187 self.apps
188 .enter(current_process, |_, _| current_process == process_id)
189 .unwrap_or(true)
190 });
191 if match_or_empty_or_nonexistant {
192 self.owning_process.set(process_id);
193 } else {
194 return CommandReturn::failure(ErrorCode::NOMEM);
195 }
196
197 match command_num {
198 0 => CommandReturn::success(),
199 1 => {
201 if self.state.get() == State::Idle {
202 self.is_present();
203 CommandReturn::success()
204 } else {
205 CommandReturn::failure(ErrorCode::BUSY)
206 }
207 }
208 2 => {
210 if self.state.get() == State::Idle {
211 self.read_ambient_temperature();
212 CommandReturn::success()
213 } else {
214 CommandReturn::failure(ErrorCode::BUSY)
215 }
216 }
217 3 => {
219 if self.state.get() == State::Idle {
220 self.read_object_temperature();
221 CommandReturn::success()
222 } else {
223 CommandReturn::failure(ErrorCode::BUSY)
224 }
225 }
226 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
228 }
229 }
230
231 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
232 self.apps.enter(processid, |_, _| {})
233 }
234}
235
236impl<'a, S: i2c::SMBusDevice> sensors::TemperatureDriver<'a> for Mlx90614SMBus<'a, S> {
237 fn set_client(&self, temperature_client: &'a dyn sensors::TemperatureClient) {
238 self.temperature_client.replace(temperature_client);
239 }
240
241 fn read_temperature(&self) -> Result<(), ErrorCode> {
242 self.read_object_temperature();
243 Ok(())
244 }
245}