use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::types::CurrentChannel;
pub fn get_supported_channels(iface: &str) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
let output = std::process::Command::new("iw").args(["phy"]).output();
let phy_output = std::process::Command::new("iw").args(["dev", iface, "info"]).output();
let phy_name = match phy_output {
Ok(o) => {
let s = String::from_utf8_lossy(&o.stdout);
let mut name = String::new();
for line in s.lines() {
let t = line.trim();
if t.starts_with("wiphy ") {
name = format!("phy{}", t.strip_prefix("wiphy ").unwrap_or("0"));
break;
}
}
name
}
Err(_) => String::new(),
};
let mut channels_2g = Vec::new();
let mut channels_5g = Vec::new();
let mut channels_6g = Vec::new();
if let Ok(o) = output {
let s = String::from_utf8_lossy(&o.stdout);
let mut in_our_phy = phy_name.is_empty();
let mut in_freq_section = false;
for line in s.lines() {
let trimmed = line.trim();
if trimmed.starts_with("Wiphy ") {
let this_phy = trimmed.strip_prefix("Wiphy ").unwrap_or("");
in_our_phy = phy_name.is_empty() || this_phy == phy_name;
in_freq_section = false;
continue;
}
if !in_our_phy { continue; }
if trimmed.starts_with("Frequencies:") { in_freq_section = true; continue; }
if in_freq_section && !trimmed.starts_with('*') && !trimmed.contains("MHz") {
in_freq_section = false;
continue;
}
if in_freq_section && trimmed.contains("MHz") && !trimmed.contains("disabled") {
let parts: Vec<&str> = trimmed.split_whitespace().collect();
for (i, part) in parts.iter().enumerate() {
if *part == "MHz" && i > 0 {
if let Ok(freq) = parts[i - 1].replace('*', "").trim().parse::<u16>() {
let ch = crate::parse::frequency_to_channel(freq);
if ch > 0 {
if freq < 3000 && !channels_2g.contains(&ch) { channels_2g.push(ch); }
else if (3000..5900).contains(&freq) && !channels_5g.contains(&ch) { channels_5g.push(ch); }
else if freq >= 5900 && !channels_6g.contains(&ch) { channels_6g.push(ch); }
}
}
break;
}
}
}
}
}
if channels_2g.is_empty() { channels_2g = (1..=13).collect(); }
if channels_5g.is_empty() {
channels_5g = vec![36,40,44,48,52,56,60,64,100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165];
}
(channels_2g, channels_5g, channels_6g)
}
#[allow(clippy::too_many_arguments)]
pub fn start_channel_hopper(
iface: &str,
fixed_channel: u8,
current_channel: CurrentChannel,
dwell_2g: u64,
dwell_5g: u64,
dwell_6g: u64,
shutdown: Arc<AtomicBool>,
hop_pause: Arc<AtomicBool>,
) {
if fixed_channel > 0 {
let _ = std::process::Command::new("iw")
.args(["dev", iface, "set", "channel", &fixed_channel.to_string()])
.output();
*current_channel.lock().unwrap() = fixed_channel;
return;
}
let (channels_2g, channels_5g, channels_6g) = get_supported_channels(iface);
let iface = iface.to_string();
std::thread::spawn(move || {
let mut hop_sequence: Vec<(u8, u64)> = Vec::new();
let max_len = channels_2g.len().max(channels_5g.len());
for i in 0..max_len {
if i < channels_2g.len() { hop_sequence.push((channels_2g[i], dwell_2g)); }
if i < channels_5g.len() { hop_sequence.push((channels_5g[i], dwell_5g)); }
}
for ch in &channels_6g { hop_sequence.push((*ch, dwell_6g)); }
if hop_sequence.is_empty() { return; }
while !shutdown.load(Ordering::Relaxed) {
for &(ch, dwell) in &hop_sequence {
if shutdown.load(Ordering::Relaxed) { break; }
if hop_pause.load(Ordering::Relaxed) {
std::thread::sleep(Duration::from_millis(100));
continue;
}
let result = std::process::Command::new("iw")
.args(["dev", &iface, "set", "channel", &ch.to_string()])
.output();
if let Ok(o) = result {
if o.status.success() {
*current_channel.lock().unwrap() = ch;
}
}
std::thread::sleep(Duration::from_millis(dwell));
}
}
});
}