uconsole_sleep/hardware/
rf.rs1use std::{
3 fs,
4 path::{Path, PathBuf},
5};
6
7use log::{debug, info, warn};
8
9pub const RFKILL_PATH_BT: &str = "/sys/class/rfkill/rfkill0";
10pub const RFKILL_PATH_WIFI: &str = "/sys/class/rfkill/rfkill1";
11
12pub fn rfkill_state_path(path: &std::path::Path) -> PathBuf {
13 path.join("state")
14}
15
16pub fn write_rfkill_state(path: &Path, block: bool, dry_run: bool) {
17 let state = rfkill_state_path(path);
18 if dry_run {
19 debug!(
20 "DRY-RUN: would write '{}' to {}",
21 if block { "0" } else { "1" },
22 state.display()
23 );
24 return;
25 }
26 let _ = std::fs::write(&state, if block { "0" } else { "1" });
27 info!(
28 "WiFi: {} via {}",
29 if block { "blocked" } else { "unblocked" },
30 state.display()
31 );
32}
33
34pub fn find_default_rfkill_path() -> Option<PathBuf> {
35 let p = PathBuf::from(RFKILL_PATH_WIFI);
36 if p.exists() { Some(p) } else { None }
37}
38
39pub fn find_default_rfkill_path_bt() -> Option<PathBuf> {
40 let p = PathBuf::from(RFKILL_PATH_BT);
41 if p.exists() { Some(p) } else { None }
42}
43
44#[derive(Clone, Debug)]
47pub struct WifiConfig {
48 pub enabled: bool,
49 pub rfkill_path: Option<PathBuf>,
50}
51
52impl WifiConfig {
53 pub fn new(enabled: bool, rfkill_path: Option<PathBuf>) -> Self {
54 let mut p = rfkill_path;
55 if enabled && p.is_none() {
56 p = Some(PathBuf::from(RFKILL_PATH_WIFI));
57 }
58 WifiConfig {
59 enabled,
60 rfkill_path: p,
61 }
62 }
63
64 pub fn block(&self, dry_run: bool) {
65 if !self.enabled {
66 return;
67 }
68 if let Some(path) = &self.rfkill_path {
69 let state = path.join("state");
70 if dry_run {
71 debug!("DRY-RUN: would write '0' to {}", state.display());
72 return;
73 }
74 let _ = fs::write(&state, "0");
75 debug!("WiFi: blocked via {}", state.display());
76 } else {
77 warn!("WiFi toggling enabled but no rfkill path provided");
78 }
79 }
80
81 pub fn unblock(&self, dry_run: bool) {
82 if !self.enabled {
83 return;
84 }
85 if let Some(path) = &self.rfkill_path {
86 let state = path.join("state");
87 if dry_run {
88 debug!("DRY-RUN: would write '1' to {}", state.display());
89 return;
90 }
91 let _ = fs::write(&state, "1");
92 debug!("WiFi: unblocked via {}", state.display());
93 } else {
94 warn!("WiFi toggling enabled but no rfkill path provided");
95 }
96 }
97}
98
99#[derive(Clone, Debug)]
101pub struct BTConfig {
102 pub enabled: bool,
103 pub rfkill_path: Option<PathBuf>,
104}
105
106impl BTConfig {
107 pub fn new(enabled: bool, rfkill_path: Option<PathBuf>) -> Self {
108 let mut p = rfkill_path;
109 if enabled && p.is_none() {
110 p = Some(PathBuf::from(RFKILL_PATH_BT));
111 }
112 BTConfig {
113 enabled,
114 rfkill_path: p,
115 }
116 }
117
118 pub fn block(&self, dry_run: bool) {
119 if !self.enabled {
120 return;
121 }
122 if let Some(path) = &self.rfkill_path {
123 let state = path.join("state");
124 if dry_run {
125 debug!("DRY-RUN: would write '0' to {}", state.display());
126 return;
127 }
128 let _ = fs::write(&state, "0");
129 debug!("BT: blocked via {}", state.display());
130 } else {
131 warn!("BT toggling enabled but no rfkill path provided");
132 }
133 }
134
135 pub fn unblock(&self, dry_run: bool) {
136 if !self.enabled {
137 return;
138 }
139 if let Some(path) = &self.rfkill_path {
140 let state = path.join("state");
141 if dry_run {
142 debug!("DRY-RUN: would write '1' to {}", state.display());
143 return;
144 }
145 let _ = fs::write(&state, "1");
146 debug!("BT: unblocked via {}", state.display());
147 } else {
148 warn!("BT toggling enabled but no rfkill path provided");
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use std::env;
157 use std::fs;
158
159 #[test]
160 fn test_find_default_rfkill_path() {
161 let _ = find_default_rfkill_path();
162 }
163
164 #[test]
165 fn test_find_default_rfkill_path_bt() {
166 let _ = find_default_rfkill_path_bt();
167 }
168
169 #[test]
170 fn test_write_rfkill_state_dry_run() {
171 let tmp = env::temp_dir().join(format!(
172 "uconsole_wifi_{}",
173 std::time::SystemTime::now()
174 .duration_since(std::time::UNIX_EPOCH)
175 .unwrap()
176 .as_millis()
177 ));
178 let _ = fs::create_dir_all(&tmp);
179 fs::write(tmp.join("state"), "0").unwrap();
180 write_rfkill_state(&tmp, true, true);
181 let s = fs::read_to_string(tmp.join("state")).unwrap();
183 assert_eq!(s, "0");
184 }
185
186 #[test]
187 fn test_write_rfkill_state_dry_run_bt() {
188 let tmp = env::temp_dir().join(format!(
189 "uconsole_bt_{}",
190 std::time::SystemTime::now()
191 .duration_since(std::time::UNIX_EPOCH)
192 .unwrap()
193 .as_millis()
194 ));
195 let _ = fs::create_dir_all(&tmp);
196 fs::write(tmp.join("state"), "0").unwrap();
197 write_rfkill_state(&tmp, true, true);
198 let s = fs::read_to_string(tmp.join("state")).unwrap();
200 assert_eq!(s, "0");
201 }
202}