1use 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; break; }
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()); 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 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}