extern crate regex;
use std::io::{self, BufRead};
use std::{fmt, fs, path};
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RevisionNum {
V1(u8, u8),
V2(u8),
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemorySize {
MiB256,
MiB512,
MiB1024,
MiB2048,
MiB4096,
}
impl MemorySize {
pub fn mib(&self) -> u16 {
match self {
&MemorySize::MiB256 => 256,
&MemorySize::MiB512 => 512,
&MemorySize::MiB1024 => 1024,
&MemorySize::MiB2048 => 2048,
&MemorySize::MiB4096 => 4096,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Manufacturer {
SonyUK,
SonyJapan,
Egoman,
Embest,
Qisda,
Stadium,
}
impl fmt::Display for Manufacturer {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&Manufacturer::SonyUK => write!(f, "Sony UK"),
&Manufacturer::SonyJapan => write!(f, "Sony Japan"),
&Manufacturer::Egoman => write!(f, "Egoman"),
&Manufacturer::Embest => write!(f, "Embest"),
&Manufacturer::Qisda => write!(f, "Qisda"),
&Manufacturer::Stadium => write!(f, "Stadium"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Model {
A,
APlus,
B,
BPlus,
B2,
Alpha,
B3,
B3Plus,
A3Plus,
CM1,
Zero,
CM3,
ZeroW,
Internal,
CM3Plus,
B4,
}
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&Model::A => write!(f, "A"),
&Model::APlus => write!(f, "A+"),
&Model::B => write!(f, "B"),
&Model::BPlus => write!(f, "B+"),
&Model::B2 => write!(f, "2B"),
&Model::Alpha => write!(f, "Alpha"),
&Model::B3 => write!(f, "3B"),
&Model::B3Plus => write!(f, "3B+"),
&Model::A3Plus => write!(f, "3A+"),
&Model::CM1 => write!(f, "CM1"),
&Model::Zero => write!(f, "Zero"),
&Model::CM3 => write!(f, "CM3"),
&Model::ZeroW => write!(f, "Zero W"),
&Model::Internal => write!(f, "Internal use only"),
&Model::CM3Plus => write!(f, "CM3+"),
&Model::B4 => write!(f, "4B"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Processor {
BCM2835,
BCM2836,
BCM2837,
BCM2711,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RaspberryPiInfo {
pub revision: Revision,
pub serial: String,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Revision {
pub revision_code: u32,
pub memory: MemorySize,
pub mfg: Manufacturer,
pub model: Model,
pub revision_num: RevisionNum,
pub processor: Processor,
}
pub fn load_cpuinfo() -> io::Result<Option<RaspberryPiInfo>> {
parse_cpuinfo_path(path::Path::new("/proc/cpuinfo"))
}
fn parse_cpuinfo_path(path: &path::Path) -> io::Result<Option<RaspberryPiInfo>> {
let f = fs::File::open(path)?;
let rev_pattern = regex::Regex::new("^Revision\t+: ([0-9a-fA-F]+)").unwrap();
let serial_pattern = regex::Regex::new("Serial\t+: ([0-9a-fA-F]+)").unwrap();
let mut revision = None;
let mut serial = None;
for r in io::BufReader::new(f).lines() {
let line = r?;
if let Some(captures) = rev_pattern.captures(&line) {
let hex_str = captures.get(1).unwrap().as_str();
let num = if let Ok(n) = u32::from_str_radix(hex_str, 16) {
n
} else {
return Ok(None);
};
revision = parse_num(num);
} else if let Some(captures) = serial_pattern.captures(&line) {
let s = captures.get(1).unwrap().as_str();
serial = Some(s.to_string());
}
}
Ok(revision.and_then(|r| {
serial.map(|s| RaspberryPiInfo {
revision: r,
serial: s,
})
}))
}
fn parse_old_num(num: u32) -> Option<Revision> {
match num {
0x2 | 0x3 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Egoman,
model: Model::B,
revision_num: RevisionNum::V1(1, 0),
processor: Processor::BCM2835,
}),
0x4 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::SonyUK,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x5 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Qisda,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x6 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Egoman,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x7 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Egoman,
model: Model::A,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x8 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::SonyUK,
model: Model::A,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x9 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Qisda,
model: Model::A,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0xd => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::Egoman,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0xe => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::SonyUK,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0xf => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::Egoman,
model: Model::B,
revision_num: RevisionNum::V1(2, 0),
processor: Processor::BCM2835,
}),
0x10 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::SonyUK,
model: Model::BPlus,
revision_num: RevisionNum::V1(1, 0),
processor: Processor::BCM2835,
}),
0x11 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::SonyUK,
model: Model::CM1,
revision_num: RevisionNum::V1(1, 0),
processor: Processor::BCM2835,
}),
0x12 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::SonyUK,
model: Model::APlus,
revision_num: RevisionNum::V1(1, 1),
processor: Processor::BCM2835,
}),
0x13 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::Embest,
model: Model::BPlus,
revision_num: RevisionNum::V1(1, 2),
processor: Processor::BCM2835,
}),
0x14 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB512,
mfg: Manufacturer::Embest,
model: Model::CM1,
revision_num: RevisionNum::V1(1, 0),
processor: Processor::BCM2835,
}),
0x15 => Some(Revision {
revision_code: num,
memory: MemorySize::MiB256,
mfg: Manufacturer::Embest,
model: Model::APlus,
revision_num: RevisionNum::V1(1, 1),
processor: Processor::BCM2835,
}),
_ => None,
}
}
fn parse_num(num: u32) -> Option<Revision> {
if (num >> 23) & 0x1 != 1 {
return parse_old_num(num);
}
let memory = match (num >> 20) & 0x7 {
0 => MemorySize::MiB256,
1 => MemorySize::MiB512,
2 => MemorySize::MiB1024,
3 => MemorySize::MiB2048,
4 => MemorySize::MiB4096,
_ => return None,
};
let mfg = match (num >> 16) & 0xF {
0 => Manufacturer::SonyUK,
1 => Manufacturer::Egoman,
2 => Manufacturer::Embest,
3 => Manufacturer::SonyJapan,
4 => Manufacturer::Embest,
5 => Manufacturer::Stadium,
_ => return None,
};
let processor = match (num >> 12) & 0xF {
0 => Processor::BCM2835,
1 => Processor::BCM2836,
2 => Processor::BCM2837,
3 => Processor::BCM2711,
_ => return None,
};
let model = match (num >> 4) & 0xFF {
0x0 => Model::A,
0x1 => Model::B,
0x2 => Model::APlus,
0x3 => Model::BPlus,
0x4 => Model::B2,
0x5 => Model::Alpha,
0x6 => Model::CM1,
0x8 => Model::B3,
0x9 => Model::Zero,
0xA => Model::CM3,
0xC => Model::ZeroW,
0xD => Model::B3Plus,
0xE => Model::A3Plus,
0xF => Model::Internal,
0x10 => Model::CM3Plus,
0x11 => Model::B4,
_ => return None,
};
let rev = num & 0xF;
Some(Revision {
revision_code: num,
memory,
mfg,
model,
revision_num: RevisionNum::V2(rev as u8),
processor,
})
}