use crate::errors::EthtoolError;
use crate::ethtool_const::*;
use crate::settings_parser::SettingsParser;
use crate::EthernetInfo;
use libc::ifreq;
use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};
use nix::unistd::close;
use std::mem::{transmute, zeroed};
use std::os::unix::io::RawFd;
ioctl_readwrite_bad!(ethtool_ioctl, libc::SIOCETHTOOL, ifreq);
#[repr(C)]
#[derive(Clone)]
pub struct CmdContext {
devname: String,
fd: RawFd,
ifr: ifreq,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Default)]
pub struct EthtoolLinkSettings {
pub cmd: u32,
pub speed: u32,
pub duplex: u8,
pub port: u8,
pub phy_address: u8,
pub autoneg: u8,
pub mdio_support: u8,
pub eth_tp_mdix: u8,
pub eth_tp_mdix_ctrl: u8,
pub link_mode_masks_nwords: i8,
pub transceiver: u8,
pub master_slave_cfg: u8,
pub master_slave_state: u8,
pub rate_matching: u8,
pub reserved: [u32; 7],
pub link_mode_masks: [u32; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct EthtoolCommnad {
pub req: EthtoolLinkSettings,
pub link_mode_data: [u32; 3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32],
}
impl EthtoolCommnad {
pub fn new(cmd: u32) -> Result<Self, EthtoolError> {
let mut ecmd: Self = EthtoolCommnad {
req: EthtoolLinkSettings::default(),
link_mode_data: [0u32; 3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32],
};
ecmd.req.cmd = cmd;
Ok(ecmd)
}
pub fn into_ethernet_info(self, devname: &str) -> EthernetInfo {
unsafe {
let mut supported_link_modes_u32 = [0u32; ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
let mut advertised_link_modes_u32 = [0u32; ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
let mut link_mode_data_ptr = self.link_mode_data.as_ptr();
let supported_link_modes = std::slice::from_raw_parts(
link_mode_data_ptr,
self.req.link_mode_masks_nwords as usize,
);
supported_link_modes_u32[..supported_link_modes.len()]
.copy_from_slice(supported_link_modes);
link_mode_data_ptr =
link_mode_data_ptr.offset(self.req.link_mode_masks_nwords as isize);
let advertised_link_modes = std::slice::from_raw_parts(
link_mode_data_ptr,
self.req.link_mode_masks_nwords as usize,
);
advertised_link_modes_u32[..advertised_link_modes.len()]
.copy_from_slice(advertised_link_modes);
let settings_parser = SettingsParser::new(
self.req.port,
&supported_link_modes_u32,
&advertised_link_modes_u32,
);
EthernetInfo::from_settings_parser(devname, settings_parser)
}
}
}
#[cfg(target_arch = "x86_64")]
fn str_to_arr(string: &str) -> [i8; IFNAMSIZ] {
let mut arr_u8 = [0i8; IFNAMSIZ];
for (idx, ch) in string.bytes().enumerate() {
arr_u8[idx] = ch as i8;
}
arr_u8
}
#[cfg(target_arch = "aarch64")]
fn str_to_arr(string: &str) -> [u8; IFNAMSIZ] {
let mut arr_u8 = [0u8; IFNAMSIZ];
for (idx, ch) in string.bytes().enumerate() {
arr_u8[idx] = ch;
}
arr_u8
}
impl CmdContext {
pub fn new(dev_name: &str) -> Result<Self, EthtoolError> {
if dev_name.len() > IFNAMSIZ {
return Err(EthtoolError::new("The device name is too long."));
}
let socket_ret = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
);
if socket_ret.is_err() {
return Err(EthtoolError::new("Failed to create socket."));
}
let fd = socket_ret.unwrap();
Ok(CmdContext {
devname: dev_name.to_string(),
fd,
ifr: unsafe { zeroed() },
})
}
pub fn get_ethtool_link_settings(
&mut self,
mut ecmd: EthtoolCommnad,
) -> Result<EthtoolCommnad, EthtoolError> {
unsafe {
self.ifr = ifreq {
ifr_name: str_to_arr(&self.devname),
ifr_ifru: libc::__c_anonymous_ifr_ifru {
ifru_data: { transmute(&mut ecmd as *mut _) },
},
};
let ret = ethtool_ioctl(self.fd, &mut self.ifr);
ret.map(|_| *(self.ifr.ifr_ifru.ifru_data as *mut EthtoolCommnad))
.map_err(|_| EthtoolError::new("Failed to get EthtoolCommnad"))
}
}
pub fn close_socket(&self) {
close(self.fd).expect("Error close socket");
}
pub fn ifname(&self) -> &str {
&self.devname
}
}