1use core::cell::Cell;
110
111use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
112use kernel::hil::sensors;
113use kernel::hil::spi;
114use kernel::syscall::{CommandReturn, SyscallDriver};
115use kernel::utilities::cells::{MapCell, OptionalCell};
116use kernel::utilities::leasable_buffer::SubSliceMut;
117use kernel::{ErrorCode, ProcessId};
118
119use capsules_core::driver;
120pub const DRIVER_NUM: usize = driver::NUM::L3gd20 as usize;
121
122const L3GD20_WHO_AM_I: u8 = 0xD4;
124
125const L3GD20_REG_WHO_AM_I: u8 = 0x0F;
127const L3GD20_REG_CTRL_REG1: u8 = 0x20;
128const L3GD20_REG_CTRL_REG2: u8 = 0x21;
129const L3GD20_REG_CTRL_REG4: u8 = 0x23;
131const L3GD20_REG_CTRL_REG5: u8 = 0x24;
132const L3GD20_REG_OUT_TEMP: u8 = 0x26;
134const L3GD20_REG_OUT_X_L: u8 = 0x28;
136pub const L3GD20_TX_SIZE: usize = 10;
158pub const L3GD20_RX_SIZE: usize = 10;
159
160pub const TX_BUF_LEN: usize = L3GD20_TX_SIZE;
161pub const RX_BUF_LEN: usize = L3GD20_RX_SIZE;
162
163const L3GD20_SCALE_250: isize = 875; const L3GD20_SCALE_500: isize = 1750; const L3GD20_SCALE_2000: isize = 7000; #[derive(Copy, Clone, PartialEq)]
169enum L3gd20Status {
170 Idle,
171 IsPresent,
172 PowerOn,
173 EnableHpf,
174 SetHpfParameters,
175 SetScale,
176 ReadXYZ,
177 ReadTemperature,
178}
179
180#[derive(Default)]
186pub struct App {}
187
188pub struct L3gd20Spi<'a, S: spi::SpiMasterDevice<'a>> {
189 spi: &'a S,
190 txbuffer: MapCell<SubSliceMut<'static, u8>>,
191 rxbuffer: MapCell<SubSliceMut<'static, u8>>,
192 status: Cell<L3gd20Status>,
193 hpf_enabled: Cell<bool>,
194 hpf_mode: Cell<u8>,
195 hpf_divider: Cell<u8>,
196 scale: Cell<u8>,
197 current_process: OptionalCell<ProcessId>,
198 grants: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
199 nine_dof_client: OptionalCell<&'a dyn sensors::NineDofClient>,
200 temperature_client: OptionalCell<&'a dyn sensors::TemperatureClient>,
201}
202
203impl<'a, S: spi::SpiMasterDevice<'a>> L3gd20Spi<'a, S> {
204 pub fn new(
205 spi: &'a S,
206 txbuffer: &'static mut [u8; L3GD20_TX_SIZE],
207 rxbuffer: &'static mut [u8; L3GD20_RX_SIZE],
208 grants: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
209 ) -> L3gd20Spi<'a, S> {
210 L3gd20Spi {
212 spi,
213 txbuffer: MapCell::new((&mut txbuffer[..]).into()),
214 rxbuffer: MapCell::new((&mut rxbuffer[..]).into()),
215 status: Cell::new(L3gd20Status::Idle),
216 hpf_enabled: Cell::new(false),
217 hpf_mode: Cell::new(0),
218 hpf_divider: Cell::new(0),
219 scale: Cell::new(0),
220 current_process: OptionalCell::empty(),
221 grants,
222 nine_dof_client: OptionalCell::empty(),
223 temperature_client: OptionalCell::empty(),
224 }
225 }
226
227 pub fn is_present(&self) -> bool {
228 self.status.set(L3gd20Status::IsPresent);
229 self.txbuffer.take().map(|mut buf| {
230 buf.reset();
231 buf[0] = L3GD20_REG_WHO_AM_I | 0x80;
232 buf[1] = 0x00;
233 buf.slice(..2);
234 let _ = self.spi.read_write_bytes(buf, self.rxbuffer.take());
236 });
237 false
238 }
239
240 pub fn power_on(&self) {
241 self.status.set(L3gd20Status::PowerOn);
242 self.txbuffer.take().map(|mut buf| {
243 buf.reset();
244 buf[0] = L3GD20_REG_CTRL_REG1;
245 buf[1] = 0x0F;
246 buf.slice(..2);
247 let _ = self.spi.read_write_bytes(buf, None);
249 });
250 }
251
252 fn enable_hpf(&self, enabled: bool) {
253 self.status.set(L3gd20Status::EnableHpf);
254 self.hpf_enabled.set(enabled);
255 self.txbuffer.take().map(|mut buf| {
256 buf.reset();
257 buf[0] = L3GD20_REG_CTRL_REG5;
258 buf[1] = u8::from(enabled) << 4;
259 buf.slice(..2);
260 let _ = self.spi.read_write_bytes(buf, None);
262 });
263 }
264
265 fn set_hpf_parameters(&self, mode: u8, divider: u8) {
266 self.status.set(L3gd20Status::SetHpfParameters);
267 self.hpf_mode.set(mode);
268 self.hpf_divider.set(divider);
269 self.txbuffer.take().map(|mut buf| {
270 buf.reset();
271 buf[0] = L3GD20_REG_CTRL_REG2;
272 buf[1] = (mode & 0x03) << 4 | (divider & 0x0F);
273 buf.slice(..2);
274 let _ = self.spi.read_write_bytes(buf, None);
276 });
277 }
278
279 fn set_scale(&self, scale: u8) {
280 self.status.set(L3gd20Status::SetScale);
281 self.scale.set(scale);
282 self.txbuffer.take().map(|mut buf| {
283 buf.reset();
284 buf[0] = L3GD20_REG_CTRL_REG4;
285 buf[1] = (scale & 0x03) << 4;
286 buf.slice(..2);
287 let _ = self.spi.read_write_bytes(buf, None);
289 });
290 }
291
292 fn read_xyz(&self) {
293 self.status.set(L3gd20Status::ReadXYZ);
294 self.txbuffer.take().map(|mut buf| {
295 buf.reset();
296 buf[0] = L3GD20_REG_OUT_X_L | 0x80 | 0x40;
297 buf[1] = 0x00;
298 buf[2] = 0x00;
299 buf[3] = 0x00;
300 buf[4] = 0x00;
301 buf[5] = 0x00;
302 buf[6] = 0x00;
303 buf.slice(..7);
304 let _ = self.spi.read_write_bytes(buf, self.rxbuffer.take());
306 });
307 }
308
309 fn read_temperature(&self) {
310 self.status.set(L3gd20Status::ReadTemperature);
311 self.txbuffer.take().map(|mut buf| {
312 buf.reset();
313 buf[0] = L3GD20_REG_OUT_TEMP | 0x80;
314 buf[1] = 0x00;
315 buf.slice(..2);
316 let _ = self.spi.read_write_bytes(buf, self.rxbuffer.take());
318 });
319 }
320
321 pub fn configure(&self) -> Result<(), ErrorCode> {
322 self.spi.configure(
323 spi::ClockPolarity::IdleHigh,
324 spi::ClockPhase::SampleTrailing,
325 1_000_000,
326 )
327 }
328}
329
330impl<'a, S: spi::SpiMasterDevice<'a>> SyscallDriver for L3gd20Spi<'a, S> {
331 fn command(
332 &self,
333 command_num: usize,
334 data1: usize,
335 data2: usize,
336 process_id: ProcessId,
337 ) -> CommandReturn {
338 if command_num == 0 {
339 return CommandReturn::success();
340 }
341
342 let match_or_empty_or_nonexistent = self.current_process.map_or(true, |current_process| {
343 self.grants
344 .enter(current_process, |_, _| current_process == process_id)
345 .unwrap_or(true)
346 });
347
348 if match_or_empty_or_nonexistent {
349 self.current_process.set(process_id);
350 } else {
351 return CommandReturn::failure(ErrorCode::RESERVE);
352 }
353
354 match command_num {
355 1 => {
357 if self.status.get() == L3gd20Status::Idle {
358 self.is_present();
359 CommandReturn::success()
360 } else {
361 CommandReturn::failure(ErrorCode::BUSY)
362 }
363 }
364 2 => {
366 if self.status.get() == L3gd20Status::Idle {
367 self.power_on();
368 CommandReturn::success()
369 } else {
370 CommandReturn::failure(ErrorCode::BUSY)
371 }
372 }
373 3 => {
375 if self.status.get() == L3gd20Status::Idle {
376 let scale = data1 as u8;
377 self.set_scale(scale);
378 CommandReturn::success()
379 } else {
380 CommandReturn::failure(ErrorCode::BUSY)
381 }
382 }
383 4 => {
385 if self.status.get() == L3gd20Status::Idle {
386 let mode = data1 as u8;
387 let divider = data2 as u8;
388 self.set_hpf_parameters(mode, divider);
389 CommandReturn::success()
390 } else {
391 CommandReturn::failure(ErrorCode::BUSY)
392 }
393 }
394 5 => {
396 if self.status.get() == L3gd20Status::Idle {
397 let enabled = data1 == 1;
398 self.enable_hpf(enabled);
399 CommandReturn::success()
400 } else {
401 CommandReturn::failure(ErrorCode::BUSY)
402 }
403 }
404 6 => {
406 if self.status.get() == L3gd20Status::Idle {
407 self.read_xyz();
408 CommandReturn::success()
409 } else {
410 CommandReturn::failure(ErrorCode::BUSY)
411 }
412 }
413 7 => {
415 if self.status.get() == L3gd20Status::Idle {
416 self.read_temperature();
417 CommandReturn::success()
418 } else {
419 CommandReturn::failure(ErrorCode::BUSY)
420 }
421 }
422 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
424 }
425 }
426
427 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
428 self.grants.enter(processid, |_, _| {})
429 }
430}
431
432impl<'a, S: spi::SpiMasterDevice<'a>> spi::SpiMasterClient for L3gd20Spi<'a, S> {
433 fn read_write_done(
434 &self,
435 write_buffer: SubSliceMut<'static, u8>,
436 read_buffer: Option<SubSliceMut<'static, u8>>,
437 status: Result<usize, ErrorCode>,
438 ) {
439 self.current_process.map(|proc_id| {
440 let _result = self.grants.enter(proc_id, |_app, upcalls| {
441 self.status.set(match self.status.get() {
442 L3gd20Status::IsPresent => {
443 let present = if let Some(ref buf) = read_buffer {
444 buf[1] == L3GD20_WHO_AM_I
445 } else {
446 false
447 };
448 upcalls
449 .schedule_upcall(0, (1, usize::from(present), 0))
450 .ok();
451 L3gd20Status::Idle
452 }
453
454 L3gd20Status::ReadXYZ => {
455 let mut x: usize = 0;
456 let mut y: usize = 0;
457 let mut z: usize = 0;
458 let values = if let Some(ref buf) = read_buffer {
459 if status.unwrap_or(0) >= 7 {
460 self.nine_dof_client.map(|client| {
461 let scale = match self.scale.get() {
463 0 => L3GD20_SCALE_250,
464 1 => L3GD20_SCALE_500,
465 _ => L3GD20_SCALE_2000,
466 };
467 let x: usize =
468 ((buf[1] as i16 | ((buf[2] as i16) << 8)) as isize * scale
469 / 100000)
470 as usize;
471 let y: usize =
472 ((buf[3] as i16 | ((buf[4] as i16) << 8)) as isize * scale
473 / 100000)
474 as usize;
475 let z: usize =
476 ((buf[5] as i16 | ((buf[6] as i16) << 8)) as isize * scale
477 / 100000)
478 as usize;
479 client.callback(x, y, z);
480 });
481 x = (buf[1] as i16 | ((buf[2] as i16) << 8)) as usize;
484 y = (buf[3] as i16 | ((buf[4] as i16) << 8)) as usize;
485 z = (buf[5] as i16 | ((buf[6] as i16) << 8)) as usize;
486 true
487 } else {
488 self.nine_dof_client.map(|client| {
489 client.callback(0, 0, 0);
490 });
491 false
492 }
493 } else {
494 false
495 };
496 if values {
497 upcalls.schedule_upcall(0, (x, y, z)).ok();
498 } else {
499 upcalls.schedule_upcall(0, (0, 0, 0)).ok();
500 }
501 L3gd20Status::Idle
502 }
503
504 L3gd20Status::ReadTemperature => {
505 let mut temperature = 0;
506 let value = if let Some(ref buf) = read_buffer {
507 if status.unwrap_or(0) >= 2 {
508 temperature = buf[1] as i32;
509 self.temperature_client.map(|client| {
510 client.callback(Ok(temperature * 100));
511 });
512 true
513 } else {
514 self.temperature_client.map(|client| {
515 client.callback(Err(ErrorCode::FAIL));
516 });
517 false
518 }
519 } else {
520 false
521 };
522 if value {
523 upcalls
524 .schedule_upcall(0, (temperature as usize, 0, 0))
525 .ok();
526 } else {
527 upcalls.schedule_upcall(0, (0, 0, 0)).ok();
528 }
529 L3gd20Status::Idle
530 }
531
532 _ => {
533 upcalls.schedule_upcall(0, (0, 0, 0)).ok();
534 L3gd20Status::Idle
535 }
536 });
537 });
538 });
539 self.txbuffer.replace(write_buffer);
540 if let Some(buf) = read_buffer {
541 self.rxbuffer.replace(buf);
542 }
543 }
544}
545
546impl<'a, S: spi::SpiMasterDevice<'a>> sensors::NineDof<'a> for L3gd20Spi<'a, S> {
547 fn set_client(&self, nine_dof_client: &'a dyn sensors::NineDofClient) {
548 self.nine_dof_client.replace(nine_dof_client);
549 }
550
551 fn read_gyroscope(&self) -> Result<(), ErrorCode> {
552 if self.status.get() == L3gd20Status::Idle {
553 self.read_xyz();
554 Ok(())
555 } else {
556 Err(ErrorCode::BUSY)
557 }
558 }
559}
560
561impl<'a, S: spi::SpiMasterDevice<'a>> sensors::TemperatureDriver<'a> for L3gd20Spi<'a, S> {
562 fn set_client(&self, temperature_client: &'a dyn sensors::TemperatureClient) {
563 self.temperature_client.replace(temperature_client);
564 }
565
566 fn read_temperature(&self) -> Result<(), ErrorCode> {
567 if self.status.get() == L3gd20Status::Idle {
568 self.read_temperature();
569 Ok(())
570 } else {
571 Err(ErrorCode::BUSY)
572 }
573 }
574}