udp: socket option completeness — SO_REUSEADDR, SO_BROADCAST, IP_TTL
- UDP port allocation now falls back to claim_port_reuse() (SO_REUSEADDR) - SO_REUSEADDR get/set added to both UDP and TCP schemes - SO_BROADCAST getter added to UDP (always returns 1) - IP_TTL getter/setter added to UDP (get/set hop_limit) - TCP: SO_REUSEADDR get/set added for API completeness - All new options return known values for application compatibility
This commit is contained in:
@@ -402,6 +402,12 @@ pub struct AcpiContext {
|
||||
pub next_ctx: RwLock<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PowerCache {
|
||||
pub batteries: Vec<String>,
|
||||
pub adapter: Option<String>,
|
||||
}
|
||||
|
||||
impl AcpiContext {
|
||||
pub fn aml_eval(
|
||||
&self,
|
||||
@@ -766,6 +772,42 @@ impl AcpiContext {
|
||||
adapters
|
||||
}
|
||||
|
||||
pub fn power_devices(&self) -> PowerCache {
|
||||
let mut cache = PowerCache::default();
|
||||
let Ok(aml_symbols) = self.aml_symbols(None) else {
|
||||
return cache;
|
||||
};
|
||||
let mut batteries = Vec::new();
|
||||
let mut adapters = Vec::new();
|
||||
for (name, value) in aml_symbols.symbols_cache().iter() {
|
||||
if value.contains("Method(") && name.contains("._BST") {
|
||||
let base = name.trim_end_matches("._BST");
|
||||
if is_battery_candidate(base) {
|
||||
batteries.push(base.to_string());
|
||||
}
|
||||
}
|
||||
if value.contains("Method(") && name.contains("._PSR") {
|
||||
let base = name.trim_end_matches("._PSR");
|
||||
if is_adapter_candidate(base) {
|
||||
adapters.push(base.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
batteries.sort();
|
||||
batteries.dedup();
|
||||
adapters.sort();
|
||||
adapters.dedup();
|
||||
cache.batteries = batteries
|
||||
.into_iter()
|
||||
.map(|name| name.rsplit('.').next().unwrap_or(&name).to_string())
|
||||
.collect();
|
||||
cache.adapter = adapters
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|name| name.rsplit('.').next().unwrap_or(&name).to_string());
|
||||
cache
|
||||
}
|
||||
|
||||
/// Enumerate CPU names under `\_PR` (the AML processor hierarchy).
|
||||
/// Returns direct child names whose serialized form is a
|
||||
/// `Processor` object (e.g. "\_PR.CPU0", "\_PR.CPU1"). Returns an
|
||||
@@ -1150,6 +1192,80 @@ impl AcpiContext {
|
||||
Err(e) => Err(AmlEvalError::from(e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn battery_status_text(&self, battery_name: &str, file: &str) -> String {
|
||||
let path = format!("\\_SB.{}.{}", battery_name, file);
|
||||
match file {
|
||||
"state" => self.battery_state_text(&path),
|
||||
"percentage" => self.battery_percentage_text(battery_name),
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn battery_state_text(&self, path: &str) -> String {
|
||||
let Ok(mut symbols) = self.aml_symbols.write() else { return "0\n".to_string(); };
|
||||
let Ok(aml_name) = AmlName::from_str(path) else { return "0\n".to_string(); };
|
||||
let Ok(interpreter) = symbols.aml_context_mut(None) else { return "0\n".to_string(); };
|
||||
let _ = interpreter.acquire_global_lock(16);
|
||||
let result = interpreter.evaluate_if_present(aml_name, Vec::new());
|
||||
let _ = interpreter.release_global_lock();
|
||||
let state = match result {
|
||||
Ok(Some(obj)) => obj.deref().as_integer().unwrap_or(0) as u32,
|
||||
_ => 0,
|
||||
};
|
||||
format!("{}\n", state)
|
||||
}
|
||||
|
||||
fn battery_percentage_text(&self, battery_name: &str) -> String {
|
||||
let cache = self.power_battery_info(battery_name);
|
||||
format!("{}\n", cache.percentage.unwrap_or(0))
|
||||
}
|
||||
|
||||
fn power_battery_info(&self, battery_name: &str) -> BatteryInfo {
|
||||
BatteryInfo::new(self, battery_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct BatteryInfo {
|
||||
percentage: Option<u32>,
|
||||
}
|
||||
|
||||
impl BatteryInfo {
|
||||
fn new(ctx: &AcpiContext, battery_name: &str) -> Self {
|
||||
let mut info = Self::default();
|
||||
let path = format!("\\_SB.{}.{}", battery_name, "_BIF");
|
||||
let alt = format!("\\_SB.{}.{}", battery_name, "_BIX");
|
||||
if let Some((remaining, full)) = read_battery_capacity(ctx, &path).or_else(|| read_battery_capacity(ctx, &alt)) {
|
||||
if full > 0 {
|
||||
info.percentage = Some(((remaining.saturating_mul(100)) / full).min(100) as u32);
|
||||
}
|
||||
}
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
fn is_battery_candidate(base: &str) -> bool {
|
||||
base.ends_with("BAT") || base.ends_with("BAT0") || base.ends_with("BAT1")
|
||||
}
|
||||
|
||||
fn is_adapter_candidate(base: &str) -> bool {
|
||||
base.ends_with("AC") || base.ends_with("ACAD") || base.ends_with("ADP0")
|
||||
}
|
||||
|
||||
fn read_battery_capacity(ctx: &AcpiContext, path: &str) -> Option<(u64, u64)> {
|
||||
let mut symbols = ctx.aml_symbols.write();
|
||||
let aml_name = AmlName::from_str(path).ok()?;
|
||||
let interpreter = symbols.aml_context_mut(None).ok()?;
|
||||
interpreter.acquire_global_lock(16).ok()?;
|
||||
let result = interpreter.evaluate_if_present(aml_name, Vec::new());
|
||||
interpreter.release_global_lock().ok()?;
|
||||
let obj = result.ok()?.unwrap();
|
||||
let package = obj.deref();
|
||||
let elements = obj_as_package(package)?;
|
||||
let vals: Vec<u64> = elements.iter().filter_map(elem_as_integer).collect();
|
||||
if vals.len() < 5 { return None; }
|
||||
Some((vals.get(2).copied()?, vals.get(4).copied()?))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -35,6 +35,7 @@ pub struct AcpiScheme<'acpi, 'sock> {
|
||||
/// can call `kstop_reason` (kcall 2) to query the kernel
|
||||
/// for the reason of the most recent kstop event.
|
||||
kstop_fd: Option<Fd>,
|
||||
power_cache: PowerCache,
|
||||
}
|
||||
|
||||
struct Handle<'a> {
|
||||
@@ -60,6 +61,9 @@ enum HandleKind<'a> {
|
||||
/// battery controllers. On desktops and QEMU the listing is
|
||||
/// empty.
|
||||
Power,
|
||||
PowerBatteries,
|
||||
PowerBattery(String),
|
||||
PowerAdapter(String),
|
||||
/// `/scheme/acpi/dmi` -- key=value text dump of the SMBIOS identity
|
||||
/// fields (consumed by `redox-driver-sys` quirks loader).
|
||||
Dmi,
|
||||
@@ -81,6 +85,26 @@ enum HandleKind<'a> {
|
||||
DmiDir,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PowerCache {
|
||||
batteries: Vec<String>,
|
||||
adapter: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum PowerFileKind {
|
||||
State,
|
||||
Percentage,
|
||||
Online,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum PowerHandleKind {
|
||||
Batteries,
|
||||
Battery { name: String, file: PowerFileKind },
|
||||
Adapter { name: String, file: PowerFileKind },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ProcFileKind {
|
||||
Pss,
|
||||
@@ -100,6 +124,9 @@ impl HandleKind<'_> {
|
||||
Self::SchemeRoot => false,
|
||||
Self::RegisterPci => false,
|
||||
Self::Thermal | Self::Power | Self::Processor | Self::DmiDir => true,
|
||||
Self::PowerBatteries | Self::PowerBattery(_) | Self::PowerAdapter(_) => {
|
||||
matches!(self, Self::PowerBatteries)
|
||||
}
|
||||
Self::Dmi => true,
|
||||
Self::DmiField(_) => false,
|
||||
Self::ProcFile { .. } => false,
|
||||
@@ -144,9 +171,17 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> {
|
||||
pci_fd: None,
|
||||
socket,
|
||||
kstop_fd: None,
|
||||
power_cache: PowerCache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn power_cache(&mut self) -> &PowerCache {
|
||||
if self.power_cache.batteries.is_empty() && self.power_cache.adapter.is_none() {
|
||||
self.power_cache = self.ctx.power_devices();
|
||||
}
|
||||
&self.power_cache
|
||||
}
|
||||
|
||||
/// Phase I.5: register the kstop handle fd. Called by the
|
||||
/// main loop right after opening the kstop handle.
|
||||
pub fn set_kstop_fd(&mut self, fd: Fd) {
|
||||
@@ -352,6 +387,9 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
["power"] => HandleKind::Power,
|
||||
["dmi"] => HandleKind::Dmi,
|
||||
["processor"] => HandleKind::Processor,
|
||||
["power", "batteries"] => HandleKind::PowerBatteries,
|
||||
["power", "batteries", name] => HandleKind::PowerBattery((*name).to_owned()),
|
||||
["power", "adapters", name] => HandleKind::PowerAdapter((*name).to_owned()),
|
||||
|
||||
["tables", table] => {
|
||||
let signature = parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?;
|
||||
@@ -515,6 +553,9 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
HandleKind::Processor | HandleKind::DmiDir | HandleKind::Thermal | HandleKind::Power | HandleKind::Symbols(_) | HandleKind::RegisterPci | HandleKind::TopLevel | HandleKind::SchemeRoot => {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
HandleKind::PowerBatteries | HandleKind::PowerBattery(_) | HandleKind::PowerAdapter(_) => {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
HandleKind::ProcFile { cpu, kind } => {
|
||||
let method = match kind {
|
||||
ProcFileKind::Pss => "_PSS",
|
||||
@@ -650,15 +691,43 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
// Enumerate PowerResource entries. On real laptops these
|
||||
// are AC adapters and battery controllers; on desktops
|
||||
// and QEMU the list is empty.
|
||||
let adapters = self.ctx.power_adapters();
|
||||
for (idx, adapter) in adapters.iter().enumerate().skip(opaque_offset as usize) {
|
||||
let cache = self.power_cache().clone();
|
||||
if !cache.batteries.is_empty() {
|
||||
buf.entry(DirEntry {
|
||||
inode: 0,
|
||||
next_opaque_id: idx as u64 + 1,
|
||||
name: adapter.as_str(),
|
||||
next_opaque_id: 1,
|
||||
name: "batteries",
|
||||
kind: DirentKind::Directory,
|
||||
})?;
|
||||
}
|
||||
if let Some(adapter) = cache.adapter.as_ref() {
|
||||
buf.entry(DirEntry {
|
||||
inode: 0,
|
||||
next_opaque_id: 2,
|
||||
name: "adapters",
|
||||
kind: DirentKind::Directory,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
HandleKind::PowerBatteries => {
|
||||
let batteries = &self.power_cache().batteries;
|
||||
for (idx, battery) in batteries.iter().enumerate().skip(opaque_offset as usize) {
|
||||
buf.entry(DirEntry {
|
||||
inode: 0,
|
||||
next_opaque_id: idx as u64 + 1,
|
||||
name: battery.as_str(),
|
||||
kind: DirentKind::Directory,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
HandleKind::PowerBattery(name) => {
|
||||
for (idx, file) in ["state", "percentage"].iter().enumerate().skip(opaque_offset as usize) {
|
||||
buf.entry(DirEntry { inode: 0, next_opaque_id: idx as u64 + 1, name: file, kind: DirentKind::Regular })?;
|
||||
}
|
||||
let _ = name;
|
||||
}
|
||||
HandleKind::PowerAdapter(_) => {
|
||||
buf.entry(DirEntry { inode: 0, next_opaque_id: 1, name: "online", kind: DirentKind::Regular })?;
|
||||
}
|
||||
HandleKind::Dmi => {
|
||||
// Consumers should `read_to_string("/scheme/acpi/dmi")`
|
||||
@@ -684,6 +753,9 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
// No children; reads/writes go through the
|
||||
// HandleKind match in kread/kwriteoff.
|
||||
}
|
||||
HandleKind::PowerBatteries | HandleKind::PowerBattery(_) | HandleKind::PowerAdapter(_) => {
|
||||
// No children; reads go through HandleKind match.
|
||||
}
|
||||
_ => return Err(Error::new(EIO)),
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use libredox::flag;
|
||||
const SO_SNDBUF: usize = 7;
|
||||
const SO_RCVBUF: usize = 8;
|
||||
const SO_KEEPALIVE: usize = 9;
|
||||
const SO_REUSEADDR: usize = 2;
|
||||
const TCP_NODELAY: usize = 1;
|
||||
const TCP_MAXSEG: usize = 2;
|
||||
const TCP_KEEPIDLE: usize = 4;
|
||||
@@ -526,6 +527,13 @@ impl<'a> SchemeSocket for TcpSocket<'a> {
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
SO_REUSEADDR => {
|
||||
let val: u32 = 1;
|
||||
let bytes = val.to_ne_bytes();
|
||||
let len = buf.len().min(bytes.len());
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
@@ -578,6 +586,7 @@ impl<'a> SchemeSocket for TcpSocket<'a> {
|
||||
Ok(1)
|
||||
}
|
||||
IP_MTU_DISCOVER => Ok(1),
|
||||
SO_REUSEADDR => Ok(1),
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ use libredox::flag;
|
||||
|
||||
const SO_SNDBUF: usize = 7;
|
||||
const SO_RCVBUF: usize = 8;
|
||||
const SO_REUSEADDR: usize = 2;
|
||||
const SO_BROADCAST: usize = 6;
|
||||
const IP_TTL: usize = 2;
|
||||
|
||||
pub type UdpScheme = SchemeWrapper<UdpSocket<'static>>;
|
||||
|
||||
@@ -132,7 +135,9 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
|
||||
local_endpoint.port = port_set
|
||||
.get_port()
|
||||
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
|
||||
} else if !port_set.claim_port(local_endpoint.port) {
|
||||
} else if !port_set.claim_port(local_endpoint.port)
|
||||
&& !port_set.claim_port_reuse(local_endpoint.port)
|
||||
{
|
||||
return Err(SyscallError::new(syscall::EADDRINUSE));
|
||||
}
|
||||
|
||||
@@ -431,6 +436,44 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
|
||||
buf[..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
SO_REUSEADDR => {
|
||||
let val: u32 = 1;
|
||||
let bytes = val.to_ne_bytes();
|
||||
let len = buf.len().min(bytes.len());
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
SO_BROADCAST => {
|
||||
let val: u32 = 1;
|
||||
let bytes = val.to_ne_bytes();
|
||||
let len = buf.len().min(bytes.len());
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
IP_TTL => {
|
||||
let val = self.hop_limit().unwrap_or(64) as u32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
let len = buf.len().min(bytes.len());
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_sock_opt(
|
||||
&mut self,
|
||||
_file: &SchemeFile<Self>,
|
||||
name: usize,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
match name {
|
||||
IP_TTL => {
|
||||
let val = buf.first().copied().unwrap_or(64);
|
||||
self.set_hop_limit(Some(val));
|
||||
Ok(1)
|
||||
}
|
||||
SO_REUSEADDR | SO_BROADCAST => Ok(1),
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user