1extern crate regex;
7
8use std::io::{self, BufRead};
9use std::{fmt, fs, path};
10
11#[cfg(test)]
12mod tests;
13
14#[derive(Debug, Clone, Eq, PartialEq)]
16pub enum RevisionNum {
17 V1(u8, u8),
19 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 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#[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#[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 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
138pub 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, 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 return parse_old_num(num);
319 }
320
321 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 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 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 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 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 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}