1use anyhow::{Result, anyhow};
34use serde::{Deserialize, Serialize};
35use std::{
36 ffi::OsString,
37 fs::{read_dir, read_to_string},
38 path::Path,
39};
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Firmware {
43 pub driver_name: String,
44 pub attributes: Vec<Attribute>,
45}
46
47impl Firmware {
48 const FIRMWARE_DIR: &'static str = "/sys/class/firmware-attributes/";
49
50 pub fn new() -> Result<Self> {
51 let mut firmware_dir_contents = read_dir(Self::FIRMWARE_DIR)?;
52 let driver_dir = firmware_dir_contents
53 .next()
54 .ok_or_else(|| anyhow!("No `firmware-attributes` directory found."))??;
55
56 let driver_name = os_str_into_str(driver_dir.file_name())?;
57 let attributes_dir_contents = read_dir(driver_dir.path())?;
58 let mut attributes = vec![];
59
60 for attr in attributes_dir_contents {
61 let attr = attr?;
62 let metadata = attr.metadata()?;
63 let fname = os_str_into_str(attr.file_name())?;
64
65 if !metadata.is_dir() || &fname != "attributes" {
66 continue;
67 }
68
69 let attribute = Self::read_attributes(attr.path())?;
70 attributes = attribute; break; }
73
74 Ok(Self {
75 driver_name,
76 attributes,
77 })
78 }
79
80 fn read_attributes<P>(dir: P) -> Result<Vec<Attribute>>
81 where
82 P: AsRef<Path> + std::fmt::Debug,
83 {
84 let mut attrs = vec![];
85 let dir_contents = read_dir(dir)?;
86
87 for d in dir_contents {
88 let d = d?;
89 if !d.metadata()?.is_dir() {
90 continue;
91 }
92 let attribute = Attribute::read_dir(d.path())?;
93 attrs.push(attribute);
94 }
95 attrs.sort_by_key(|key| key.display_name.clone()); Ok(attrs)
98 }
99}
100
101fn os_str_into_str(os_str: OsString) -> Result<String> {
102 let s = os_str
103 .into_string()
104 .map_err(|err| anyhow!("Failed to convert directory name into the string: {err:?}"))?;
105 Ok(s)
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct Attribute {
110 pub current_value: String,
111 pub display_name: String,
112 pub possible_values: String,
113 pub param_type: String,
114}
115
116impl Attribute {
117 pub fn read_dir<P>(dir: P) -> Result<Self>
118 where
119 P: AsRef<Path> + std::fmt::Debug,
120 {
121 let dir_contents = read_dir(dir)?;
123
124 let mut current_value = String::new();
125 let mut display_name = String::new();
126 let mut possible_values = String::new();
127 let mut param_type = String::new();
128
129 for d in dir_contents {
130 let d = d?;
131 if !d.metadata()?.is_file() {
132 continue;
133 }
134 let name = os_str_into_str(d.file_name())?;
135
136 let read = || -> Result<String> {
137 let s = read_to_string(d.path())?;
138 Ok(s.trim().to_string())
139 };
140
141 match &name as &str {
142 "display_name" => display_name = read()?,
143 "current_value" => current_value = read()?,
144 "possible_values" => possible_values = read()?,
145 "type" => param_type = read()?,
146 _ => {}
147 }
148 }
149
150 Ok(Self {
151 current_value,
152 display_name,
153 possible_values,
154 param_type,
155 })
156 }
157}