drone-sd-core 0.2.2

Secure Digital cards driver for Drone.
//! Cyclic Redundancy Code (CRC).
//!
//! The CRC is intended to protect SD Memory Card commands, responses, and data
//! transfer against transmission errors on the SD Memory Card bus. One CRC is
//! generated for every command and checked for every response on the CMD line.
//! For data blocks, one CRC per transferred block is generated.

/// The CRC7 check is used for all commands, for all responses except type R3,
/// and for the CSD and CID registers. The CRC7 is a 7-bit value and is computed
/// as follows:
///
/// * Generator polynomial: G(x) = x<sup>7</sup> + x<sup>3</sup> + 1
/// * M(x) = (first bit) * x<sup>n</sup> + (second bit) * x<sup>n-1</sup> +
///   ...  + (last bit) * x<sup>0</sup>
/// * CRC[6...0] = Remainder [(M(x) * x<sup>7</sup>)/G(x)]
///
/// The first bit is the most left bit of the corresponding bit string (of the
/// command, response, CID or CSD).  The degree n of the polynomial is the
/// number of CRC protected bits decreased by one. The number of bits to be
/// protected is 40 for commands and responses (n = 39), and 120 for the CSD and
/// CID (n = 119).
#[derive(Default)]
pub struct Crc7(u8);

/// In the case of one DAT line usage, the CRC16 is used for payload protection
/// in block transfer mode. The CRC check sum is a 16-bit value and is computed
/// as follows:
///
/// * Generator polynomial G(x) = x<sup>16</sup> + x<sup>12</sup> +
///   x<sup>5</sup> + 1
/// * M(x) = (first bit) * x<sup>n</sup> + (second bit)* x<sup>n-1</sup> + ... +
///   (last bit) * x<sup>0</sup>
/// * CRC[15...0] = Remainder [(M(x) * x<sup>16</sup>)/G(x)]
///
/// The first bit is the first data bit of the corresponding block. The degree n
/// of the polynomial denotes the number of bits of the data block decreased by
/// one (e.g. n = 4095 for a block length of 512 bytes). The generator
/// polynomial G(x) is a standard CCITT polynomial. The code has a minimal
/// distance d = 4 and is used for a payload length of up to 2048 Bytes (n <=
/// 16383).
///
/// The same CRC16 method shall be used in single DAT line mode and in wide bus
/// mode. In wide bus mode, the CRC16 is done on each line separately.
#[derive(Default)]
pub struct Crc16(u16);

/// Generic CRC.
pub trait Crc: Default {
  /// CRC output.
  type Output;

  /// Adds a byte to the CRC.
  fn add_byte(&mut self, byte: u8);

  /// Returns the output value of the CRC.
  fn value(&self) -> Self::Output;

  /// Calculates a new CRC from bytes.
  fn from_bytes(bytes: &[u8]) -> Self {
    let mut crc = Self::default();
    for &byte in bytes {
      crc.add_byte(byte);
    }
    crc
  }
}

impl Crc for Crc7 {
  type Output = u8;

  fn add_byte(&mut self, byte: u8) {
    const G: u8 = 0x89;
    self.0 ^= byte;
    for _ in 0..8 {
      if self.0 & 0x80 != 0 {
        self.0 ^= G;
      }
      self.0 <<= 1;
    }
  }

  fn value(&self) -> u8 {
    self.0
  }
}

impl Crc for Crc16 {
  type Output = u16;

  fn add_byte(&mut self, byte: u8) {
    self.0 = self.0 >> 8 | self.0 << 8;
    self.0 ^= u16::from(byte);
    self.0 ^= (self.0 & 0xFF) >> 4;
    self.0 ^= self.0 << 8 << 4;
    self.0 ^= (self.0 & 0xFF) << 4 << 1;
  }

  fn value(&self) -> u16 {
    self.0
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn cmd0() {
    let mut crc = Crc7::default();
    crc.add_byte(0x40);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    assert_eq!(crc.value(), 0b10010100);
  }

  #[test]
  fn cmd17() {
    let mut crc = Crc7::default();
    crc.add_byte(0x40 | 17);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    assert_eq!(crc.value(), 0b01010100);
  }

  #[test]
  fn cmd17_response() {
    let mut crc = Crc7::default();
    crc.add_byte(17);
    crc.add_byte(0x0);
    crc.add_byte(0x0);
    crc.add_byte(0x9);
    crc.add_byte(0x0);
    assert_eq!(crc.value(), 0b01100110);
  }

  #[test]
  fn all_ones() {
    let mut crc = Crc16::default();
    for _ in 0..512 {
      crc.add_byte(0xFF);
    }
    assert_eq!(crc.value(), 0x7FA1);
  }
}