Skip to main content

ferrix_lib/
firmware.rs

1/* firmware.rs
2 *
3 * Copyright 2026 Michail Krasnov <mskrasnov07@ya.ru>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21//! Get firmware settings (only for modern systems with UEFI)
22
23use anyhow::{Result, anyhow};
24use serde::{Deserialize, Serialize};
25use std::{
26    ffi::OsString,
27    fs::{read_dir, read_to_string},
28    path::Path,
29};
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Firmware {
33    pub driver_name: String,
34    pub attributes: Vec<Attribute>,
35}
36
37impl Firmware {
38    const FIRMWARE_DIR: &'static str = "/sys/class/firmware-attributes/";
39
40    pub fn new() -> Result<Self> {
41        let mut firmware_dir_contents = read_dir(Self::FIRMWARE_DIR)?;
42        let driver_dir = firmware_dir_contents
43            .next()
44            .ok_or_else(|| anyhow!("No `firmware-attributes` directory found."))??;
45
46        let driver_name = os_str_into_str(driver_dir.file_name())?;
47        let attributes_dir_contents = read_dir(driver_dir.path())?;
48        let mut attributes = vec![];
49
50        for attr in attributes_dir_contents {
51            let attr = attr?;
52            let metadata = attr.metadata()?;
53            let fname = os_str_into_str(attr.file_name())?;
54
55            if !metadata.is_dir() || &fname != "attributes" {
56                continue;
57            }
58
59            let attribute = Self::read_attributes(attr.path())?;
60            attributes = attribute; // NOTE: shitcode?
61            break; // NOTE: shitcode?
62        }
63
64        Ok(Self {
65            driver_name,
66            attributes,
67        })
68    }
69
70    fn read_attributes<P>(dir: P) -> Result<Vec<Attribute>>
71    where
72        P: AsRef<Path> + std::fmt::Debug,
73    {
74        let mut attrs = vec![];
75        let dir_contents = read_dir(dir)?;
76
77        for d in dir_contents {
78            let d = d?;
79            if !d.metadata()?.is_dir() {
80                continue;
81            }
82            let attribute = Attribute::read_dir(d.path())?;
83            attrs.push(attribute);
84        }
85        attrs.sort_by_key(|key| key.display_name.clone()); // NOTE: SHITCODE
86
87        Ok(attrs)
88    }
89}
90
91fn os_str_into_str(os_str: OsString) -> Result<String> {
92    let s = os_str
93        .into_string()
94        .map_err(|err| anyhow!("Failed to convert directory name into the string: {err:?}"))?;
95    Ok(s)
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Attribute {
100    pub current_value: String,
101    pub display_name: String,
102    pub possible_values: String,
103    pub param_type: String,
104}
105
106impl Attribute {
107    pub fn read_dir<P>(dir: P) -> Result<Self>
108    where
109        P: AsRef<Path> + std::fmt::Debug,
110    {
111        // NOTE: We assume that we receive a DIRECTORY as input
112        let dir_contents = read_dir(dir)?;
113
114        let mut current_value = String::new();
115        let mut display_name = String::new();
116        let mut possible_values = String::new();
117        let mut param_type = String::new();
118
119        for d in dir_contents {
120            let d = d?;
121            if !d.metadata()?.is_file() {
122                continue;
123            }
124            let name = os_str_into_str(d.file_name())?;
125
126            let read = || -> Result<String> {
127                let s = read_to_string(d.path())?;
128                Ok(s.trim().to_string())
129            };
130
131            match &name as &str {
132                "display_name" => display_name = read()?,
133                "current_value" => current_value = read()?,
134                "possible_values" => possible_values = read()?,
135                "type" => param_type = read()?,
136                _ => {}
137            }
138        }
139
140        Ok(Self {
141            current_value,
142            display_name,
143            possible_values,
144            param_type,
145        })
146    }
147}