use std::{
collections::HashMap,
fmt::{Debug, Display},
io::BufRead,
};
use log::debug;
use tokio::process::Command;
use crate::{
BandWidth, BandWidthArg, CommandExt, Impl, WiFiResult, check_command,
os::iw_res::{Info, PhyInfo},
};
mod iw_res;
pub struct OS;
impl Impl for OS {
async fn check_environment() -> crate::WiFiResult {
check_command("iw").await?;
check_command("ifconfig").await?;
Ok(())
}
async fn interface_list() -> Result<Vec<crate::Interface>, crate::WiFiError> {
let output = Command::new("sh")
.arg("-c")
.arg("iw dev | grep Interface")
.output()
.await
.map_err(|e| crate::WiFiError::System(format!("Failed to execute command: {}", e)))?;
let lines = output.stdout.lines();
let mut interfaces = Vec::new();
for line in lines.map_while(|i| i.ok()) {
if let Some(interface_name) = line.split_whitespace().nth(1) {
debug!("found interface: {}", interface_name);
interfaces.push(crate::Interface {
id: interface_name.into(),
support_mode: vec![],
});
}
}
Ok(interfaces)
}
async fn set_mode(id: &ID, mode: crate::Mode) -> crate::WiFiResult {
OS::ifdown(id).await?;
Command::new("iw")
.arg("dev")
.arg(id.as_ref())
.arg("set")
.arg("type")
.arg(mode.cmd())
.execute(format!("[{id}] set mode `{}`", mode.cmd()))
.await?;
OS::ifup(id).await?;
Ok(())
}
async fn set_channel(
id: &ID,
freq_mhz: usize,
band_width: Option<BandWidthArg>,
) -> crate::WiFiResult {
let mut cmd = Command::new("iw");
let c = cmd
.arg("dev")
.arg(id.as_ref())
.arg("set")
.arg("channel")
.arg(format!("{}", freq_mhz));
if let Some(band_width) = band_width {
c.arg(band_width.to_string());
}
cmd.execute(format!("set channel for interface {id}")).await
}
async fn ifup(id: &ID) -> crate::WiFiResult {
Command::new("ifconfig")
.arg(id.as_ref())
.arg("up")
.execute("bring up interface")
.await
}
async fn ifdown(id: &ID) -> crate::WiFiResult {
Command::new("ifconfig")
.arg(id.as_ref())
.arg("down")
.execute("bring down interface")
.await
}
async fn freq_max_bandwidth(id: &ID) -> WiFiResult<HashMap<usize, BandWidth>> {
let mut out = HashMap::new();
let info = info(id).await?;
let output = Command::new("iw")
.arg("phy")
.arg(format!("phy{}", info.wiphy))
.arg("info")
.output()
.await?;
let result = String::from_utf8_lossy(&output.stdout);
let info = PhyInfo::parse(&result)?;
debug!("parsed phy info: {:?}", info);
for band in info.bands {
let mut max_width = BandWidth::HT20;
if band.capabilities.rx_ht40_sgi {
max_width = BandWidth::HT40;
}
if band.vht_capabilities.mhz80 {
max_width = BandWidth::MHz80;
}
if band.vht_capabilities.mhz160 {
max_width = BandWidth::MHz160;
}
for freq in band.frequencies {
out.insert(freq as usize, max_width);
}
}
Ok(out)
}
}
async fn info(interface: &ID) -> WiFiResult<Info> {
let output = Command::new("iw")
.args(["dev", interface.as_ref(), "info"])
.output()
.await?;
let result = String::from_utf8_lossy(&output.stdout);
Info::parse(&result)
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ID {
ifname: String,
}
impl Display for ID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ifname)
}
}
impl Debug for ID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ID({})", self.ifname)
}
}
impl From<String> for ID {
fn from(ifname: String) -> Self {
Self { ifname }
}
}
impl From<&str> for ID {
fn from(value: &str) -> Self {
Self {
ifname: value.to_string(),
}
}
}
impl AsRef<str> for ID {
fn as_ref(&self) -> &str {
self.ifname.as_str()
}
}