use std::default::Default;
use std::ffi::CStr;
use std::fs;
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::str::FromStr;
use crate::{Result, State, Technology};
const ACPI_CMBAT_MAXSTRLEN: usize = 32;
const ACPI_BATT_STAT_FULL: u32 = 0x0000;
const ACPI_BATT_STAT_DISCHARG: u32 = 0x0001;
const ACPI_BATT_STAT_CHARGING: u32 = 0x0002;
const ACPI_BATT_STAT_CRITICAL: u32 = 0x0004;
const ACPI_BATT_STAT_INVALID: u32 = ACPI_BATT_STAT_DISCHARG | ACPI_BATT_STAT_CHARGING;
const ACPI_BATT_STAT_BST_MASK: u32 = ACPI_BATT_STAT_INVALID | ACPI_BATT_STAT_CRITICAL;
const ACPI_BATT_STAT_NOT_PRESENT: u32 = ACPI_BATT_STAT_BST_MASK;
const ACPI_BATT_UNKNOWN: u32 = 0xffff_ffff;
const ACPI_BIF_UNITS_MW: u32 = 0;
const ACPI_BIF_UNITS_MA: u32 = 1;
#[derive(Debug, Eq, PartialEq)]
pub enum Units {
MilliWatts,
MilliAmperes,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AcpiBif {
units: u32, dcap: u32, lfcap: u32, btech: u32, dvol: u32, wcap: u32, lcap: u32, gra1: u32, gra2: u32, model: [u8; ACPI_CMBAT_MAXSTRLEN], serial: [u8; ACPI_CMBAT_MAXSTRLEN], type_: [u8; ACPI_CMBAT_MAXSTRLEN], oeminfo: [u8; ACPI_CMBAT_MAXSTRLEN], }
impl AcpiBif {
pub fn is_valid(&self) -> bool {
self.lfcap != 0
}
pub fn units(&self) -> Units {
match self.units {
ACPI_BIF_UNITS_MW => Units::MilliWatts,
ACPI_BIF_UNITS_MA => Units::MilliAmperes,
_ => unreachable!("Unknown units from acpi_bif"),
}
}
pub fn model(&self) -> Option<String> {
self.get_string(&self.model)
}
pub fn serial(&self) -> Option<String> {
self.get_string(&self.serial)
}
pub fn type_(&self) -> Option<String> {
self.get_string(&self.type_)
}
pub fn technology(&self) -> Technology {
match self.type_() {
None => Technology::Unknown,
Some(ref type_) => match Technology::from_str(type_) {
Ok(tech) => tech,
Err(_) => Technology::Unknown,
},
}
}
#[inline]
pub fn design_voltage(&self) -> u32 {
self.dvol
}
pub fn oem(&self) -> Option<String> {
self.get_string(&self.oeminfo)
}
#[inline]
pub fn design_capacity(&self) -> u32 {
self.dcap
}
#[inline]
pub fn last_full_capacity(&self) -> u32 {
self.lfcap
}
fn get_string(&self, bytes: &[u8]) -> Option<String> {
let striped = match bytes.iter().position(|x| *x == 0x00) {
Some(pos) => &bytes[..=pos],
None => return None,
};
match CStr::from_bytes_with_nul(&striped) {
Ok(cstr) => Some(cstr.to_string_lossy().to_string()),
Err(_) => None,
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AcpiBst {
state: u32, rate: u32, cap: u32, volt: u32, }
impl AcpiBst {
pub fn is_valid(&self) -> bool {
self.state != ACPI_BATT_STAT_NOT_PRESENT && self.cap != ACPI_BATT_UNKNOWN && self.volt != ACPI_BATT_UNKNOWN
}
#[inline]
pub fn state(&self) -> State {
match self.state {
ACPI_BATT_STAT_FULL => State::Full,
value if value & ACPI_BATT_STAT_DISCHARG != 0 => State::Discharging,
value if value & ACPI_BATT_STAT_CHARGING != 0 => State::Charging,
value if value & ACPI_BATT_STAT_CRITICAL != 0 => State::Discharging,
_ => State::Unknown,
}
}
#[inline]
pub fn rate(&self) -> u32 {
self.rate
}
#[inline]
pub fn capacity(&self) -> u32 {
self.cap
}
#[inline]
pub fn voltage(&self) -> u32 {
self.volt
}
}
#[repr(C)]
pub union AcpiBatteryIoctlArg {
unit: i32, bif: AcpiBif,
bst: AcpiBst,
}
impl Default for AcpiBatteryIoctlArg {
fn default() -> Self {
unsafe { mem::zeroed() }
}
}
ioctl_read!(acpiio_batt_get_units, b'B', 0x01, libc::c_int);
ioctl_readwrite!(acpiio_batt_get_bif, b'B', 0x10, AcpiBatteryIoctlArg);
ioctl_readwrite!(acpiio_batt_get_bst, b'B', 0x11, AcpiBatteryIoctlArg);
#[derive(Debug)]
pub struct AcpiDevice(RawFd);
impl AcpiDevice {
pub fn new() -> Result<AcpiDevice> {
let file = fs::OpenOptions::new().read(true).open("/dev/acpi")?;
Ok(AcpiDevice(file.into_raw_fd()))
}
pub fn count(&self) -> Result<libc::c_int> {
let mut arg = 0i32;
unsafe { acpiio_batt_get_units(self.0, &mut arg as *mut _)? };
Ok(arg)
}
pub fn bif(&self, unit: libc::c_int) -> Result<Option<AcpiBif>> {
let mut arg = AcpiBatteryIoctlArg::default();
unsafe {
arg.unit = unit;
acpiio_batt_get_bif(self.0, &mut arg as *mut _)?
};
let info = unsafe { arg.bif };
if info.is_valid() { Ok(Some(info)) } else { Ok(None) }
}
pub fn bst(&self, unit: i32) -> Result<Option<AcpiBst>> {
let mut arg = AcpiBatteryIoctlArg::default();
unsafe {
arg.unit = unit;
acpiio_batt_get_bst(self.0, &mut arg as *mut _)?
};
let info = unsafe { arg.bst };
if info.is_valid() { Ok(Some(info)) } else { Ok(None) }
}
}
impl AsRawFd for AcpiDevice {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Drop for AcpiDevice {
fn drop(&mut self) {
unsafe {
fs::File::from_raw_fd(self.0);
}
}
}