rpi_info/
lib.rs

1//! There's only one entry point in this crate: `load_cpuinfo()`. Call it, handle any errors, then
2//! enjoy knowing more about your Raspberry Pi hardware!
3//!
4//! Hardware data is from https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md.
5
6extern crate regex;
7
8use std::io::{self, BufRead};
9use std::{fmt, fs, path};
10
11#[cfg(test)]
12mod tests;
13
14/// This is a board revision number (e.g. 1.0 for old style, 1 for new), not the top level revision code.
15#[derive(Debug, Clone, Eq, PartialEq)]
16pub enum RevisionNum {
17    /// Old-style x.y revisions
18    V1(u8, u8),
19    /// New-style integer revisions
20    V2(u8),
21}
22
23#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
24pub enum MemorySize {
25    MiB256,
26    MiB512,
27    MiB1024,
28    MiB2048,
29    MiB4096,
30}
31
32impl MemorySize {
33    /// Returns the amount of RAM in MiB for when you want the actual number.
34    pub fn mib(&self) -> u16 {
35        match self {
36            &MemorySize::MiB256 => 256,
37            &MemorySize::MiB512 => 512,
38            &MemorySize::MiB1024 => 1024,
39            &MemorySize::MiB2048 => 2048,
40            &MemorySize::MiB4096 => 4096,
41        }
42    }
43}
44
45#[derive(Debug, Clone, Eq, PartialEq)]
46pub enum Manufacturer {
47    SonyUK,
48    SonyJapan,
49    Egoman,
50    Embest,
51    Qisda,
52    Stadium,
53}
54
55impl fmt::Display for Manufacturer {
56    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
57        match self {
58            &Manufacturer::SonyUK => write!(f, "Sony UK"),
59            &Manufacturer::SonyJapan => write!(f, "Sony Japan"),
60            &Manufacturer::Egoman => write!(f, "Egoman"),
61            &Manufacturer::Embest => write!(f, "Embest"),
62            &Manufacturer::Qisda => write!(f, "Qisda"),
63            &Manufacturer::Stadium => write!(f, "Stadium"),
64        }
65    }
66}
67
68/// The names are re-ordered because variants can't start with a number in Rust.
69#[derive(Debug, Clone, Eq, PartialEq)]
70pub enum Model {
71    A,
72    APlus,
73    B,
74    BPlus,
75    B2,
76    Alpha,
77    B3,
78    B3Plus,
79    A3Plus,
80    CM1,
81    Zero,
82    CM3,
83    ZeroW,
84    Internal,
85    CM3Plus,
86    B4,
87}
88
89impl fmt::Display for Model {
90    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
91        match self {
92            &Model::A => write!(f, "A"),
93            &Model::APlus => write!(f, "A+"),
94            &Model::B => write!(f, "B"),
95            &Model::BPlus => write!(f, "B+"),
96            &Model::B2 => write!(f, "2B"),
97            &Model::Alpha => write!(f, "Alpha"),
98            &Model::B3 => write!(f, "3B"),
99            &Model::B3Plus => write!(f, "3B+"),
100            &Model::A3Plus => write!(f, "3A+"),
101            &Model::CM1 => write!(f, "CM1"),
102            &Model::Zero => write!(f, "Zero"),
103            &Model::CM3 => write!(f, "CM3"),
104            &Model::ZeroW => write!(f, "Zero W"),
105            &Model::Internal => write!(f, "Internal use only"),
106            &Model::CM3Plus => write!(f, "CM3+"),
107            &Model::B4 => write!(f, "4B"),
108        }
109    }
110}
111
112#[derive(Debug, Clone, Eq, PartialEq)]
113pub enum Processor {
114    BCM2835,
115    BCM2836,
116    BCM2837,
117    BCM2711,
118}
119
120/// The top level aggregation of information about a particular hardware configuration.
121#[derive(Debug, Clone, Eq, PartialEq)]
122pub struct RaspberryPiInfo {
123    pub revision: Revision,
124    pub serial: String,
125}
126
127#[derive(Debug, Clone, Eq, PartialEq)]
128pub struct Revision {
129    /// The code from which the rest of the information was derived
130    pub revision_code: u32,
131    pub memory: MemorySize,
132    pub mfg: Manufacturer,
133    pub model: Model,
134    pub revision_num: RevisionNum,
135    pub processor: Processor,
136}
137
138/// Load RPi info from `/proc/cpuinfo`.
139///
140/// The outer `Result` is for any i/o errors (more likely a permission issue than any other error
141/// for a procfs file).
142///
143/// The inner `Option` will be `None` if the contents of `/proc/cpuinfo` was not recognized as
144/// any known Raspberry Pi model.
145pub fn load_cpuinfo() -> io::Result<Option<RaspberryPiInfo>> {
146    parse_cpuinfo_path(path::Path::new("/proc/cpuinfo"))
147}
148
149fn parse_cpuinfo_path(path: &path::Path) -> io::Result<Option<RaspberryPiInfo>> {
150    let f = fs::File::open(path)?;
151    let rev_pattern = regex::Regex::new("^Revision\t+: ([0-9a-fA-F]+)").unwrap();
152    let serial_pattern = regex::Regex::new("Serial\t+: ([0-9a-fA-F]+)").unwrap();
153
154    let mut revision = None;
155    let mut serial = None;
156    for r in io::BufReader::new(f).lines() {
157        let line = r?;
158        if let Some(captures) = rev_pattern.captures(&line) {
159            let hex_str = captures.get(1).unwrap().as_str();
160            let num = if let Ok(n) = u32::from_str_radix(hex_str, 16) {
161                n
162            } else {
163                return Ok(None);
164            };
165
166            revision = parse_num(num);
167        } else if let Some(captures) = serial_pattern.captures(&line) {
168            let s = captures.get(1).unwrap().as_str();
169            serial = Some(s.to_string());
170        }
171    }
172
173    Ok(revision.and_then(|r| {
174        serial.map(|s| RaspberryPiInfo {
175            revision: r,
176            serial: s,
177        })
178    }))
179}
180
181fn parse_old_num(num: u32) -> Option<Revision> {
182    match num {
183        0x2 | 0x3 => Some(Revision {
184            revision_code: num,
185            memory: MemorySize::MiB256,
186            mfg: Manufacturer::Egoman,
187            model: Model::B,
188            revision_num: RevisionNum::V1(1, 0),
189            processor: Processor::BCM2835,
190        }),
191        0x4 => Some(Revision {
192            revision_code: num,
193            memory: MemorySize::MiB256,
194            mfg: Manufacturer::SonyUK,
195            model: Model::B,
196            revision_num: RevisionNum::V1(2, 0),
197            processor: Processor::BCM2835,
198        }),
199        0x5 => Some(Revision {
200            revision_code: num,
201            memory: MemorySize::MiB256,
202            mfg: Manufacturer::Qisda,
203            model: Model::B,
204            revision_num: RevisionNum::V1(2, 0),
205            processor: Processor::BCM2835,
206        }),
207        0x6 => Some(Revision {
208            revision_code: num,
209            memory: MemorySize::MiB256,
210            mfg: Manufacturer::Egoman,
211            model: Model::B,
212            revision_num: RevisionNum::V1(2, 0),
213            processor: Processor::BCM2835,
214        }),
215        0x7 => Some(Revision {
216            revision_code: num,
217            memory: MemorySize::MiB256,
218            mfg: Manufacturer::Egoman,
219            model: Model::A,
220            revision_num: RevisionNum::V1(2, 0),
221            processor: Processor::BCM2835,
222        }),
223        0x8 => Some(Revision {
224            revision_code: num,
225            memory: MemorySize::MiB256,
226            mfg: Manufacturer::SonyUK,
227            model: Model::A,
228            revision_num: RevisionNum::V1(2, 0),
229            processor: Processor::BCM2835,
230        }),
231        0x9 => Some(Revision {
232            revision_code: num,
233            memory: MemorySize::MiB256,
234            mfg: Manufacturer::Qisda,
235            model: Model::A,
236            revision_num: RevisionNum::V1(2, 0),
237            processor: Processor::BCM2835,
238        }),
239        0xd => Some(Revision {
240            revision_code: num,
241            memory: MemorySize::MiB512,
242            mfg: Manufacturer::Egoman,
243            model: Model::B,
244            revision_num: RevisionNum::V1(2, 0),
245            processor: Processor::BCM2835,
246        }),
247        0xe => Some(Revision {
248            revision_code: num,
249            memory: MemorySize::MiB512,
250            mfg: Manufacturer::SonyUK,
251            model: Model::B,
252            revision_num: RevisionNum::V1(2, 0),
253            processor: Processor::BCM2835,
254        }),
255        0xf => Some(Revision {
256            revision_code: num,
257            memory: MemorySize::MiB512,
258            mfg: Manufacturer::Egoman,
259            model: Model::B,
260            revision_num: RevisionNum::V1(2, 0),
261            processor: Processor::BCM2835,
262        }),
263        0x10 => Some(Revision {
264            revision_code: num,
265            memory: MemorySize::MiB512,
266            mfg: Manufacturer::SonyUK,
267            model: Model::BPlus,
268            revision_num: RevisionNum::V1(1, 0),
269            processor: Processor::BCM2835,
270        }),
271        0x11 => Some(Revision {
272            revision_code: num,
273            memory: MemorySize::MiB512,
274            mfg: Manufacturer::SonyUK,
275            model: Model::CM1,
276            revision_num: RevisionNum::V1(1, 0),
277            processor: Processor::BCM2835,
278        }),
279        0x12 => Some(Revision {
280            revision_code: num,
281            memory: MemorySize::MiB256,
282            mfg: Manufacturer::SonyUK,
283            model: Model::APlus,
284            revision_num: RevisionNum::V1(1, 1),
285            processor: Processor::BCM2835,
286        }),
287        0x13 => Some(Revision {
288            revision_code: num,
289            memory: MemorySize::MiB512,
290            mfg: Manufacturer::Embest,
291            model: Model::BPlus,
292            revision_num: RevisionNum::V1(1, 2),
293            processor: Processor::BCM2835,
294        }),
295        0x14 => Some(Revision {
296            revision_code: num,
297            memory: MemorySize::MiB512,
298            mfg: Manufacturer::Embest,
299            model: Model::CM1,
300            revision_num: RevisionNum::V1(1, 0),
301            processor: Processor::BCM2835,
302        }),
303        0x15 => Some(Revision {
304            revision_code: num, // Documented as "256/512" so who knows
305            memory: MemorySize::MiB256,
306            mfg: Manufacturer::Embest,
307            model: Model::APlus,
308            revision_num: RevisionNum::V1(1, 1),
309            processor: Processor::BCM2835,
310        }),
311        _ => None,
312    }
313}
314
315fn parse_num(num: u32) -> Option<Revision> {
316    if (num >> 23) & 0x1 != 1 {
317        // bit 23 is not set; this is an old revision.
318        return parse_old_num(num);
319    }
320
321    // bits 20-22
322    let memory = match (num >> 20) & 0x7 {
323        0 => MemorySize::MiB256,
324        1 => MemorySize::MiB512,
325        2 => MemorySize::MiB1024,
326        3 => MemorySize::MiB2048,
327        4 => MemorySize::MiB4096,
328        _ => return None,
329    };
330
331    // bits 16-19
332    let mfg = match (num >> 16) & 0xF {
333        0 => Manufacturer::SonyUK,
334        1 => Manufacturer::Egoman,
335        2 => Manufacturer::Embest,
336        3 => Manufacturer::SonyJapan,
337        4 => Manufacturer::Embest,
338        5 => Manufacturer::Stadium,
339        _ => return None,
340    };
341
342    // bits 12-15
343    let processor = match (num >> 12) & 0xF {
344        0 => Processor::BCM2835,
345        1 => Processor::BCM2836,
346        2 => Processor::BCM2837,
347        3 => Processor::BCM2711,
348        _ => return None,
349    };
350
351    // bits 4-11
352    let model = match (num >> 4) & 0xFF {
353        0x0 => Model::A,
354        0x1 => Model::B,
355        0x2 => Model::APlus,
356        0x3 => Model::BPlus,
357        0x4 => Model::B2,
358        0x5 => Model::Alpha,
359        0x6 => Model::CM1,
360        0x8 => Model::B3,
361        0x9 => Model::Zero,
362        0xA => Model::CM3,
363        // no 0xB defined
364        0xC => Model::ZeroW,
365        0xD => Model::B3Plus,
366        0xE => Model::A3Plus,
367        0xF => Model::Internal,
368        0x10 => Model::CM3Plus,
369        0x11 => Model::B4,
370        _ => return None,
371    };
372
373    // bits 0-3
374    let rev = num & 0xF;
375
376    Some(Revision {
377        revision_code: num,
378        memory,
379        mfg,
380        model,
381        revision_num: RevisionNum::V2(rev as u8),
382        processor,
383    })
384}