rustberry 0.1.0

All-purpose Raspberry Pi library for Rust
Documentation
// Copyright (C) 2018  Adam Gausmann
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! System information utilities.

use std::fmt::{self, Debug, Display, Formatter};
use std::fs::File;
use std::io::{BufRead, BufReader};

use error::{Error, ErrorKind};

/// The model name of the board.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Model {
    Pi1A,
    Pi1APlus,
    Pi1B,
    Pi1BPlus,
    Pi2B,
    Pi3B,
    Pi3BPlus,
    PiZero,
    PiZeroW,
    PiCm1,
    PiCm3
}

impl Display for Model {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let model = match *self {
            Model::Pi1A     => "Raspberry Pi 1 Model A",
            Model::Pi1APlus => "Raspberry Pi 1 Model A+",
            Model::Pi1B     => "Raspberry Pi 1 Model B",
            Model::Pi1BPlus => "Raspberry Pi 1 Model B+",
            Model::Pi2B     => "Raspberry Pi 2 Model B",
            Model::Pi3B     => "Raspberry Pi 3 Model B",
            Model::Pi3BPlus => "Raspberry Pi 3 Model B+",
            Model::PiZero   => "Raspberry Pi Zero",
            Model::PiZeroW  => "Raspberry Pi Zero W",
            Model::PiCm1    => "Raspberry Pi Compute Module 1",
            Model::PiCm3    => "Raspberry Pi Compute Module 3"
        };
        f.write_str(model)
    }
}

/// The manufacturer of the board.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Mfg {
    Unknown,
    Sony,
    Qisda,
    Egoman,
    Embest,
    SonyJp
}

impl Display for Mfg {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

/// The SoC that is used on this board.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Chip {
    Bcm2835,
    Bcm2836,
    Bcm2837
}

impl Display for Chip {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

/// Information detected about the current system.
#[derive(Debug)]
pub struct System {

    /// The detected board model.
    pub model: Model,

    /// The detected board manufacturer.
    pub manufacturer: Mfg,

    /// The detected SoC.
    pub chip: Chip,

    /// The detected memory capacity (in MB).
    pub memory: usize
} 

impl System {

    /// Attempts to open `/proc/cpuinfo` and read information about the system,
    /// returning it it found.
    ///
    /// # Errors
    ///
    /// Returns `Error` with the kind `IoError` if `/proc/cpuinfo` could not be
    /// opened for reading, or `UnsupportedSystem` if the system is not
    /// supported or could otherwise not be detected.
    pub fn detect() -> Result<System, Error> {
        let f = BufReader::new(File::open("/proc/cpuinfo")?);
        
        let revision = f.lines()
            .collect::<Result<Vec<String>, _>>()?.into_iter()
            .find(|s| s.starts_with("Revision"))
            .ok_or(Error::new(ErrorKind::UnsupportedSystem))?
            .split(": ").nth(1)
            .ok_or(Error::new(ErrorKind::UnsupportedSystem))?
            .to_string();
        let revision = u64::from_str_radix(&revision, 16).unwrap();

        // Information gathered from https://elinux.org/RPi_HardwareHistory
        let (model, manufacturer, chip, memory) = match revision {
            0x000002 => (Model::Pi1B,     Mfg::Unknown, Chip::Bcm2835, 256),
            0x000003 => (Model::Pi1B,     Mfg::Unknown, Chip::Bcm2835, 256),
            0x000004 => (Model::Pi1B,     Mfg::Sony,    Chip::Bcm2835, 256),
            0x000005 => (Model::Pi1B,     Mfg::Qisda,   Chip::Bcm2835, 256),
            0x000006 => (Model::Pi1B,     Mfg::Egoman,  Chip::Bcm2835, 256),
            0x000007 => (Model::Pi1A,     Mfg::Egoman,  Chip::Bcm2835, 256),
            0x000008 => (Model::Pi1A,     Mfg::Sony,    Chip::Bcm2835, 256),
            0x000009 => (Model::Pi1A,     Mfg::Qisda,   Chip::Bcm2835, 256),
            0x00000d => (Model::Pi1B,     Mfg::Egoman,  Chip::Bcm2835, 512),
            0x00000e => (Model::Pi1B,     Mfg::Sony,    Chip::Bcm2835, 512),
            0x00000f => (Model::Pi1B,     Mfg::Qisda,   Chip::Bcm2835, 512),
            0x000010 => (Model::Pi1BPlus, Mfg::Sony,    Chip::Bcm2835, 512),
            0x000011 => (Model::PiCm1,    Mfg::Sony,    Chip::Bcm2835, 512),
            0x000012 => (Model::Pi1APlus, Mfg::Sony,    Chip::Bcm2835, 256),
            0x000013 => (Model::Pi1BPlus, Mfg::Embest,  Chip::Bcm2835, 512),
            0x000014 => (Model::PiCm1,    Mfg::Embest,  Chip::Bcm2835, 512),
            0x000015 => (Model::Pi1APlus, Mfg::Embest,  Chip::Bcm2835, 256),
            0xa01040 => (Model::Pi2B,     Mfg::Sony,    Chip::Bcm2836, 1024),
            0xa01041 => (Model::Pi2B,     Mfg::Sony,    Chip::Bcm2836, 1024),
            0xa21041 => (Model::Pi2B,     Mfg::Embest,  Chip::Bcm2836, 1024),
            0xa22042 => (Model::Pi2B,     Mfg::Embest,  Chip::Bcm2837, 1024),
            0x900021 => (Model::Pi1APlus, Mfg::Sony,    Chip::Bcm2835, 512),
            0x900032 => (Model::Pi1BPlus, Mfg::Sony,    Chip::Bcm2835, 512),
            0x900092 => (Model::PiZero,   Mfg::Sony,    Chip::Bcm2835, 512),
            0x900093 => (Model::PiZero,   Mfg::Sony,    Chip::Bcm2835, 512),
            0x920093 => (Model::PiZero,   Mfg::Embest,  Chip::Bcm2835, 512),
            0x9000c1 => (Model::PiZeroW,  Mfg::Sony,    Chip::Bcm2835, 512),
            0xa02082 => (Model::Pi3B,     Mfg::Sony,    Chip::Bcm2837, 1024),
            0xa020a0 => (Model::PiCm3,    Mfg::Sony,    Chip::Bcm2837, 1024),
            0xa22082 => (Model::Pi3B,     Mfg::Embest,  Chip::Bcm2837, 1024),
            0xa32082 => (Model::Pi3B,     Mfg::SonyJp,  Chip::Bcm2837, 1024),
            0xa020d3 => (Model::Pi3BPlus, Mfg::Sony,    Chip::Bcm2837, 1024),
            _ => return Err(Error::new(ErrorKind::UnsupportedSystem)),
        };

        Ok(System {
            model,
            manufacturer,
            chip,
            memory
        })
    }

    /// The base location in physical memory where peripheral registers are
    /// mapped - this is dependent on the SoC.
    pub fn peripheral_offset(&self) -> usize {
        match self.chip {
            Chip::Bcm2835 => 0x2000_0000,
            Chip::Bcm2836 => 0x3f00_0000,
            Chip::Bcm2837 => 0x3f00_0000
        }
    }
}