drone-sd-core 0.2.2

Secure Digital cards driver for Drone.
use consts::NCR;
use core::mem;
use crc::{Crc, Crc16, Crc7};
use drone_core::drv::{Driver, Resource};
use drone_core::thr::prelude::*;
use errors::{CmdError, DmaError, R1Error, SpiInitError};
use futures::prelude::*;
use sd_spi::{SdSpi, SdSpiRes};
use tokens::{
  Cond, Csd, Ocr, ReadResponse, Status, WriteResponse, BLOCK_START,
  BLOCK_START_MULTI_WRITE, R1, STOP_TRAN,
};

const STARTUP_CYCLES: usize = 10;
const INIT_TIMEOUT: usize = 100;

static mut RX_FORGET: u8 = 0;
static TX_IDLE: u8 = 0xFF;

/// SD SPI session driver.
#[derive(Driver)]
#[driver(forward)]
pub struct SdSpiSessCore<T: SdSpiSessResCore>(T);

/// SD SPI session resource.
pub trait SdSpiSessResCore: Resource<Source = Self> + Driver {
  /// SD resource.
  type SdRes: SdSpiRes;

  /// SPI thread token.
  type SpiInt: ThrToken<Ltt>;

  /// Returns a reference to the SD driver.
  fn sd(&self) -> &SdSpi<Self::SdRes>;

  /// Returns a mutable reference to the SD driver.
  fn sd_mut(&mut self) -> &mut SdSpi<Self::SdRes>;

  /// Opens a SPI communication.
  fn spi_open(&self);

  /// Closes a SPI communication.
  fn spi_close(&self);

  /// Opens a two-way DMA communication.
  ///
  /// # Arguments
  ///
  /// * `rx_ptr` - A mutable pointer to the receiving byte array.
  /// * `rx_inc` - Whether to increment `rx_ptr` after each write.
  /// * `rx_len` - Number of bytes to receive.
  /// * `tx_ptr` - A pointer to the transmitting byte array.
  /// * `tx_inc` - Whether to increment `tx_ptr` after each read.
  /// * `tx_len` - Number of bytes to transmit.
  ///
  /// # Safety
  ///
  /// `rx_ptr` and `tx_ptr` must remain valid until the returned future
  /// completes.
  unsafe fn dma_exchange<'sess>(
    &'sess mut self,
    rx_ptr: *mut u8,
    rx_inc: bool,
    rx_len: usize,
    tx_ptr: *const u8,
    tx_inc: bool,
    tx_len: usize,
  ) -> Box<Future<Item = (), Error = DmaError> + 'sess>;

  /// Skips bytes for which `f` returns `None`. Returns `x` for the first
  /// `Some(x)`.
  fn wait_byte<'sess, R, F>(
    &'sess mut self,
    first_byte: u8,
    max_skip: usize,
    f: F,
  ) -> Box<Future<Item = R, Error = CmdError> + 'sess>
  where
    R: Send + 'static,
    F: FnMut(u8) -> Option<R> + Send + 'static;

  /// Sends a byte.
  fn send_byte(&mut self, value: u8);

  /// Returns a future, which resolves after 1 ms.
  fn startup_delay(&mut self) -> Box<Future<Item = (), Error = !>>;
}

impl<T: SdSpiSessResCore> SdSpiSessCore<T> {
  /// Waits 1 ms.
  #[inline(always)]
  pub fn startup_wait(&mut self) -> Box<Future<Item = (), Error = !>> {
    self.0.startup_delay()
  }

  /// Executes SPI mode initialization flow.
  pub fn init<'sess>(
    &'sess mut self,
    voltage: Ocr,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = Ocr, Error = SpiInitError> + 'sess {
    async(static move || {
      self.0.spi_open();
      await!(self.startup_cycles())?;
      self.0.sd().slave_select();
      await!(self.wait_until_ready(INIT_TIMEOUT))?;
      let r1 = await!(self.go_idle_state(buf))?;
      if !r1.in_idle_state() {
        return Err(SpiInitError::NotIdle);
      }
      await!(self.wait_until_ready(INIT_TIMEOUT))?;
      match await!(self.send_if_cond(0b0001, 0xAA, buf)) {
        Ok(cond) => {
          // Ver2.00 or later SD Memory Card
          if cond.is_err() {
            return Err(SpiInitError::CondMismatch);
          }
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          let (_r1, ocr) = await!(self.read_ocr(buf))?;
          if !ocr.supports(voltage) {
            return Err(SpiInitError::UnsupportedVoltage);
          }
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          await!(self.crc_on_off(true, buf))?;
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          while await!(self.sd_send_op_cond(true, buf))?.in_idle_state() {
            await!(self.wait_until_ready(INIT_TIMEOUT))?;
          }
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          let (_r1, ocr) = await!(self.read_ocr(buf))?;
          self.end();
          Ok(ocr)
        }
        Err(CmdError::R1(R1Error::Illegal)) => {
          // Ver1.X SD Memory Card or Not SD Memory Card
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          let (_r1, ocr) = await!(self.read_ocr(buf))?;
          if !ocr.supports(voltage) {
            return Err(SpiInitError::UnsupportedVoltage);
          }
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          await!(self.crc_on_off(true, buf))?;
          await!(self.wait_until_ready(INIT_TIMEOUT))?;
          while await!(self.sd_send_op_cond(false, buf))?.in_idle_state() {
            await!(self.wait_until_ready(INIT_TIMEOUT))?;
          }
          self.end();
          Ok(ocr)
        }
        Err(err) => Err(err)?,
      }
    })
  }

  /// Begins SPI session.
  pub fn begin(&self) {
    self.0.spi_open();
    self.0.sd().slave_select();
  }

  /// Ends SPI session.
  pub fn end(&self) {
    self.0.spi_close();
    self.0.sd().slave_unselect();
  }

  /// Resets the SD Memory Card.
  pub fn go_idle_state<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = R1, Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(0, 0x0000_0000, buf))?;
      Ok(await!(self.wait_r1())?)
    })
  }

  /// Sends SD Memory Card interface condition that includes host supply voltage
  /// information and asks the accessed card whether card can operate in
  /// supplied voltage range.
  pub fn send_if_cond<'sess>(
    &'sess mut self,
    voltage: u32,
    pattern: u32,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = Result<(R1, Cond), (R1, Cond)>, Error = CmdError> + 'sess
  {
    async(static move || {
      await!(self.send_cmd(8, (voltage << 8) | pattern, buf))?;
      let r1 = await!(self.wait_r1())?;
      r1.errck(true)?;
      await!(self.recv_bytes(&mut buf[..4]))?;
      let cond = Cond::from_buf(buf);
      if cond.voltage_accepted() == voltage && cond.check_pattern() == pattern {
        Ok(Ok((r1, cond)))
      } else {
        Ok(Err((r1, cond)))
      }
    })
  }

  /// Asks the selected card to send its card-specific data (CSD).
  pub fn send_csd<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    timeout: usize,
  ) -> impl Future<Item = (R1, Csd), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(9, 0x0000_0000, buf))?;
      let r1 = await!(self.wait_r1())?;
      r1.errck(true)?;
      await!(self.wait_read_response(timeout))?;
      await!(self.recv_bytes(&mut buf[..16 + 2]))?;
      let (data, crc) = buf.split_at(16);
      self.data_crc_verify(data, crc)?;
      Ok((r1, Csd::from_buf(buf)))
    })
  }

  /// Asks the selected card to send its status register.
  pub fn send_status<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = (R1, Status), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(13, 0x0000_0000, buf))?;
      Ok(await!(self.wait_r2())?)
    })
  }

  /// Reads the OCR register of a card.
  pub fn read_ocr<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = (R1, Ocr), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(58, 0x0000_0000, buf))?;
      let r1 = await!(self.wait_r1())?;
      r1.errck(true)?;
      await!(self.recv_bytes(&mut buf[..4]))?;
      Ok((r1, Ocr::from_buf(buf)))
    })
  }

  /// Sends host capacity support information and activates the card's
  /// initialization process.
  pub fn sd_send_op_cond<'sess>(
    &'sess mut self,
    hcs: bool,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = R1, Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_app_cmd(41, (hcs as u32) << 30, buf))?;
      Ok(await!(self.wait_r1())?)
    })
  }

  /// In case of SDSC Card, block length is set by this command.  In case of
  /// SDHC and SDXC Cards, block length of the memory access commands are fixed
  /// to 512 bytes.  The length of LOCK_UNLOCK command is set by this command
  /// regardless of card capacity.
  pub fn set_blocklen<'sess>(
    &'sess mut self,
    length: u32,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = R1, Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(16, length, buf))?;
      Ok(await!(self.wait_r1())?)
    })
  }

  /// Turns the CRC option on or off.
  pub fn crc_on_off<'sess>(
    &'sess mut self,
    on: bool,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = R1, Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(59, on as u32, buf))?;
      Ok(await!(self.wait_r1())?)
    })
  }

  /// Reads a block of data.
  pub fn read_block<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    data: &'sess mut [u8],
    address: u32,
    timeout: usize,
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(17, address, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      await!(self.wait_read_response(timeout))?;
      await!(self.recv_bytes(data))?;
      await!(self.recv_bytes(&mut buf[..2]))?;
      self.data_crc_verify(data, buf)?;
      Ok(())
    })
  }

  /// Reads multiple blocks of data.
  pub fn read_blocks<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    data: &'sess mut [u8],
    address: u32,
    block_length: usize,
    timeout: usize,
  ) -> impl Future<Item = R1, Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(18, address, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      for data in data.chunks_mut(block_length) {
        await!(self.wait_read_response(timeout))?;
        await!(self.recv_bytes(data))?;
        await!(self.recv_bytes(&mut buf[..2]))?;
        self.data_crc_verify(data, buf)?;
      }
      await!(self.send_cmd(12, 0x0000_0000, buf))?;
      Ok(await!(self.wait_r1())?)
    })
  }

  /// Writes a block of data.
  pub fn write_block<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    data: &'sess [u8],
    address: u32,
    timeout: usize,
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(24, address, buf))?;
      await!(self.wait_r1_and_until_ready())?.errck(false)?;
      await!(self.send_data_block(BLOCK_START, data))?.errck()?;
      await!(self.wait_until_ready(timeout))?;
      let (_r1, status) = await!(self.send_status(buf))?;
      status.errck()?;
      Ok(())
    })
  }

  /// Writes multiple blocks of data.
  pub fn write_blocks<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    data: &'sess [u8],
    address: u32,
    block_length: usize,
    timeout: usize,
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(25, address, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      let mut resp;
      for data in data.chunks(block_length) {
        await!(self.wait_until_ready(timeout))?;
        resp = await!(self.send_data_block(BLOCK_START_MULTI_WRITE, data))?;
        resp.errck()?;
      }
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_and_wait_until_ready(STOP_TRAN, timeout))?;
      let (_r1, status) = await!(self.send_status(buf))?;
      status.errck()?;
      Ok(())
    })
  }

  /// Erases multiple blocks of data.
  pub fn erase_blocks<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
    start: u32,
    end: u32,
    timeout: usize,
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.wait_until_ready(timeout))?;
      await!(self.send_cmd(32, start, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      await!(self.send_cmd(33, end, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      await!(self.send_cmd(33, 0x0000_0000, buf))?;
      await!(self.wait_r1())?.errck(false)?;
      Ok(())
    })
  }

  fn startup_cycles<'sess>(
    &'sess mut self,
  ) -> Box<Future<Item = (), Error = DmaError> + 'sess> {
    unsafe {
      self.0.dma_exchange(
        &mut RX_FORGET,
        false,
        STARTUP_CYCLES,
        &TX_IDLE,
        false,
        STARTUP_CYCLES,
      )
    }
  }

  fn send_cmd<'sess>(
    &'sess mut self,
    cmd: u8,
    mut arg: u32,
    buf: &'sess mut [u8],
  ) -> Box<Future<Item = (), Error = DmaError> + 'sess> {
    assert!(buf.len() >= 6);
    let (mut buf, mut crc) = (buf.as_mut_ptr(), Crc7::default());
    unsafe {
      let mut push = |byte| {
        *buf = byte;
        buf = buf.offset(1);
        crc.add_byte(byte);
      };
      push(cmd | 0x40);
      for _ in 0..4 {
        arg = arg.rotate_left(8);
        push(arg as u8);
      }
    }
    unsafe {
      *buf = crc.value() | 1;
      buf = buf.offset(-5);
      self.0.dma_exchange(&mut RX_FORGET, false, 6, buf, true, 6)
    }
  }

  fn send_app_cmd<'sess>(
    &'sess mut self,
    cmd: u8,
    arg: u32,
    buf: &'sess mut [u8],
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.send_cmd(55, 0x0000_0000, buf))?;
      await!(self.wait_r1_and_until_ready())?.errck(false)?;
      await!(self.send_cmd(cmd, arg, buf))?;
      Ok(())
    })
  }

  fn send_and_wait_until_ready<'sess>(
    &'sess mut self,
    first_byte: u8,
    max_skip: usize,
  ) -> Box<Future<Item = (), Error = CmdError> + 'sess> {
    self.0.wait_byte(first_byte, max_skip, |byte| {
      if byte == 0xFF {
        Some(())
      } else {
        None
      }
    })
  }

  fn wait_until_ready<'sess>(
    &'sess mut self,
    max_skip: usize,
  ) -> Box<Future<Item = (), Error = CmdError> + 'sess> {
    self.send_and_wait_until_ready(0xFF, max_skip)
  }

  fn wait_r1<'sess>(
    &'sess mut self,
  ) -> Box<Future<Item = R1, Error = CmdError> + 'sess> {
    self.0.wait_byte(0xFF, NCR, R1::try_from)
  }

  fn wait_r1_and_until_ready<'sess>(
    &'sess mut self,
  ) -> Box<Future<Item = R1, Error = CmdError> + 'sess> {
    let mut r1 = None;
    self.0.wait_byte(0xFF, NCR, move |byte| {
      if r1.is_none() {
        r1 = R1::try_from(byte);
      } else if byte == 0xFF {
        return r1;
      }
      None
    })
  }

  fn wait_r2<'sess>(
    &'sess mut self,
  ) -> impl Future<Item = (R1, Status), Error = CmdError> + 'sess {
    let mut r1 = None;
    let resp = self.0.wait_byte(0xFF, NCR, move |byte| match r1 {
      None => {
        r1 = R1::try_from(byte);
        if let Some(r1) = r1 {
          if let Err(err) = r1.errck(true) {
            return Some(Err(err));
          }
        }
        None
      }
      Some(r1) => Some(Ok((r1, Status::new(byte)))),
    });
    async(static move || Ok(await!(resp)??))
  }

  fn wait_read_response<'sess>(
    &'sess mut self,
    max_skip: usize,
  ) -> impl Future<Item = (), Error = CmdError> + 'sess {
    async(static move || {
      await!(self.0.wait_byte(0xFF, max_skip, ReadResponse::try_from))?
        .errck()?;
      Ok(())
    })
  }

  fn recv_bytes<'sess>(
    &'sess mut self,
    buf: &'sess mut [u8],
  ) -> Box<Future<Item = (), Error = DmaError> + 'sess> {
    unsafe {
      self.0.dma_exchange(
        buf.as_mut_ptr(),
        true,
        buf.len(),
        &TX_IDLE,
        false,
        buf.len(),
      )
    }
  }

  fn send_data_block<'sess>(
    &'sess mut self,
    token: u8,
    data: &'sess [u8],
  ) -> impl Future<Item = WriteResponse, Error = DmaError> + 'sess {
    async(static move || {
      self.0.send_byte(token);
      unsafe {
        await!(self.0.dma_exchange(
          &mut RX_FORGET,
          false,
          data.len() + 1,
          data.as_ptr(),
          true,
          data.len(),
        ))?;
      }
      let crc = Crc16::from_bytes(data).value();
      let mut resp: WriteResponse = unsafe { mem::uninitialized() };
      let buf: [u8; 3] = [((crc & 0xFF_00) >> 8) as u8, crc as u8, 0xFF];
      unsafe {
        await!(self.0.dma_exchange(
          &mut resp as *mut _ as *mut u8,
          false,
          3,
          buf.as_ptr(),
          true,
          3
        ))?;
      }
      Ok(resp)
    })
  }

  #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
  #[inline(always)]
  fn data_crc_verify(&self, data: &[u8], crc: &[u8]) -> Result<(), CmdError> {
    assert!(crc.len() >= 2);
    let check_crc = unsafe { u16::from_be(*(crc.as_ptr() as *const u16)) };
    if Crc16::from_bytes(data).value() == check_crc {
      Ok(())
    } else {
      Err(CmdError::DataCrc)
    }
  }
}